[#161] Improve logging

Signed-off-by: Andrey Berezin <a.berezin@yadro.com>
This commit is contained in:
Andrey Berezin 2023-11-29 16:34:59 +03:00
parent 3b071f02f7
commit 873d6e3d14
40 changed files with 980 additions and 950 deletions

View file

@ -1,6 +1,6 @@
from typing import Optional from typing import Optional
import allure from frostfs_testlib import reporter
from frostfs_testlib.resources.cli import CLI_DEFAULT_TIMEOUT from frostfs_testlib.resources.cli import CLI_DEFAULT_TIMEOUT
from frostfs_testlib.resources.error_patterns import OBJECT_ACCESS_DENIED from frostfs_testlib.resources.error_patterns import OBJECT_ACCESS_DENIED
from frostfs_testlib.shell import Shell from frostfs_testlib.shell import Shell
@ -31,7 +31,7 @@ def can_get_object(
wallet_config: Optional[str] = None, wallet_config: Optional[str] = None,
xhdr: Optional[dict] = None, xhdr: Optional[dict] = None,
) -> bool: ) -> bool:
with allure.step("Try get object from container"): with reporter.step("Try get object from container"):
try: try:
got_file_path = get_object_from_random_node( got_file_path = get_object_from_random_node(
wallet, wallet,
@ -63,7 +63,7 @@ def can_put_object(
xhdr: Optional[dict] = None, xhdr: Optional[dict] = None,
attributes: Optional[dict] = None, attributes: Optional[dict] = None,
) -> bool: ) -> bool:
with allure.step("Try put object to container"): with reporter.step("Try put object to container"):
try: try:
put_object_to_random_node( put_object_to_random_node(
wallet, wallet,
@ -94,7 +94,7 @@ def can_delete_object(
wallet_config: Optional[str] = None, wallet_config: Optional[str] = None,
xhdr: Optional[dict] = None, xhdr: Optional[dict] = None,
) -> bool: ) -> bool:
with allure.step("Try delete object from container"): with reporter.step("Try delete object from container"):
try: try:
delete_object( delete_object(
wallet, wallet,
@ -125,7 +125,7 @@ def can_get_head_object(
xhdr: Optional[dict] = None, xhdr: Optional[dict] = None,
timeout: Optional[str] = CLI_DEFAULT_TIMEOUT, timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
) -> bool: ) -> bool:
with allure.step("Try get head of object"): with reporter.step("Try get head of object"):
try: try:
head_object( head_object(
wallet, wallet,
@ -157,7 +157,7 @@ def can_get_range_of_object(
xhdr: Optional[dict] = None, xhdr: Optional[dict] = None,
timeout: Optional[str] = CLI_DEFAULT_TIMEOUT, timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
) -> bool: ) -> bool:
with allure.step("Try get range of object"): with reporter.step("Try get range of object"):
try: try:
get_range( get_range(
wallet, wallet,
@ -190,7 +190,7 @@ def can_get_range_hash_of_object(
xhdr: Optional[dict] = None, xhdr: Optional[dict] = None,
timeout: Optional[str] = CLI_DEFAULT_TIMEOUT, timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
) -> bool: ) -> bool:
with allure.step("Try get range hash of object"): with reporter.step("Try get range hash of object"):
try: try:
get_range_hash( get_range_hash(
wallet, wallet,
@ -223,7 +223,7 @@ def can_search_object(
xhdr: Optional[dict] = None, xhdr: Optional[dict] = None,
timeout: Optional[str] = CLI_DEFAULT_TIMEOUT, timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
) -> bool: ) -> bool:
with allure.step("Try search object in container"): with reporter.step("Try search object in container"):
try: try:
oids = search_object( oids = search_object(
wallet, wallet,

View file

@ -1,6 +1,6 @@
import time import time
import allure from frostfs_testlib import reporter
from frostfs_testlib.resources.common import STORAGE_GC_TIME from frostfs_testlib.resources.common import STORAGE_GC_TIME
from frostfs_testlib.utils import datetime_utils from frostfs_testlib.utils import datetime_utils
@ -33,5 +33,5 @@ def placement_policy_from_container(container_info: str) -> str:
def wait_for_gc_pass_on_storage_nodes() -> None: def wait_for_gc_pass_on_storage_nodes() -> None:
wait_time = datetime_utils.parse_time(STORAGE_GC_TIME) wait_time = datetime_utils.parse_time(STORAGE_GC_TIME)
with allure.step(f"Wait {wait_time}s until GC completes on storage nodes"): with reporter.step(f"Wait {wait_time}s until GC completes on storage nodes"):
time.sleep(wait_time) time.sleep(wait_time)

View file

@ -3,8 +3,8 @@ import uuid
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional from typing import Optional
import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.common import DEFAULT_WALLET_CONFIG, DEFAULT_WALLET_PASS from frostfs_testlib.resources.common import DEFAULT_WALLET_CONFIG, DEFAULT_WALLET_PASS
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.shell import Shell from frostfs_testlib.shell import Shell
@ -39,9 +39,7 @@ class Wallets:
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def wallets(default_wallet: str, temp_directory: str, cluster: Cluster) -> Wallets: def wallets(default_wallet: str, temp_directory: str, cluster: Cluster) -> Wallets:
other_wallets_paths = [ other_wallets_paths = [os.path.join(temp_directory, f"{str(uuid.uuid4())}.json") for _ in range(2)]
os.path.join(temp_directory, f"{str(uuid.uuid4())}.json") for _ in range(2)
]
for other_wallet_path in other_wallets_paths: for other_wallet_path in other_wallets_paths:
wallet_utils.init_wallet(other_wallet_path, DEFAULT_WALLET_PASS) wallet_utils.init_wallet(other_wallet_path, DEFAULT_WALLET_PASS)
@ -72,11 +70,7 @@ def wallets(default_wallet: str, temp_directory: str, cluster: Cluster) -> Walle
if role == EACLRole.SYSTEM: if role == EACLRole.SYSTEM:
continue continue
for wallet in wallets: for wallet in wallets:
allure.attach.file( reporter.attach(wallet.wallet_path, os.path.basename(wallet.wallet_path))
wallet.wallet_path,
os.path.basename(wallet.wallet_path),
allure.attachment_type.JSON,
)
return wallets_collection return wallets_collection
@ -91,7 +85,7 @@ 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
) -> tuple[str, list[str], str]: ) -> tuple[str, list[str], str]:
user_wallet = wallets.get_wallet() user_wallet = wallets.get_wallet()
with allure.step("Create eACL public container"): with reporter.step("Create eACL public container"):
cid = create_container( cid = create_container(
user_wallet.wallet_path, user_wallet.wallet_path,
basic_acl=PUBLIC_ACL, basic_acl=PUBLIC_ACL,
@ -99,7 +93,7 @@ def eacl_container_with_objects(
endpoint=cluster.default_rpc_endpoint, endpoint=cluster.default_rpc_endpoint,
) )
with allure.step("Add test objects to container"): with reporter.step("Add test objects to container"):
objects_oids = [ objects_oids = [
put_object_to_random_node( put_object_to_random_node(
user_wallet.wallet_path, user_wallet.wallet_path,
@ -114,5 +108,5 @@ def eacl_container_with_objects(
yield cid, objects_oids, file_path yield cid, objects_oids, file_path
# with allure.step('Delete eACL public container'): # with reporter.step('Delete eACL public container'):
# delete_container(user_wallet, cid) # delete_container(user_wallet, cid)

View file

@ -1,5 +1,6 @@
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.wellknown_acl import PRIVATE_ACL_F, PUBLIC_ACL_F, READONLY_ACL_F from frostfs_testlib.resources.wellknown_acl import PRIVATE_ACL_F, PUBLIC_ACL_F, READONLY_ACL_F
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
@ -23,7 +24,7 @@ class TestACLBasic(ClusterTestBase):
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def public_container(self, wallets): def public_container(self, wallets):
user_wallet = wallets.get_wallet() user_wallet = wallets.get_wallet()
with allure.step("Create public container"): with reporter.step("Create public container"):
cid_public = create_container( cid_public = create_container(
user_wallet.wallet_path, user_wallet.wallet_path,
basic_acl=PUBLIC_ACL_F, basic_acl=PUBLIC_ACL_F,
@ -33,13 +34,13 @@ class TestACLBasic(ClusterTestBase):
yield cid_public yield cid_public
# with allure.step('Delete public container'): # with reporter.step('Delete public container'):
# delete_container(user_wallet.wallet_path, cid_public) # delete_container(user_wallet.wallet_path, cid_public)
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def private_container(self, wallets: Wallets): def private_container(self, wallets: Wallets):
user_wallet = wallets.get_wallet() user_wallet = wallets.get_wallet()
with allure.step("Create private container"): with reporter.step("Create private container"):
cid_private = create_container( cid_private = create_container(
user_wallet.wallet_path, user_wallet.wallet_path,
basic_acl=PRIVATE_ACL_F, basic_acl=PRIVATE_ACL_F,
@ -49,13 +50,13 @@ class TestACLBasic(ClusterTestBase):
yield cid_private yield cid_private
# with allure.step('Delete private container'): # with reporter.step('Delete private container'):
# delete_container(user_wallet.wallet_path, cid_private) # delete_container(user_wallet.wallet_path, cid_private)
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def read_only_container(self, wallets: Wallets): def read_only_container(self, wallets: Wallets):
user_wallet = wallets.get_wallet() user_wallet = wallets.get_wallet()
with allure.step("Create public readonly container"): with reporter.step("Create public readonly container"):
cid_read_only = create_container( cid_read_only = create_container(
user_wallet.wallet_path, user_wallet.wallet_path,
basic_acl=READONLY_ACL_F, basic_acl=READONLY_ACL_F,
@ -65,7 +66,7 @@ class TestACLBasic(ClusterTestBase):
yield cid_read_only yield cid_read_only
# with allure.step('Delete public readonly container'): # with reporter.step('Delete public readonly container'):
# delete_container(user_wallet.wallet_path, cid_read_only) # delete_container(user_wallet.wallet_path, cid_read_only)
@allure.title("Operations with basic ACL on public container (obj_size={object_size})") @allure.title("Operations with basic ACL on public container (obj_size={object_size})")
@ -77,7 +78,7 @@ class TestACLBasic(ClusterTestBase):
other_wallet = wallets.get_wallet(role=EACLRole.OTHERS) other_wallet = wallets.get_wallet(role=EACLRole.OTHERS)
cid = public_container cid = public_container
for wallet, desc in ((user_wallet, "owner"), (other_wallet, "other users")): for wallet, desc in ((user_wallet, "owner"), (other_wallet, "other users")):
with allure.step("Add test objects to container"): with reporter.step("Add test objects to container"):
# We create new objects for each wallet because check_full_access_to_container # We create new objects for each wallet because check_full_access_to_container
# deletes the object # deletes the object
owner_object_oid = put_object_to_random_node( owner_object_oid = put_object_to_random_node(
@ -96,7 +97,7 @@ class TestACLBasic(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
attributes={"created": "other"}, attributes={"created": "other"},
) )
with allure.step(f"Check {desc} has full access to public container"): with reporter.step(f"Check {desc} has full access to public container"):
check_full_access_to_container( check_full_access_to_container(
wallet.wallet_path, wallet.wallet_path,
cid, cid,
@ -122,13 +123,13 @@ class TestACLBasic(ClusterTestBase):
user_wallet = wallets.get_wallet() user_wallet = wallets.get_wallet()
other_wallet = wallets.get_wallet(role=EACLRole.OTHERS) other_wallet = wallets.get_wallet(role=EACLRole.OTHERS)
cid = private_container cid = private_container
with allure.step("Add test objects to container"): with reporter.step("Add test objects to container"):
owner_object_oid = put_object_to_random_node( owner_object_oid = put_object_to_random_node(
user_wallet.wallet_path, file_path, cid, shell=self.shell, cluster=self.cluster user_wallet.wallet_path, file_path, cid, shell=self.shell, cluster=self.cluster
) )
with allure.step("Check only owner has full access to private container"): with reporter.step("Check only owner has full access to private container"):
with allure.step("Check no one except owner has access to operations with container"): with reporter.step("Check no one except owner has access to operations with container"):
check_no_access_to_container( check_no_access_to_container(
other_wallet.wallet_path, other_wallet.wallet_path,
cid, cid,
@ -138,7 +139,7 @@ class TestACLBasic(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step("Check owner has full access to private container"): with reporter.step("Check owner has full access to private container"):
check_full_access_to_container( check_full_access_to_container(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
@ -149,9 +150,7 @@ class TestACLBasic(ClusterTestBase):
) )
@allure.title("Operations with basic ACL on READONLY container (obj_size={object_size})") @allure.title("Operations with basic ACL on READONLY container (obj_size={object_size})")
def test_basic_acl_readonly( def test_basic_acl_readonly(self, wallets: Wallets, client_shell: Shell, read_only_container: str, file_path: str):
self, wallets: Wallets, client_shell: Shell, read_only_container: str, file_path: str
):
""" """
Test basic ACL Operations for Read-Only Container. Test basic ACL Operations for Read-Only Container.
""" """
@ -159,12 +158,12 @@ class TestACLBasic(ClusterTestBase):
other_wallet = wallets.get_wallet(role=EACLRole.OTHERS) other_wallet = wallets.get_wallet(role=EACLRole.OTHERS)
cid = read_only_container cid = read_only_container
with allure.step("Add test objects to container"): with reporter.step("Add test objects to container"):
object_oid = put_object_to_random_node( object_oid = put_object_to_random_node(
user_wallet.wallet_path, file_path, cid, shell=client_shell, cluster=self.cluster user_wallet.wallet_path, file_path, cid, shell=client_shell, cluster=self.cluster
) )
with allure.step("Check other has read-only access to operations with container"): with reporter.step("Check other has read-only access to operations with container"):
check_read_only_container( check_read_only_container(
other_wallet.wallet_path, other_wallet.wallet_path,
cid, cid,
@ -174,7 +173,7 @@ class TestACLBasic(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step("Check owner has full access to public container"): with reporter.step("Check owner has full access to public container"):
check_full_access_to_container( check_full_access_to_container(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,

View file

@ -1,11 +1,7 @@
import allure import allure
import pytest import pytest
from frostfs_testlib.steps.acl import ( from frostfs_testlib import reporter
create_eacl, from frostfs_testlib.steps.acl import create_eacl, form_bearertoken_file, set_eacl, wait_for_cache_expired
form_bearertoken_file,
set_eacl,
wait_for_cache_expired,
)
from frostfs_testlib.storage.dataclasses.acl import EACLAccess, EACLOperation, EACLRole, EACLRule from frostfs_testlib.storage.dataclasses.acl import EACLAccess, EACLOperation, EACLRole, EACLRule
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
@ -34,7 +30,7 @@ class TestACLBearer(ClusterTestBase):
deny_wallet = wallets.get_wallet(role) deny_wallet = wallets.get_wallet(role)
endpoint = self.cluster.default_rpc_endpoint endpoint = self.cluster.default_rpc_endpoint
with allure.step(f"Check {role.value} has full access to container without bearer token"): with reporter.step(f"Check {role.value} has full access to container without bearer token"):
check_full_access_to_container( check_full_access_to_container(
deny_wallet.wallet_path, deny_wallet.wallet_path,
cid, cid,
@ -45,29 +41,22 @@ class TestACLBearer(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step(f"Set deny all operations for {role.value} via eACL"): with reporter.step(f"Set deny all operations for {role.value} via eACL"):
eacl = [ eacl = [EACLRule(access=EACLAccess.DENY, role=role, operation=op) for op in EACLOperation]
EACLRule(access=EACLAccess.DENY, role=role, operation=op) for op in EACLOperation
]
eacl_file = create_eacl(cid, eacl, shell=self.shell) eacl_file = create_eacl(cid, eacl, shell=self.shell)
set_eacl(user_wallet.wallet_path, cid, eacl_file, shell=self.shell, endpoint=endpoint) set_eacl(user_wallet.wallet_path, cid, eacl_file, shell=self.shell, endpoint=endpoint)
wait_for_cache_expired() wait_for_cache_expired()
with allure.step(f"Create bearer token for {role.value} with all operations allowed"): with reporter.step(f"Create bearer token for {role.value} with all operations allowed"):
bearer = form_bearertoken_file( bearer = form_bearertoken_file(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
[ [EACLRule(operation=op, access=EACLAccess.ALLOW, role=role) for op in EACLOperation],
EACLRule(operation=op, access=EACLAccess.ALLOW, role=role)
for op in EACLOperation
],
shell=self.shell, shell=self.shell,
endpoint=self.cluster.default_rpc_endpoint, endpoint=self.cluster.default_rpc_endpoint,
) )
with allure.step( with reporter.step(f"Check {role.value} without token has no access to all operations with container"):
f"Check {role.value} without token has no access to all operations with container"
):
check_no_access_to_container( check_no_access_to_container(
deny_wallet.wallet_path, deny_wallet.wallet_path,
cid, cid,
@ -78,9 +67,7 @@ class TestACLBearer(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step( with reporter.step(f"Check {role.value} with token has access to all operations with container"):
f"Check {role.value} with token has access to all operations with container"
):
check_full_access_to_container( check_full_access_to_container(
deny_wallet.wallet_path, deny_wallet.wallet_path,
cid, cid,
@ -92,17 +79,13 @@ class TestACLBearer(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step(f"Set allow all operations for {role.value} via eACL"): with reporter.step(f"Set allow all operations for {role.value} via eACL"):
eacl = [ eacl = [EACLRule(access=EACLAccess.ALLOW, role=role, operation=op) for op in EACLOperation]
EACLRule(access=EACLAccess.ALLOW, role=role, operation=op) for op in EACLOperation
]
eacl_file = create_eacl(cid, eacl, shell=self.shell) eacl_file = create_eacl(cid, eacl, shell=self.shell)
set_eacl(user_wallet.wallet_path, cid, eacl_file, shell=self.shell, endpoint=endpoint) set_eacl(user_wallet.wallet_path, cid, eacl_file, shell=self.shell, endpoint=endpoint)
wait_for_cache_expired() wait_for_cache_expired()
with allure.step( with reporter.step(f"Check {role.value} without token has access to all operations with container"):
f"Check {role.value} without token has access to all operations with container"
):
check_full_access_to_container( check_full_access_to_container(
deny_wallet.wallet_path, deny_wallet.wallet_path,
cid, cid,
@ -145,19 +128,13 @@ class TestACLBearer(ClusterTestBase):
} }
deny_map_with_bearer = { deny_map_with_bearer = {
EACLRole.USER: [ EACLRole.USER: [op for op in deny_map[EACLRole.USER] if op not in bearer_map[EACLRole.USER]],
op for op in deny_map[EACLRole.USER] if op not in bearer_map[EACLRole.USER] EACLRole.OTHERS: [op for op in deny_map[EACLRole.OTHERS] if op not in bearer_map[EACLRole.OTHERS]],
],
EACLRole.OTHERS: [
op for op in deny_map[EACLRole.OTHERS] if op not in bearer_map[EACLRole.OTHERS]
],
} }
eacl_deny = [] eacl_deny = []
for role, operations in deny_map.items(): for role, operations in deny_map.items():
eacl_deny += [ eacl_deny += [EACLRule(access=EACLAccess.DENY, role=role, operation=op) for op in operations]
EACLRule(access=EACLAccess.DENY, role=role, operation=op) for op in operations
]
set_eacl( set_eacl(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
@ -167,7 +144,7 @@ class TestACLBearer(ClusterTestBase):
) )
wait_for_cache_expired() wait_for_cache_expired()
with allure.step("Check rule consistency without bearer"): with reporter.step("Check rule consistency without bearer"):
check_custom_access_to_container( check_custom_access_to_container(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
@ -189,7 +166,7 @@ class TestACLBearer(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step("Check rule consistency using bearer token"): with reporter.step("Check rule consistency using bearer token"):
bearer_user = form_bearertoken_file( bearer_user = form_bearertoken_file(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,

View file

@ -1,5 +1,6 @@
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.steps.acl import create_eacl, set_eacl, wait_for_cache_expired from frostfs_testlib.steps.acl import create_eacl, set_eacl, wait_for_cache_expired
from frostfs_testlib.steps.cli.container import create_container from frostfs_testlib.steps.cli.container import create_container
@ -30,7 +31,7 @@ class TestEACLContainer(ClusterTestBase):
user_wallet = wallets.get_wallet() user_wallet = wallets.get_wallet()
storage_nodes = self.cluster.storage_nodes storage_nodes = self.cluster.storage_nodes
node_count = len(storage_nodes) node_count = len(storage_nodes)
with allure.step("Create eACL public container with full placement rule"): with reporter.step("Create eACL public container with full placement rule"):
full_placement_rule = f"REP {node_count} IN X CBF 1 SELECT {node_count} FROM * AS X" full_placement_rule = f"REP {node_count} IN X CBF 1 SELECT {node_count} FROM * AS X"
cid = create_container( cid = create_container(
wallet=user_wallet.wallet_path, wallet=user_wallet.wallet_path,
@ -40,7 +41,7 @@ class TestEACLContainer(ClusterTestBase):
endpoint=self.cluster.default_rpc_endpoint, endpoint=self.cluster.default_rpc_endpoint,
) )
with allure.step("Add test object to container"): with reporter.step("Add test object to container"):
oid = put_object_to_random_node( oid = put_object_to_random_node(
user_wallet.wallet_path, file_path, cid, shell=self.shell, cluster=self.cluster user_wallet.wallet_path, file_path, cid, shell=self.shell, cluster=self.cluster
) )
@ -71,7 +72,7 @@ class TestEACLContainer(ClusterTestBase):
not_deny_role_str = "user" if deny_role == EACLRole.OTHERS else "all others" not_deny_role_str = "user" if deny_role == EACLRole.OTHERS else "all others"
cid, object_oids, file_path = eacl_container_with_objects cid, object_oids, file_path = eacl_container_with_objects
with allure.step(f"Deny all operations for {deny_role_str} via eACL"): with reporter.step(f"Deny all operations for {deny_role_str} via eACL"):
eacl_deny = [EACLRule(access=EACLAccess.DENY, role=deny_role, operation=op) for op in EACLOperation] eacl_deny = [EACLRule(access=EACLAccess.DENY, role=deny_role, operation=op) for op in EACLOperation]
set_eacl( set_eacl(
user_wallet.wallet_path, user_wallet.wallet_path,
@ -82,8 +83,8 @@ class TestEACLContainer(ClusterTestBase):
) )
wait_for_cache_expired() wait_for_cache_expired()
with allure.step(f"Check only {not_deny_role_str} has full access to container"): with reporter.step(f"Check only {not_deny_role_str} has full access to container"):
with allure.step(f"Check {deny_role_str} has not access to any operations with container"): with reporter.step(f"Check {deny_role_str} has not access to any operations with container"):
check_no_access_to_container( check_no_access_to_container(
deny_role_wallet.wallet_path, deny_role_wallet.wallet_path,
cid, cid,
@ -93,7 +94,7 @@ class TestEACLContainer(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step(f"Check {not_deny_role_wallet} has full access to eACL public container"): with reporter.step(f"Check {not_deny_role_wallet} has full access to eACL public container"):
check_full_access_to_container( check_full_access_to_container(
not_deny_role_wallet.wallet_path, not_deny_role_wallet.wallet_path,
cid, cid,
@ -103,7 +104,7 @@ class TestEACLContainer(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step(f"Allow all operations for {deny_role_str} via eACL"): with reporter.step(f"Allow all operations for {deny_role_str} via eACL"):
eacl_deny = [EACLRule(access=EACLAccess.ALLOW, role=deny_role, operation=op) for op in EACLOperation] eacl_deny = [EACLRule(access=EACLAccess.ALLOW, role=deny_role, operation=op) for op in EACLOperation]
set_eacl( set_eacl(
user_wallet.wallet_path, user_wallet.wallet_path,
@ -114,7 +115,7 @@ class TestEACLContainer(ClusterTestBase):
) )
wait_for_cache_expired() wait_for_cache_expired()
with allure.step("Check all have full access to eACL public container"): with reporter.step("Check all have full access to eACL public container"):
check_full_access_to_container( check_full_access_to_container(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
@ -140,7 +141,7 @@ class TestEACLContainer(ClusterTestBase):
other_wallet, other_wallet_allow = wallets.get_wallets_list(EACLRole.OTHERS)[0:2] other_wallet, other_wallet_allow = wallets.get_wallets_list(EACLRole.OTHERS)[0:2]
cid, object_oids, file_path = eacl_container_with_objects cid, object_oids, file_path = eacl_container_with_objects
with allure.step("Deny all operations for others except single wallet via eACL"): with reporter.step("Deny all operations for others except single wallet via eACL"):
eacl = [ eacl = [
EACLRule( EACLRule(
access=EACLAccess.ALLOW, access=EACLAccess.ALLOW,
@ -159,8 +160,8 @@ class TestEACLContainer(ClusterTestBase):
) )
wait_for_cache_expired() wait_for_cache_expired()
with allure.step("Check only owner and allowed other have full access to public container"): with reporter.step("Check only owner and allowed other have full access to public container"):
with allure.step("Check other has not access to operations with container"): with reporter.step("Check other has not access to operations with container"):
check_no_access_to_container( check_no_access_to_container(
other_wallet.wallet_path, other_wallet.wallet_path,
cid, cid,
@ -170,7 +171,7 @@ class TestEACLContainer(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step("Check owner has full access to public container"): with reporter.step("Check owner has full access to public container"):
check_full_access_to_container( check_full_access_to_container(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
@ -180,7 +181,7 @@ class TestEACLContainer(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step("Check allowed other has full access to public container"): with reporter.step("Check allowed other has full access to public container"):
check_full_access_to_container( check_full_access_to_container(
other_wallet_allow.wallet_path, other_wallet_allow.wallet_path,
cid, cid,
@ -201,7 +202,7 @@ class TestEACLContainer(ClusterTestBase):
storage_nodes = self.cluster.storage_nodes storage_nodes = self.cluster.storage_nodes
storage_node = self.cluster.storage_nodes[0] storage_node = self.cluster.storage_nodes[0]
with allure.step("Deny all operations for user via eACL"): with reporter.step("Deny all operations for user via eACL"):
eacl_deny = [EACLRule(access=EACLAccess.DENY, role=EACLRole.USER, operation=op) for op in EACLOperation] eacl_deny = [EACLRule(access=EACLAccess.DENY, role=EACLRole.USER, operation=op) for op in EACLOperation]
eacl_deny += [EACLRule(access=EACLAccess.DENY, role=EACLRole.OTHERS, operation=op) for op in EACLOperation] eacl_deny += [EACLRule(access=EACLAccess.DENY, role=EACLRole.OTHERS, operation=op) for op in EACLOperation]
set_eacl( set_eacl(
@ -213,11 +214,11 @@ class TestEACLContainer(ClusterTestBase):
) )
wait_for_cache_expired() wait_for_cache_expired()
with allure.step("Drop object to check replication"): with reporter.step("Drop object to check replication"):
drop_object(storage_node, cid=cid, oid=oid) drop_object(storage_node, cid=cid, oid=oid)
storage_wallet_path = storage_node.get_wallet_path() storage_wallet_path = storage_node.get_wallet_path()
with allure.step("Wait for dropped object replicated"): with reporter.step("Wait for dropped object replicated"):
wait_object_replication( wait_object_replication(
cid, cid,
oid, oid,
@ -234,7 +235,7 @@ class TestEACLContainer(ClusterTestBase):
cid, object_oids, file_path = eacl_container_with_objects cid, object_oids, file_path = eacl_container_with_objects
endpoint = self.cluster.default_rpc_endpoint endpoint = self.cluster.default_rpc_endpoint
with allure.step("Check IR and STORAGE rules compliance"): with reporter.step("Check IR and STORAGE rules compliance"):
assert not can_put_object( assert not can_put_object(
ir_wallet.wallet_path, ir_wallet.wallet_path,
cid, cid,
@ -361,7 +362,7 @@ class TestEACLContainer(ClusterTestBase):
wallet_config=storage_wallet.config_path, wallet_config=storage_wallet.config_path,
) )
with allure.step("Deny all operations for SYSTEM via eACL"): with reporter.step("Deny all operations for SYSTEM via eACL"):
set_eacl( set_eacl(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
@ -377,7 +378,7 @@ class TestEACLContainer(ClusterTestBase):
) )
wait_for_cache_expired() wait_for_cache_expired()
with allure.step("Check IR and STORAGE rules compliance with deny eACL"): with reporter.step("Check IR and STORAGE rules compliance with deny eACL"):
assert not can_put_object( assert not can_put_object(
wallet=ir_wallet.wallet_path, wallet=ir_wallet.wallet_path,
cid=cid, cid=cid,
@ -511,7 +512,7 @@ class TestEACLContainer(ClusterTestBase):
wallet_config=storage_wallet.config_path, wallet_config=storage_wallet.config_path,
) )
with allure.step("Allow all operations for SYSTEM via eACL"): with reporter.step("Allow all operations for SYSTEM via eACL"):
set_eacl( set_eacl(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
@ -527,7 +528,7 @@ class TestEACLContainer(ClusterTestBase):
) )
wait_for_cache_expired() wait_for_cache_expired()
with allure.step("Check IR and STORAGE rules compliance with allow eACL"): with reporter.step("Check IR and STORAGE rules compliance with allow eACL"):
assert not can_put_object( assert not can_put_object(
wallet=ir_wallet.wallet_path, wallet=ir_wallet.wallet_path,
cid=cid, cid=cid,

View file

@ -1,5 +1,6 @@
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.steps.acl import create_eacl, form_bearertoken_file, set_eacl, wait_for_cache_expired from frostfs_testlib.steps.acl import create_eacl, form_bearertoken_file, set_eacl, wait_for_cache_expired
from frostfs_testlib.steps.cli.container import create_container, delete_container from frostfs_testlib.steps.cli.container import create_container, delete_container
@ -61,7 +62,7 @@ class TestEACLFilters(ClusterTestBase):
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def eacl_container_with_objects(self, wallets: Wallets, file_path: str): def eacl_container_with_objects(self, wallets: Wallets, file_path: str):
user_wallet = wallets.get_wallet() user_wallet = wallets.get_wallet()
with allure.step("Create eACL public container"): with reporter.step("Create eACL public container"):
cid = create_container( cid = create_container(
user_wallet.wallet_path, user_wallet.wallet_path,
basic_acl=PUBLIC_ACL, basic_acl=PUBLIC_ACL,
@ -69,7 +70,7 @@ class TestEACLFilters(ClusterTestBase):
endpoint=self.cluster.default_rpc_endpoint, endpoint=self.cluster.default_rpc_endpoint,
) )
with allure.step("Add test objects to container"): with reporter.step("Add test objects to container"):
objects_with_header = [ objects_with_header = [
put_object_to_random_node( put_object_to_random_node(
user_wallet.wallet_path, user_wallet.wallet_path,
@ -107,7 +108,7 @@ class TestEACLFilters(ClusterTestBase):
yield cid, objects_with_header, objects_with_other_header, objects_without_header, file_path yield cid, objects_with_header, objects_with_other_header, objects_without_header, file_path
with allure.step("Delete eACL public container"): with reporter.step("Delete eACL public container"):
delete_container( delete_container(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
@ -134,7 +135,7 @@ class TestEACLFilters(ClusterTestBase):
file_path, file_path,
) = eacl_container_with_objects ) = eacl_container_with_objects
with allure.step("Deny all operations for other with eACL request filter"): with reporter.step("Deny all operations for other with eACL request filter"):
equal_filter = EACLFilter(**self.REQ_EQUAL_FILTER.__dict__) equal_filter = EACLFilter(**self.REQ_EQUAL_FILTER.__dict__)
equal_filter.match_type = match_type equal_filter.match_type = match_type
eacl_deny = [ eacl_deny = [
@ -168,7 +169,7 @@ class TestEACLFilters(ClusterTestBase):
objects_with_other_header, objects_with_other_header,
objects_without_header, objects_without_header,
): ):
with allure.step("Check other has full access when sending request without headers"): with reporter.step("Check other has full access when sending request without headers"):
check_full_access_to_container( check_full_access_to_container(
other_wallet.wallet_path, other_wallet.wallet_path,
cid, cid,
@ -178,7 +179,7 @@ class TestEACLFilters(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step("Check other has full access when sending request with allowed headers"): with reporter.step("Check other has full access when sending request with allowed headers"):
check_full_access_to_container( check_full_access_to_container(
other_wallet.wallet_path, other_wallet.wallet_path,
cid, cid,
@ -189,7 +190,7 @@ class TestEACLFilters(ClusterTestBase):
xhdr=allow_headers, xhdr=allow_headers,
) )
with allure.step("Check other has no access when sending request with denied headers"): with reporter.step("Check other has no access when sending request with denied headers"):
check_no_access_to_container( check_no_access_to_container(
other_wallet.wallet_path, other_wallet.wallet_path,
cid, cid,
@ -200,7 +201,7 @@ class TestEACLFilters(ClusterTestBase):
xhdr=deny_headers, xhdr=deny_headers,
) )
with allure.step( with reporter.step(
"Check other has full access when sending request " "with denied headers and using bearer token" "Check other has full access when sending request " "with denied headers and using bearer token"
): ):
bearer_other = form_bearertoken_file( bearer_other = form_bearertoken_file(
@ -239,7 +240,7 @@ class TestEACLFilters(ClusterTestBase):
file_path, file_path,
) = eacl_container_with_objects ) = eacl_container_with_objects
with allure.step("Deny all operations for other with object filter"): with reporter.step("Deny all operations for other with object filter"):
equal_filter = EACLFilter(**self.OBJ_EQUAL_FILTER.__dict__) equal_filter = EACLFilter(**self.OBJ_EQUAL_FILTER.__dict__)
equal_filter.match_type = match_type equal_filter.match_type = match_type
eacl_deny = [ eacl_deny = [
@ -267,7 +268,7 @@ class TestEACLFilters(ClusterTestBase):
# but eACL rule should ignore request headers and validate # but eACL rule should ignore request headers and validate
# only object headers # only object headers
for xhdr in (self.ATTRIBUTE, self.OTHER_ATTRIBUTE, None): for xhdr in (self.ATTRIBUTE, self.OTHER_ATTRIBUTE, None):
with allure.step("Check other have full access to objects without attributes"): with reporter.step("Check other have full access to objects without attributes"):
check_full_access_to_container( check_full_access_to_container(
other_wallet.wallet_path, other_wallet.wallet_path,
cid, cid,
@ -278,7 +279,7 @@ class TestEACLFilters(ClusterTestBase):
xhdr=xhdr, xhdr=xhdr,
) )
with allure.step("Check other have full access to objects without deny attribute"): with reporter.step("Check other have full access to objects without deny attribute"):
check_full_access_to_container( check_full_access_to_container(
other_wallet.wallet_path, other_wallet.wallet_path,
cid, cid,
@ -289,7 +290,7 @@ class TestEACLFilters(ClusterTestBase):
xhdr=xhdr, xhdr=xhdr,
) )
with allure.step("Check other have no access to objects with deny attribute"): with reporter.step("Check other have no access to objects with deny attribute"):
with pytest.raises(AssertionError): with pytest.raises(AssertionError):
assert can_get_head_object( assert can_get_head_object(
other_wallet.wallet_path, other_wallet.wallet_path,
@ -310,7 +311,7 @@ class TestEACLFilters(ClusterTestBase):
xhdr=xhdr, xhdr=xhdr,
) )
with allure.step("Check other have access to objects with deny attribute and using bearer token"): with reporter.step("Check other have access to objects with deny attribute and using bearer token"):
bearer_other = form_bearertoken_file( bearer_other = form_bearertoken_file(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
@ -337,7 +338,7 @@ class TestEACLFilters(ClusterTestBase):
) )
allow_attribute = self.OTHER_ATTRIBUTE if match_type == EACLMatchType.STRING_EQUAL else self.ATTRIBUTE allow_attribute = self.OTHER_ATTRIBUTE if match_type == EACLMatchType.STRING_EQUAL else self.ATTRIBUTE
with allure.step("Check other can PUT objects without denied attribute"): with reporter.step("Check other can PUT objects without denied attribute"):
assert can_put_object( assert can_put_object(
other_wallet.wallet_path, other_wallet.wallet_path,
cid, cid,
@ -349,7 +350,7 @@ class TestEACLFilters(ClusterTestBase):
assert can_put_object(other_wallet.wallet_path, cid, file_path, shell=self.shell, cluster=self.cluster) assert can_put_object(other_wallet.wallet_path, cid, file_path, shell=self.shell, cluster=self.cluster)
deny_attribute = self.ATTRIBUTE if match_type == EACLMatchType.STRING_EQUAL else self.OTHER_ATTRIBUTE deny_attribute = self.ATTRIBUTE if match_type == EACLMatchType.STRING_EQUAL else self.OTHER_ATTRIBUTE
with allure.step("Check other can not PUT objects with denied attribute"): with reporter.step("Check other can not PUT objects with denied attribute"):
with pytest.raises(AssertionError): with pytest.raises(AssertionError):
assert can_put_object( assert can_put_object(
other_wallet.wallet_path, other_wallet.wallet_path,
@ -360,7 +361,7 @@ class TestEACLFilters(ClusterTestBase):
attributes=deny_attribute, attributes=deny_attribute,
) )
with allure.step("Check other can PUT objects with denied attribute and using bearer token"): with reporter.step("Check other can PUT objects with denied attribute and using bearer token"):
bearer_other_for_put = form_bearertoken_file( bearer_other_for_put = form_bearertoken_file(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
@ -402,7 +403,7 @@ class TestEACLFilters(ClusterTestBase):
file_path, file_path,
) = eacl_container_with_objects ) = eacl_container_with_objects
with allure.step("Deny all operations for others except few operations allowed by object filter"): with reporter.step("Deny all operations for others except few operations allowed by object filter"):
equal_filter = EACLFilter(**self.OBJ_EQUAL_FILTER.__dict__) equal_filter = EACLFilter(**self.OBJ_EQUAL_FILTER.__dict__)
equal_filter.match_type = match_type equal_filter.match_type = match_type
eacl = [ eacl = [
@ -437,7 +438,7 @@ class TestEACLFilters(ClusterTestBase):
allow_attribute = self.OTHER_ATTRIBUTE allow_attribute = self.OTHER_ATTRIBUTE
deny_attribute = self.ATTRIBUTE deny_attribute = self.ATTRIBUTE
with allure.step("Check other cannot get and put objects without attributes"): with reporter.step("Check other cannot get and put objects without attributes"):
oid = objects_without_header.pop() oid = objects_without_header.pop()
with pytest.raises(AssertionError): with pytest.raises(AssertionError):
assert can_get_head_object( assert can_get_head_object(
@ -459,7 +460,7 @@ class TestEACLFilters(ClusterTestBase):
with pytest.raises(AssertionError): with pytest.raises(AssertionError):
assert can_put_object(other_wallet.wallet_path, cid, file_path, shell=self.shell, cluster=self.cluster) assert can_put_object(other_wallet.wallet_path, cid, file_path, shell=self.shell, cluster=self.cluster)
with allure.step("Check other can get and put objects without attributes and using bearer token"): with reporter.step("Check other can get and put objects without attributes and using bearer token"):
bearer_other = form_bearertoken_file( bearer_other = form_bearertoken_file(
user_wallet.wallet_path, user_wallet.wallet_path,
cid, cid,
@ -500,7 +501,7 @@ class TestEACLFilters(ClusterTestBase):
bearer=bearer_other, bearer=bearer_other,
) )
with allure.step("Check other can get objects with attributes matching the filter"): with reporter.step("Check other can get objects with attributes matching the filter"):
oid = allow_objects.pop() oid = allow_objects.pop()
assert can_get_head_object( assert can_get_head_object(
other_wallet.wallet_path, other_wallet.wallet_path,
@ -526,7 +527,7 @@ class TestEACLFilters(ClusterTestBase):
attributes=allow_attribute, attributes=allow_attribute,
) )
with allure.step("Check other cannot get objects without attributes matching the filter"): with reporter.step("Check other cannot get objects without attributes matching the filter"):
with pytest.raises(AssertionError): with pytest.raises(AssertionError):
assert can_get_head_object( assert can_get_head_object(
other_wallet.wallet_path, other_wallet.wallet_path,
@ -554,7 +555,7 @@ class TestEACLFilters(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step( with reporter.step(
"Check other can get objects without attributes matching the filter " "and using bearer token" "Check other can get objects without attributes matching the filter " "and using bearer token"
): ):
oid = deny_objects.pop() oid = deny_objects.pop()

View file

@ -10,10 +10,10 @@ import allure
import pytest import pytest
import yaml import yaml
from dateutil import parser from dateutil import parser
from frostfs_testlib import plugins from frostfs_testlib import plugins, reporter
from frostfs_testlib.healthcheck.interfaces import Healthcheck from frostfs_testlib.healthcheck.interfaces import Healthcheck
from frostfs_testlib.hosting import Hosting from frostfs_testlib.hosting import Hosting
from frostfs_testlib.reporter import AllureHandler, get_reporter from frostfs_testlib.reporter import AllureHandler, StepsLogger
from frostfs_testlib.resources.common import ( from frostfs_testlib.resources.common import (
ASSETS_DIR, ASSETS_DIR,
COMPLEX_OBJECT_CHUNKS_COUNT, COMPLEX_OBJECT_CHUNKS_COUNT,
@ -48,6 +48,9 @@ def pytest_configure(config: pytest.Config):
config.option.markexpr = f"logs_after_session or ({markers})" config.option.markexpr = f"logs_after_session or ({markers})"
number_key = pytest.StashKey[str]()
start_time = pytest.StashKey[int]()
test_outcome = pytest.StashKey[str]()
# pytest hook. Do not rename # pytest hook. Do not rename
def pytest_collection_modifyitems(items: list[pytest.Item]): def pytest_collection_modifyitems(items: list[pytest.Item]):
# Make network tests last based on @pytest.mark.node_mgmt and logs_test to be latest # Make network tests last based on @pytest.mark.node_mgmt and logs_test to be latest
@ -60,6 +63,35 @@ def pytest_collection_modifyitems(items: list[pytest.Item]):
items.sort(key=lambda item: priority(item)) items.sort(key=lambda item: priority(item))
# pytest hook. Do not rename
def pytest_collection_finish(session: pytest.Session):
items_total = len(session.items)
for number, item in enumerate(session.items, 1):
item.stash[number_key] = f"[{number}/{items_total}]"
item.stash[test_outcome] = ""
# pytest hook. Do not rename
def pytest_runtest_setup(item: pytest.Item):
item.stash[start_time] = int(datetime.now().timestamp())
logger.info(f"STARTED {item.stash[number_key]}: {item.name}")
# pytest hook. Do not rename
def pytest_runtest_makereport(item: pytest.Item, call: pytest.CallInfo):
if call.excinfo is not None:
item.stash[test_outcome] += f"FAILED on {call.when}; "
if call.when == "teardown":
duration = int(datetime.now().timestamp()) - item.stash[start_time]
if not item.stash[test_outcome]:
outcome = "PASSED "
else:
outcome = item.stash[test_outcome]
logger.info(f"ENDED {item.stash[number_key]}: {item.name}: {outcome}(duration={duration}s)")
# pytest hook. Do not rename
def pytest_generate_tests(metafunc: pytest.Metafunc): def pytest_generate_tests(metafunc: pytest.Metafunc):
if ( if (
TEST_CYCLES_COUNT <= 1 TEST_CYCLES_COUNT <= 1
@ -78,7 +110,9 @@ def pytest_generate_tests(metafunc: pytest.Metafunc):
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def configure_testlib(): def configure_testlib():
get_reporter().register_handler(AllureHandler()) reporter.get_reporter().register_handler(AllureHandler())
reporter.get_reporter().register_handler(StepsLogger())
logging.getLogger("paramiko").setLevel(logging.INFO) logging.getLogger("paramiko").setLevel(logging.INFO)
# Register Services for cluster # Register Services for cluster
@ -186,7 +220,7 @@ def cluster(temp_directory: str, hosting: Hosting, client_shell: Shell) -> Clust
yield cluster yield cluster
@allure.step("[Class]: Provide S3 policy") @reporter.step("[Class]: Provide S3 policy")
@pytest.fixture(scope="class") @pytest.fixture(scope="class")
def s3_policy(request: pytest.FixtureRequest): def s3_policy(request: pytest.FixtureRequest):
policy = None policy = None
@ -212,7 +246,7 @@ def cluster_state_controller(client_shell: Shell, cluster: Cluster, healthcheck:
yield controller yield controller
@allure.step("[Class]: Create S3 client") @reporter.step("[Class]: Create S3 client")
@pytest.fixture( @pytest.fixture(
scope="class", scope="class",
params=[ params=[
@ -254,7 +288,7 @@ def versioning_status(request: pytest.FixtureRequest) -> VersioningStatus:
return VersioningStatus.UNDEFINED return VersioningStatus.UNDEFINED
@allure.step("Create/delete bucket") @reporter.step("Create/delete bucket")
@pytest.fixture @pytest.fixture
def bucket(s3_client: S3ClientWrapper, versioning_status: VersioningStatus, request: pytest.FixtureRequest): def bucket(s3_client: S3ClientWrapper, versioning_status: VersioningStatus, request: pytest.FixtureRequest):
@ -269,7 +303,7 @@ def bucket(s3_client: S3ClientWrapper, versioning_status: VersioningStatus, requ
s3_helper.delete_bucket_with_objects(s3_client, bucket_name) s3_helper.delete_bucket_with_objects(s3_client, bucket_name)
@allure.step("Create two buckets") @reporter.step("Create two buckets")
@pytest.fixture @pytest.fixture
def two_buckets(s3_client: S3ClientWrapper, request: pytest.FixtureRequest): def two_buckets(s3_client: S3ClientWrapper, request: pytest.FixtureRequest):
bucket_1 = s3_client.create_bucket() bucket_1 = s3_client.create_bucket()
@ -281,13 +315,16 @@ def two_buckets(s3_client: S3ClientWrapper, request: pytest.FixtureRequest):
s3_helper.delete_bucket_with_objects(s3_client, bucket_name) s3_helper.delete_bucket_with_objects(s3_client, bucket_name)
@allure.step("[Autouse/Session] Check binary versions") @reporter.step("[Autouse/Session] Check binary versions")
@pytest.fixture(scope="session", autouse=True) @pytest.fixture(scope="session", autouse=True)
def check_binary_versions(hosting: Hosting, client_shell: Shell, request: pytest.FixtureRequest): def check_binary_versions(hosting: Hosting, client_shell: Shell, request: pytest.FixtureRequest):
local_versions = version_utils.get_local_binaries_versions(client_shell) local_versions = version_utils.get_local_binaries_versions(client_shell)
remote_versions = version_utils.get_remote_binaries_versions(hosting) remote_versions = version_utils.get_remote_binaries_versions(hosting)
all_versions = {**local_versions, **remote_versions} all_versions = {
**local_versions,
**{binary_name: binary["version"] for binary_name, binary in remote_versions.items()},
}
environment_dir = request.config.getoption("--alluredir") environment_dir = request.config.getoption("--alluredir")
if not environment_dir: if not environment_dir:
@ -297,23 +334,23 @@ def check_binary_versions(hosting: Hosting, client_shell: Shell, request: pytest
env_utils.save_env_properties(file_path, all_versions) env_utils.save_env_properties(file_path, all_versions)
@allure.step("Prepare tmp directory") @reporter.step("Prepare tmp directory")
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def temp_directory(): def temp_directory(configure_testlib):
with allure.step("Prepare tmp directory"): with reporter.step("Prepare tmp directory"):
full_path = os.path.join(os.getcwd(), ASSETS_DIR) full_path = os.path.join(os.getcwd(), ASSETS_DIR)
shutil.rmtree(full_path, ignore_errors=True) shutil.rmtree(full_path, ignore_errors=True)
os.mkdir(full_path) os.mkdir(full_path)
yield full_path yield full_path
with allure.step("Remove tmp directory"): with reporter.step("Remove tmp directory"):
shutil.rmtree(full_path) shutil.rmtree(full_path)
@allure.step("[Autouse/Session] Test session start time") @reporter.step("[Autouse/Session] Test session start time")
@pytest.fixture(scope="session", autouse=True) @pytest.fixture(scope="session", autouse=True)
def session_start_time(): def session_start_time(configure_testlib):
start_time = datetime.utcnow() start_time = datetime.utcnow()
return start_time return start_time
@ -321,23 +358,22 @@ def session_start_time():
@allure.title("[Autouse/Session] After deploy healthcheck") @allure.title("[Autouse/Session] After deploy healthcheck")
@pytest.fixture(scope="session", autouse=True) @pytest.fixture(scope="session", autouse=True)
def after_deploy_healthcheck(cluster: Cluster): def after_deploy_healthcheck(cluster: Cluster):
with allure.step("Wait for cluster readiness after deploy"): with reporter.step("Wait for cluster readiness after deploy"):
parallel(readiness_on_node, cluster.cluster_nodes) parallel(readiness_on_node, cluster.cluster_nodes)
SERVICE_ACTIVE_TIME = 20 SERVICE_ACTIVE_TIME = 20
@wait_for_success(60 * SERVICE_ACTIVE_TIME * 3, 60) @wait_for_success(60 * SERVICE_ACTIVE_TIME * 3, 60, title="Wait for {cluster_node} readiness")
@allure.step("Check readiness on node {cluster_node}")
def readiness_on_node(cluster_node: ClusterNode): def readiness_on_node(cluster_node: ClusterNode):
# TODO: Move to healtcheck classes # TODO: Move to healtcheck classes
svc_name = cluster_node.service(StorageNode).get_service_systemctl_name() svc_name = cluster_node.service(StorageNode).get_service_systemctl_name()
with allure.step(f"Check service {svc_name} is active"): with reporter.step(f"Check service {svc_name} is active"):
result = cluster_node.host.get_shell().exec(f"systemctl is-active {svc_name}") result = cluster_node.host.get_shell().exec(f"systemctl is-active {svc_name}")
assert "active" == result.stdout.strip(), f"Service {svc_name} should be in active state" assert "active" == result.stdout.strip(), f"Service {svc_name} should be in active state"
with allure.step(f"Check service {svc_name} is active more than {SERVICE_ACTIVE_TIME} minutes"): with reporter.step(f"Check service {svc_name} is active more than {SERVICE_ACTIVE_TIME} minutes"):
result = cluster_node.host.get_shell().exec( result = cluster_node.host.get_shell().exec(
f"systemctl show {svc_name} --property ActiveEnterTimestamp | cut -d '=' -f 2" f"systemctl show {svc_name} --property ActiveEnterTimestamp | cut -d '=' -f 2"
) )
@ -362,15 +398,15 @@ def run_health_check(healthcheck: Healthcheck, cluster: Cluster, request: pytest
parallel(healthcheck.storage_healthcheck, cluster.cluster_nodes) parallel(healthcheck.storage_healthcheck, cluster.cluster_nodes)
@allure.step("Prepare wallet and deposit") @reporter.step("Prepare wallet and deposit")
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def default_wallet(wallet_factory: WalletFactory) -> str: def default_wallet(wallet_factory: WalletFactory) -> str:
wallet = wallet_factory.create_wallet(password=DEFAULT_WALLET_PASS) wallet = wallet_factory.create_wallet(password=DEFAULT_WALLET_PASS)
allure.attach.file(wallet.path, os.path.basename(wallet.path), allure.attachment_type.JSON) reporter.attach(wallet.path, os.path.basename(wallet.path))
return wallet.path return wallet.path
@allure.step("[Class]: Container placement policy for keys") @reporter.step("[Class]: Container placement policy for keys")
@pytest.fixture(scope="class") @pytest.fixture(scope="class")
def auth_container_placement_policy(cluster: Cluster, request: pytest.FixtureRequest): def auth_container_placement_policy(cluster: Cluster, request: pytest.FixtureRequest):
placeholders = { placeholders = {
@ -389,5 +425,5 @@ def auth_container_placement_policy(cluster: Cluster, request: pytest.FixtureReq
@allure.title("Select random node for testing") @allure.title("Select random node for testing")
def node_under_test(cluster: Cluster) -> ClusterNode: def node_under_test(cluster: Cluster) -> ClusterNode:
selected_node = random.choice(cluster.cluster_nodes) selected_node = random.choice(cluster.cluster_nodes)
allure.attach(f"{selected_node}", "Selected node", allure.attachment_type.TEXT) reporter.attach(f"{selected_node}", "Selected node")
return selected_node return selected_node

View file

@ -2,6 +2,7 @@ import json
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.wellknown_acl import PRIVATE_ACL_F from frostfs_testlib.resources.wellknown_acl import PRIVATE_ACL_F
from frostfs_testlib.steps.cli.container import ( from frostfs_testlib.steps.cli.container import (
create_container, create_container,
@ -58,7 +59,7 @@ class TestContainer(ClusterTestBase):
if name: if name:
info_to_check.add(f"Name={name}") info_to_check.add(f"Name={name}")
with allure.step("Check container has correct information"): with reporter.step("Check container has correct information"):
expected_policy = placement_rule.casefold() expected_policy = placement_rule.casefold()
actual_policy = placement_policy_from_container(container_info) actual_policy = placement_policy_from_container(container_info)
assert ( assert (
@ -69,7 +70,7 @@ class TestContainer(ClusterTestBase):
expected_info = info.casefold() expected_info = info.casefold()
assert expected_info in container_info, f"Expected {expected_info} in container info:\n{container_info}" assert expected_info in container_info, f"Expected {expected_info} in container info:\n{container_info}"
with allure.step("Delete container and check it was deleted"): with reporter.step("Delete container and check it was deleted"):
delete_container( delete_container(
wallet, wallet,
cid, cid,
@ -87,7 +88,7 @@ class TestContainer(ClusterTestBase):
placement_rule = "REP 2 IN X CBF 1 SELECT 2 FROM * AS X" placement_rule = "REP 2 IN X CBF 1 SELECT 2 FROM * AS X"
cids: list[str] = [] cids: list[str] = []
with allure.step(f"Create {containers_count} containers"): with reporter.step(f"Create {containers_count} containers"):
for _ in range(containers_count): for _ in range(containers_count):
cids.append( cids.append(
create_container( create_container(
@ -100,7 +101,7 @@ class TestContainer(ClusterTestBase):
) )
) )
with allure.step("Wait for containers occur in container list"): with reporter.step("Wait for containers occur in container list"):
for cid in cids: for cid in cids:
wait_for_container_creation( wait_for_container_creation(
wallet, wallet,
@ -110,7 +111,7 @@ class TestContainer(ClusterTestBase):
endpoint=self.cluster.default_rpc_endpoint, endpoint=self.cluster.default_rpc_endpoint,
) )
with allure.step("Delete containers and check they were deleted"): with reporter.step("Delete containers and check they were deleted"):
for cid in cids: for cid in cids:
delete_container(wallet, cid, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint) delete_container(wallet, cid, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint)
self.tick_epoch() self.tick_epoch()

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@ from time import sleep
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.common import MORPH_BLOCK_TIME from frostfs_testlib.resources.common import MORPH_BLOCK_TIME
from frostfs_testlib.steps.cli.object import neo_go_query_height from frostfs_testlib.steps.cli.object import neo_go_query_height
from frostfs_testlib.storage.controllers import ClusterStateController from frostfs_testlib.storage.controllers import ClusterStateController
@ -13,18 +14,18 @@ from frostfs_testlib.utils import datetime_utils
@pytest.mark.time @pytest.mark.time
@pytest.mark.failover @pytest.mark.failover
class TestTime(ClusterTestBase): class TestTime(ClusterTestBase):
@allure.step("Neo-go should continue to release blocks") @reporter.step("Neo-go should continue to release blocks")
def check_nodes_block(self, cluster_state_controller: ClusterStateController): def check_nodes_block(self, cluster_state_controller: ClusterStateController):
count_blocks = {} count_blocks = {}
with allure.step("Get current block id"): with reporter.step("Get current block id"):
for cluster_node in self.cluster.cluster_nodes: for cluster_node in self.cluster.cluster_nodes:
cluster_state_controller.get_node_date(cluster_node) cluster_state_controller.get_node_date(cluster_node)
count_blocks[cluster_node] = neo_go_query_height( count_blocks[cluster_node] = neo_go_query_height(
shell=cluster_node.host.get_shell(), endpoint=cluster_node.morph_chain.get_http_endpoint() shell=cluster_node.host.get_shell(), endpoint=cluster_node.morph_chain.get_http_endpoint()
)["Latest block"] )["Latest block"]
with allure.step("Wait for 3 blocks"): with reporter.step("Wait for 3 blocks"):
sleep(datetime_utils.parse_time(MORPH_BLOCK_TIME) * 3) sleep(datetime_utils.parse_time(MORPH_BLOCK_TIME) * 3)
with allure.step("Current block id should be higher than before"): with reporter.step("Current block id should be higher than before"):
for cluster_node in self.cluster.cluster_nodes: for cluster_node in self.cluster.cluster_nodes:
shell = cluster_node.host.get_shell() shell = cluster_node.host.get_shell()
now_block = neo_go_query_height(shell=shell, endpoint=cluster_node.morph_chain.get_http_endpoint())[ now_block = neo_go_query_height(shell=shell, endpoint=cluster_node.morph_chain.get_http_endpoint())[
@ -44,34 +45,34 @@ class TestTime(ClusterTestBase):
timezone_utc = datetime.timezone.utc timezone_utc = datetime.timezone.utc
node_1, node_2, node_3 = cluster_nodes[0:3] node_1, node_2, node_3 = cluster_nodes[0:3]
with allure.step("On node 1, move the system time forward by 5 days"): with reporter.step("On node 1, move the system time forward by 5 days"):
cluster_state_controller.change_node_date( cluster_state_controller.change_node_date(
node_1, (datetime.datetime.now(timezone_utc) + datetime.timedelta(days=5)) node_1, (datetime.datetime.now(timezone_utc) + datetime.timedelta(days=5))
) )
self.check_nodes_block(cluster_state_controller) self.check_nodes_block(cluster_state_controller)
with allure.step("On node 2, move the system time back 5 days."): with reporter.step("On node 2, move the system time back 5 days."):
cluster_state_controller.change_node_date( cluster_state_controller.change_node_date(
node_2, (datetime.datetime.now(timezone_utc) - datetime.timedelta(days=5)) node_2, (datetime.datetime.now(timezone_utc) - datetime.timedelta(days=5))
) )
self.check_nodes_block(cluster_state_controller) self.check_nodes_block(cluster_state_controller)
with allure.step("On node 3, move the system time forward by 10 days"): with reporter.step("On node 3, move the system time forward by 10 days"):
cluster_state_controller.change_node_date( cluster_state_controller.change_node_date(
node_3, (datetime.datetime.now(timezone_utc) + datetime.timedelta(days=10)) node_3, (datetime.datetime.now(timezone_utc) + datetime.timedelta(days=10))
) )
self.check_nodes_block(cluster_state_controller) self.check_nodes_block(cluster_state_controller)
with allure.step("Return the time on all nodes to the current one"): with reporter.step("Return the time on all nodes to the current one"):
for cluster_node in self.cluster.cluster_nodes: for cluster_node in self.cluster.cluster_nodes:
cluster_state_controller.restore_node_date(cluster_node) cluster_state_controller.restore_node_date(cluster_node)
self.check_nodes_block(cluster_state_controller) self.check_nodes_block(cluster_state_controller)
with allure.step("Reboot all nodes"): with reporter.step("Reboot all nodes"):
cluster_state_controller.shutdown_cluster(mode="soft") cluster_state_controller.shutdown_cluster(mode="soft")
cluster_state_controller.start_stopped_hosts() cluster_state_controller.start_stopped_hosts()

View file

@ -5,6 +5,7 @@ from time import sleep
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.healthcheck.interfaces import Healthcheck from frostfs_testlib.healthcheck.interfaces import Healthcheck
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.steps.cli.container import create_container from frostfs_testlib.steps.cli.container import create_container
@ -46,10 +47,10 @@ class TestFailoverNetwork(ClusterTestBase):
@allure.title("Restore network") @allure.title("Restore network")
def restore_network(self, healthcheck: Healthcheck, cluster_state_controller: ClusterStateController): def restore_network(self, healthcheck: Healthcheck, cluster_state_controller: ClusterStateController):
yield yield
with allure.step(f"Count blocked nodes {len(blocked_nodes)}"): with reporter.step(f"Count blocked nodes {len(blocked_nodes)}"):
not_empty = len(blocked_nodes) != 0 not_empty = len(blocked_nodes) != 0
for node in list(blocked_nodes): for node in list(blocked_nodes):
with allure.step(f"Restore network for {node}"): with reporter.step(f"Restore network for {node}"):
cluster_state_controller.restore_traffic(mode="ports", node=node) cluster_state_controller.restore_traffic(mode="ports", node=node)
blocked_nodes.remove(node) blocked_nodes.remove(node)
if not_empty: if not_empty:
@ -79,7 +80,7 @@ class TestFailoverNetwork(ClusterTestBase):
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_hash = get_file_hash(file_path) file_hash = get_file_hash(file_path)
with allure.step("Create container"): with reporter.step("Create container"):
placement_rule = "REP 1 CBF 1" placement_rule = "REP 1 CBF 1"
cid = create_container( cid = create_container(
wallet=default_wallet, wallet=default_wallet,
@ -91,7 +92,7 @@ class TestFailoverNetwork(ClusterTestBase):
storage_objects = [] storage_objects = []
with allure.step("Put object"): with reporter.step("Put object"):
for attribute in OBJECT_ATTRIBUTES: for attribute in OBJECT_ATTRIBUTES:
oid = put_object_to_random_node( oid = put_object_to_random_node(
wallet=default_wallet, wallet=default_wallet,
@ -150,7 +151,7 @@ class TestFailoverNetwork(ClusterTestBase):
excluded_nodes = [] excluded_nodes = []
for node in nodes_to_block: for node in nodes_to_block:
with allure.step(f"Block incoming traffic at node {node} on port {PORTS_TO_BLOCK}"): with reporter.step(f"Block incoming traffic at node {node} on port {PORTS_TO_BLOCK}"):
block_node = [ block_node = [
cluster_node for cluster_node in self.cluster.cluster_nodes if cluster_node.storage_node == node cluster_node for cluster_node in self.cluster.cluster_nodes if cluster_node.storage_node == node
] ]
@ -163,7 +164,7 @@ class TestFailoverNetwork(ClusterTestBase):
ports=PORTS_TO_BLOCK, ports=PORTS_TO_BLOCK,
) )
with allure.step(f"Check object is not stored on node {node}"): with reporter.step(f"Check object is not stored on node {node}"):
new_nodes = wait_object_replication( new_nodes = wait_object_replication(
cid, cid,
oid, oid,
@ -173,12 +174,12 @@ class TestFailoverNetwork(ClusterTestBase):
) )
assert node not in new_nodes assert node not in new_nodes
with allure.step("Check object data is not corrupted"): with reporter.step("Check object data is not corrupted"):
got_file_path = get_object(wallet, cid, oid, endpoint=new_nodes[0].get_rpc_endpoint(), shell=self.shell) got_file_path = get_object(wallet, cid, oid, endpoint=new_nodes[0].get_rpc_endpoint(), shell=self.shell)
assert get_file_hash(source_file_path) == get_file_hash(got_file_path) assert get_file_hash(source_file_path) == get_file_hash(got_file_path)
for node in nodes_to_block: for node in nodes_to_block:
with allure.step(f"Unblock incoming traffic at host {node} on port {PORTS_TO_BLOCK}"): with reporter.step(f"Unblock incoming traffic at host {node} on port {PORTS_TO_BLOCK}"):
cluster_state_controller.restore_traffic(mode="ports", node=node) cluster_state_controller.restore_traffic(mode="ports", node=node)
block_node = [ block_node = [
cluster_node for cluster_node in self.cluster.cluster_nodes if cluster_node.storage_node == node cluster_node for cluster_node in self.cluster.cluster_nodes if cluster_node.storage_node == node
@ -186,7 +187,7 @@ class TestFailoverNetwork(ClusterTestBase):
blocked_nodes.remove(*block_node) blocked_nodes.remove(*block_node)
sleep(wakeup_node_timeout) sleep(wakeup_node_timeout)
with allure.step("Check object data is not corrupted"): with reporter.step("Check object data is not corrupted"):
new_nodes = wait_object_replication(cid, oid, 2, shell=self.shell, nodes=self.cluster.storage_nodes) new_nodes = wait_object_replication(cid, oid, 2, shell=self.shell, nodes=self.cluster.storage_nodes)
got_file_path = get_object(wallet, cid, oid, shell=self.shell, endpoint=new_nodes[0].get_rpc_endpoint()) got_file_path = get_object(wallet, cid, oid, shell=self.shell, endpoint=new_nodes[0].get_rpc_endpoint())
@ -204,7 +205,7 @@ class TestFailoverNetwork(ClusterTestBase):
): ):
storage_object = storage_objects[0] storage_object = storage_objects[0]
with allure.step("Search nodes with object"): with reporter.step("Search nodes with object"):
nodes_with_object = get_object_nodes( nodes_with_object = get_object_nodes(
cluster=self.cluster, cluster=self.cluster,
wallet=default_wallet, wallet=default_wallet,
@ -214,20 +215,20 @@ class TestFailoverNetwork(ClusterTestBase):
endpoint=self.cluster.default_rpc_endpoint, endpoint=self.cluster.default_rpc_endpoint,
) )
with allure.step("Get data interface to node"): with reporter.step("Get data interface to node"):
config_interfaces = list(nodes_with_object[0].host.config.interfaces.keys()) config_interfaces = list(nodes_with_object[0].host.config.interfaces.keys())
with allure.step(f"Get data in {config_interfaces}"): with reporter.step(f"Get data in {config_interfaces}"):
data_interfaces = [interface for interface in config_interfaces if "data" in interface] data_interfaces = [interface for interface in config_interfaces if "data" in interface]
with allure.step("Block data interfaces for node"): with reporter.step("Block data interfaces for node"):
for interface in data_interfaces: for interface in data_interfaces:
cluster_state_controller.down_interface(nodes=nodes_with_object, interface=interface) cluster_state_controller.down_interface(nodes=nodes_with_object, interface=interface)
with allure.step("Tick epoch and wait 2 block"): with reporter.step("Tick epoch and wait 2 block"):
nodes_without_an_object = list(set(self.cluster.cluster_nodes) - set(nodes_with_object)) nodes_without_an_object = list(set(self.cluster.cluster_nodes) - set(nodes_with_object))
self.tick_epochs(1, alive_node=nodes_without_an_object[0].storage_node, wait_block=2) self.tick_epochs(1, alive_node=nodes_without_an_object[0].storage_node, wait_block=2)
with allure.step("Get object for target nodes to data interfaces, expect false"): with reporter.step("Get object for target nodes to data interfaces, expect false"):
with pytest.raises(RuntimeError, match="return code: 1"): with pytest.raises(RuntimeError, match="return code: 1"):
get_object( get_object(
wallet=default_wallet, wallet=default_wallet,
@ -237,7 +238,7 @@ class TestFailoverNetwork(ClusterTestBase):
endpoint=nodes_with_object[0].storage_node.get_rpc_endpoint(), endpoint=nodes_with_object[0].storage_node.get_rpc_endpoint(),
) )
with allure.step(f"Get object others nodes, expect true"): with reporter.step(f"Get object others nodes, expect true"):
input_file = get_object( input_file = get_object(
wallet=default_wallet, wallet=default_wallet,
cid=storage_object.cid, cid=storage_object.cid,
@ -247,7 +248,7 @@ class TestFailoverNetwork(ClusterTestBase):
) )
file_wait_delete.append(input_file) file_wait_delete.append(input_file)
with allure.step("Restore interface and tick 1 epoch, wait 2 block"): with reporter.step("Restore interface and tick 1 epoch, wait 2 block"):
cluster_state_controller.restore_interfaces() cluster_state_controller.restore_interfaces()
self.tick_epochs(1, alive_node=nodes_without_an_object[0].storage_node, wait_block=2) self.tick_epochs(1, alive_node=nodes_without_an_object[0].storage_node, wait_block=2)
@ -264,7 +265,7 @@ class TestFailoverNetwork(ClusterTestBase):
): ):
storage_object = storage_objects[0] storage_object = storage_objects[0]
with allure.step("Search nodes with object"): with reporter.step("Search nodes with object"):
nodes_with_object = get_object_nodes( nodes_with_object = get_object_nodes(
cluster=self.cluster, cluster=self.cluster,
wallet=default_wallet, wallet=default_wallet,
@ -274,20 +275,20 @@ class TestFailoverNetwork(ClusterTestBase):
endpoint=self.cluster.default_rpc_endpoint, endpoint=self.cluster.default_rpc_endpoint,
) )
with allure.step("Get internal interface to node"): with reporter.step("Get internal interface to node"):
config_interfaces = list(nodes_with_object[0].host.config.interfaces.keys()) config_interfaces = list(nodes_with_object[0].host.config.interfaces.keys())
with allure.step(f"Get internal in {config_interfaces}"): with reporter.step(f"Get internal in {config_interfaces}"):
internal_interfaces = [interface for interface in config_interfaces if "internal" in interface] internal_interfaces = [interface for interface in config_interfaces if "internal" in interface]
with allure.step("Block internal interfaces for node"): with reporter.step("Block internal interfaces for node"):
for interface in internal_interfaces: for interface in internal_interfaces:
cluster_state_controller.down_interface(nodes=nodes_with_object, interface=interface) cluster_state_controller.down_interface(nodes=nodes_with_object, interface=interface)
with allure.step("Tick epoch and wait 2 block"): with reporter.step("Tick epoch and wait 2 block"):
nodes_without_an_object = list(set(self.cluster.cluster_nodes) - set(nodes_with_object)) nodes_without_an_object = list(set(self.cluster.cluster_nodes) - set(nodes_with_object))
self.tick_epochs(1, alive_node=nodes_without_an_object[0].storage_node, wait_block=2) self.tick_epochs(1, alive_node=nodes_without_an_object[0].storage_node, wait_block=2)
with allure.step("Get object others node, expect false"): with reporter.step("Get object others node, expect false"):
with pytest.raises(RuntimeError, match="return code: 1"): with pytest.raises(RuntimeError, match="return code: 1"):
get_object( get_object(
wallet=default_wallet, wallet=default_wallet,
@ -297,7 +298,7 @@ class TestFailoverNetwork(ClusterTestBase):
endpoint=nodes_without_an_object[0].storage_node.get_rpc_endpoint(), endpoint=nodes_without_an_object[0].storage_node.get_rpc_endpoint(),
) )
with allure.step("Put object, others node, expect false"): with reporter.step("Put object, others node, expect false"):
with pytest.raises(RuntimeError, match="return code: 1"): with pytest.raises(RuntimeError, match="return code: 1"):
put_object( put_object(
wallet=default_wallet, wallet=default_wallet,
@ -307,7 +308,7 @@ class TestFailoverNetwork(ClusterTestBase):
endpoint=nodes_without_an_object[0].storage_node.get_rpc_endpoint(), endpoint=nodes_without_an_object[0].storage_node.get_rpc_endpoint(),
) )
with allure.step(f"Get object nodes with object, expect true"): with reporter.step(f"Get object nodes with object, expect true"):
input_file = get_object( input_file = get_object(
wallet=default_wallet, wallet=default_wallet,
cid=storage_object.cid, cid=storage_object.cid,
@ -317,7 +318,7 @@ class TestFailoverNetwork(ClusterTestBase):
) )
file_wait_delete.append(input_file) file_wait_delete.append(input_file)
with allure.step(f"Put object nodes with object, expect true"): with reporter.step(f"Put object nodes with object, expect true"):
temp_file_path = generate_file(simple_object_size.value) temp_file_path = generate_file(simple_object_size.value)
_ = put_object( _ = put_object(
wallet=default_wallet, wallet=default_wallet,
@ -327,7 +328,7 @@ class TestFailoverNetwork(ClusterTestBase):
endpoint=nodes_with_object[0].storage_node.get_rpc_endpoint(), endpoint=nodes_with_object[0].storage_node.get_rpc_endpoint(),
) )
file_wait_delete.append(temp_file_path) file_wait_delete.append(temp_file_path)
with allure.step("Restore interface and tick 1 epoch, wait 2 block"): with reporter.step("Restore interface and tick 1 epoch, wait 2 block"):
cluster_state_controller.restore_interfaces() cluster_state_controller.restore_interfaces()
self.tick_epochs(1, alive_node=nodes_without_an_object[0].storage_node, wait_block=2) self.tick_epochs(1, alive_node=nodes_without_an_object[0].storage_node, wait_block=2)
@ -350,13 +351,13 @@ class TestFailoverNetwork(ClusterTestBase):
other_interface: Interfaces, other_interface: Interfaces,
): ):
cluster_nodes = self.cluster.cluster_nodes cluster_nodes = self.cluster.cluster_nodes
with allure.step(f"Block {block_interface.value} interfaces"): with reporter.step(f"Block {block_interface.value} interfaces"):
cluster_state_controller.down_interface(cluster_nodes, block_interface.value) cluster_state_controller.down_interface(cluster_nodes, block_interface.value)
with allure.step("Tick 1 epoch and wait 2 block for sync all nodes"): with reporter.step("Tick 1 epoch and wait 2 block for sync all nodes"):
self.tick_epochs(1, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(1, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with allure.step("Create container"): with reporter.step("Create container"):
cid = create_container( cid = create_container(
wallet=default_wallet, wallet=default_wallet,
shell=self.shell, shell=self.shell,
@ -364,7 +365,7 @@ class TestFailoverNetwork(ClusterTestBase):
rule="REP 4 CBF 1", rule="REP 4 CBF 1",
) )
with allure.step("Put object"): with reporter.step("Put object"):
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_wait_delete.append(file_path) file_wait_delete.append(file_path)
@ -376,7 +377,7 @@ class TestFailoverNetwork(ClusterTestBase):
endpoint=f"{cluster_nodes[0].get_data_interface(other_interface.value)[0]}:8080", endpoint=f"{cluster_nodes[0].get_data_interface(other_interface.value)[0]}:8080",
) )
with allure.step("Get object"): with reporter.step("Get object"):
file_get_path = get_object( file_get_path = get_object(
wallet=default_wallet, wallet=default_wallet,
cid=cid, cid=cid,
@ -386,7 +387,7 @@ class TestFailoverNetwork(ClusterTestBase):
) )
file_wait_delete.append(file_get_path) file_wait_delete.append(file_get_path)
with allure.step("Restore interfaces all nodes"): with reporter.step("Restore interfaces all nodes"):
cluster_state_controller.restore_interfaces() cluster_state_controller.restore_interfaces()
self.tick_epochs(1, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(1, alive_node=cluster_nodes[0].storage_node, wait_block=2)
@ -407,19 +408,19 @@ class TestFailoverNetwork(ClusterTestBase):
cluster_nodes = self.cluster.cluster_nodes cluster_nodes = self.cluster.cluster_nodes
latest_block = {} latest_block = {}
with allure.step("Get block all nodes"): with reporter.step("Get block all nodes"):
for cluster_node in cluster_nodes: for cluster_node in cluster_nodes:
latest_block[cluster_node] = neo_go_query_height( latest_block[cluster_node] = neo_go_query_height(
shell=cluster_node.host.get_shell(), endpoint=cluster_node.morph_chain.get_http_endpoint() shell=cluster_node.host.get_shell(), endpoint=cluster_node.morph_chain.get_http_endpoint()
) )
with allure.step(f"Block {interface} interfaces"): with reporter.step(f"Block {interface} interfaces"):
cluster_state_controller.down_interface(cluster_nodes, interface.value) cluster_state_controller.down_interface(cluster_nodes, interface.value)
with allure.step("Tick 1 epoch and wait 2 block for sync all nodes"): with reporter.step("Tick 1 epoch and wait 2 block for sync all nodes"):
self.tick_epochs(1, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(1, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with allure.step("Create container"): with reporter.step("Create container"):
cid = create_container( cid = create_container(
wallet=default_wallet, wallet=default_wallet,
shell=self.shell, shell=self.shell,
@ -427,7 +428,7 @@ class TestFailoverNetwork(ClusterTestBase):
rule="REP 4 CBF 1", rule="REP 4 CBF 1",
) )
with allure.step(f"Put object, after down {interface}"): with reporter.step(f"Put object, after down {interface}"):
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_wait_delete.append(file_path) file_wait_delete.append(file_path)
@ -439,7 +440,7 @@ class TestFailoverNetwork(ClusterTestBase):
endpoint=self.cluster.default_rpc_endpoint, endpoint=self.cluster.default_rpc_endpoint,
) )
with allure.step("Get object"): with reporter.step("Get object"):
file_get_path = get_object( file_get_path = get_object(
wallet=default_wallet, wallet=default_wallet,
cid=cid, cid=cid,
@ -451,19 +452,19 @@ class TestFailoverNetwork(ClusterTestBase):
now_block = {} now_block = {}
with allure.step("Get actual block"): with reporter.step("Get actual block"):
for cluster_node in cluster_nodes: for cluster_node in cluster_nodes:
now_block[cluster_node] = neo_go_query_height( now_block[cluster_node] = neo_go_query_height(
shell=cluster_node.host.get_shell(), endpoint=cluster_node.morph_chain.get_http_endpoint() shell=cluster_node.host.get_shell(), endpoint=cluster_node.morph_chain.get_http_endpoint()
) )
with allure.step(f"Compare block"): with reporter.step(f"Compare block"):
for cluster_node, items in now_block.items(): for cluster_node, items in now_block.items():
with allure.step( with reporter.step(
f"Node - {cluster_node.host_ip}, old block - {latest_block[cluster_node]['Latest block']}, " f"Node - {cluster_node.host_ip}, old block - {latest_block[cluster_node]['Latest block']}, "
f"now block - {now_block[cluster_node]['Latest block']}" f"now block - {now_block[cluster_node]['Latest block']}"
): ):
assert latest_block[cluster_node]["Latest block"] < now_block[cluster_node]["Latest block"] assert latest_block[cluster_node]["Latest block"] < now_block[cluster_node]["Latest block"]
with allure.step("Restore interfaces all nodes"): with reporter.step("Restore interfaces all nodes"):
cluster_state_controller.restore_interfaces() cluster_state_controller.restore_interfaces()
self.tick_epochs(1, alive_node=self.cluster.cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(1, alive_node=self.cluster.cluster_nodes[0].storage_node, wait_block=2)

View file

@ -1,10 +1,10 @@
import logging import logging
import os.path import os.path
import random import random
import time
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.common import MORPH_BLOCK_TIME from frostfs_testlib.resources.common import MORPH_BLOCK_TIME
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.steps.cli.container import StorageContainer, StorageContainerInfo, create_container from frostfs_testlib.steps.cli.container import StorageContainer, StorageContainerInfo, create_container
@ -36,7 +36,7 @@ class TestFailoverServer(ClusterTestBase):
def wait_node_in_map(self, *args, **kwargs): def wait_node_in_map(self, *args, **kwargs):
check_node_in_map(*args, **kwargs) check_node_in_map(*args, **kwargs)
@allure.step("Create {count_containers} containers and {count_files} objects") @reporter.step("Create {count_containers} containers and {count_files} objects")
@pytest.fixture @pytest.fixture
def containers( def containers(
self, self,
@ -64,7 +64,7 @@ class TestFailoverServer(ClusterTestBase):
return containers return containers
@allure.step("Creation container") @reporter.step("Creation container")
@pytest.fixture() @pytest.fixture()
def container(self, default_wallet: str) -> StorageContainer: def container(self, default_wallet: str) -> StorageContainer:
select = len(self.cluster.cluster_nodes) select = len(self.cluster.cluster_nodes)
@ -80,7 +80,7 @@ class TestFailoverServer(ClusterTestBase):
storage_cont_info = StorageContainerInfo(id=cont_id, wallet_file=wallet) storage_cont_info = StorageContainerInfo(id=cont_id, wallet_file=wallet)
return StorageContainer(storage_container_info=storage_cont_info, shell=self.shell, cluster=self.cluster) return StorageContainer(storage_container_info=storage_cont_info, shell=self.shell, cluster=self.cluster)
@allure.step("Create object and delete after test") @reporter.step("Create object and delete after test")
@pytest.fixture(scope="class") @pytest.fixture(scope="class")
def storage_objects( def storage_objects(
self, self,
@ -101,16 +101,16 @@ class TestFailoverServer(ClusterTestBase):
yield object_list yield object_list
@allure.step("Select random node to stop and start it after test") @reporter.step("Select random node to stop and start it after test")
@pytest.fixture @pytest.fixture
def node_to_stop( def node_to_stop(
self, node_under_test: ClusterNode, cluster_state_controller: ClusterStateController self, node_under_test: ClusterNode, cluster_state_controller: ClusterStateController
) -> ClusterNode: ) -> ClusterNode:
yield node_under_test yield node_under_test
with allure.step(f"start {node_under_test.storage_node}"): with reporter.step(f"start {node_under_test.storage_node}"):
cluster_state_controller.start_stopped_hosts() cluster_state_controller.start_stopped_hosts()
@allure.step("Upload object with nodes and compare") @reporter.step("Upload object with nodes and compare")
def get_corrupted_objects_list( def get_corrupted_objects_list(
self, nodes: list[StorageNode], storage_objects: list[StorageObjectInfo] self, nodes: list[StorageNode], storage_objects: list[StorageObjectInfo]
) -> list[StorageObjectInfo]: ) -> list[StorageObjectInfo]:
@ -179,34 +179,34 @@ class TestFailoverServer(ClusterTestBase):
cluster_state_controller: ClusterStateController, cluster_state_controller: ClusterStateController,
): ):
with allure.step(f"Remove {node_to_stop} from the list of nodes"): with reporter.step(f"Remove {node_to_stop} from the list of nodes"):
alive_nodes = list(set(self.cluster.cluster_nodes) - {node_to_stop}) alive_nodes = list(set(self.cluster.cluster_nodes) - {node_to_stop})
storage_nodes = [cluster.storage_node for cluster in alive_nodes] storage_nodes = [cluster.storage_node for cluster in alive_nodes]
with allure.step("Tick epoch and wait for 2 blocks"): with reporter.step("Tick epoch and wait for 2 blocks"):
self.tick_epochs(1, storage_nodes[0], wait_block=2) self.tick_epochs(1, storage_nodes[0], wait_block=2)
with allure.step(f"Stop node"): with reporter.step(f"Stop node"):
cluster_state_controller.stop_node_host(node=node_to_stop, mode="hard") cluster_state_controller.stop_node_host(node=node_to_stop, mode="hard")
with allure.step("Verify that there are no corrupted objects"): with reporter.step("Verify that there are no corrupted objects"):
corrupted_objects_list = self.get_corrupted_objects_list(storage_nodes, storage_objects) corrupted_objects_list = self.get_corrupted_objects_list(storage_nodes, storage_objects)
assert not corrupted_objects_list assert not corrupted_objects_list
with allure.step(f"check {node_to_stop.storage_node} in map"): with reporter.step(f"check {node_to_stop.storage_node} in map"):
self.wait_node_in_map(node_to_stop.storage_node, self.shell, alive_node=storage_nodes[0]) self.wait_node_in_map(node_to_stop.storage_node, self.shell, alive_node=storage_nodes[0])
count_tick_epoch = int(alive_nodes[0].ir_node.get_netmap_cleaner_threshold()) + 4 count_tick_epoch = int(alive_nodes[0].ir_node.get_netmap_cleaner_threshold()) + 4
with allure.step(f"Tick {count_tick_epoch} epochs and wait for 2 blocks"): with reporter.step(f"Tick {count_tick_epoch} epochs and wait for 2 blocks"):
self.tick_epochs(count_tick_epoch, storage_nodes[0], wait_block=2) self.tick_epochs(count_tick_epoch, storage_nodes[0], wait_block=2)
with allure.step(f"Check {node_to_stop} in not map"): with reporter.step(f"Check {node_to_stop} in not map"):
self.wait_node_not_in_map(node_to_stop.storage_node, self.shell, alive_node=storage_nodes[0]) self.wait_node_not_in_map(node_to_stop.storage_node, self.shell, alive_node=storage_nodes[0])
with allure.step(f"Verify that there are no corrupted objects after {count_tick_epoch} epoch"): with reporter.step(f"Verify that there are no corrupted objects after {count_tick_epoch} epoch"):
corrupted_objects_list = self.get_corrupted_objects_list(storage_nodes, storage_objects) corrupted_objects_list = self.get_corrupted_objects_list(storage_nodes, storage_objects)
assert not corrupted_objects_list assert not corrupted_objects_list
@ -218,25 +218,25 @@ class TestFailoverServer(ClusterTestBase):
node_to_stop: ClusterNode, node_to_stop: ClusterNode,
cluster_state_controller: ClusterStateController, cluster_state_controller: ClusterStateController,
): ):
with allure.step(f"Remove {node_to_stop} from the list of nodes"): with reporter.step(f"Remove {node_to_stop} from the list of nodes"):
storage_nodes = list(set(self.cluster.storage_nodes) - {node_to_stop.storage_node}) storage_nodes = list(set(self.cluster.storage_nodes) - {node_to_stop.storage_node})
with allure.step("Tick epoch and wait for 2 blocks"): with reporter.step("Tick epoch and wait for 2 blocks"):
self.tick_epochs(1, storage_nodes[0], wait_block=2) self.tick_epochs(1, storage_nodes[0], wait_block=2)
with allure.step(f"Stop node"): with reporter.step(f"Stop node"):
cluster_state_controller.stop_node_host(node=node_to_stop, mode="hard") cluster_state_controller.stop_node_host(node=node_to_stop, mode="hard")
with allure.step("Verify that there are no corrupted objects"): with reporter.step("Verify that there are no corrupted objects"):
corrupted_objects_list = self.get_corrupted_objects_list(storage_nodes, storage_objects) corrupted_objects_list = self.get_corrupted_objects_list(storage_nodes, storage_objects)
assert not corrupted_objects_list assert not corrupted_objects_list
with allure.step(f"Check {node_to_stop} in map"): with reporter.step(f"Check {node_to_stop} in map"):
self.wait_node_in_map(node_to_stop.storage_node, self.shell, alive_node=storage_nodes[0]) self.wait_node_in_map(node_to_stop.storage_node, self.shell, alive_node=storage_nodes[0])
cluster_state_controller.start_node_host(node_to_stop) cluster_state_controller.start_node_host(node_to_stop)
with allure.step("Verify that there are no corrupted objects"): with reporter.step("Verify that there are no corrupted objects"):
corrupted_objects_list = self.get_corrupted_objects_list(storage_nodes, storage_objects) corrupted_objects_list = self.get_corrupted_objects_list(storage_nodes, storage_objects)
assert not corrupted_objects_list assert not corrupted_objects_list
@ -253,12 +253,12 @@ class TestFailoverServer(ClusterTestBase):
object_info, object_nodes = object_and_nodes object_info, object_nodes = object_and_nodes
node_not_object = list(set(self.cluster.cluster_nodes) - set(object_nodes))[0] node_not_object = list(set(self.cluster.cluster_nodes) - set(object_nodes))[0]
with allure.step("Stop all nodes with object, except 1"): with reporter.step("Stop all nodes with object, except 1"):
for cluster_node in object_nodes[1:]: for cluster_node in object_nodes[1:]:
cluster_state_controller.stop_node_host(node=cluster_node, mode="hard") cluster_state_controller.stop_node_host(node=cluster_node, mode="hard")
with allure.step("Get object"): with reporter.step("Get object"):
with allure.step(f"Get operation to {node_not_object} where it does not exist, expect success"): with reporter.step(f"Get operation to {node_not_object} where it does not exist, expect success"):
get_object( get_object(
wallet=default_wallet, wallet=default_wallet,
cid=object_info.cid, cid=object_info.cid,
@ -266,7 +266,7 @@ class TestFailoverServer(ClusterTestBase):
shell=self.shell, shell=self.shell,
endpoint=node_not_object.storage_node.get_rpc_endpoint(), endpoint=node_not_object.storage_node.get_rpc_endpoint(),
) )
with allure.step(f"Get operation to {object_nodes[0]} with object, expect success"): with reporter.step(f"Get operation to {object_nodes[0]} with object, expect success"):
get_object( get_object(
wallet=default_wallet, wallet=default_wallet,
cid=object_info.cid, cid=object_info.cid,
@ -275,7 +275,7 @@ class TestFailoverServer(ClusterTestBase):
endpoint=object_nodes[0].storage_node.get_rpc_endpoint(), endpoint=object_nodes[0].storage_node.get_rpc_endpoint(),
) )
with allure.step(f"Put operation to node with object, expect error"): with reporter.step(f"Put operation to node with object, expect error"):
with pytest.raises(RuntimeError): with pytest.raises(RuntimeError):
put_object( put_object(
wallet=default_wallet, wallet=default_wallet,
@ -293,7 +293,7 @@ class TestFailoverServer(ClusterTestBase):
simple_file: str, simple_file: str,
up_stop_nodes: None, up_stop_nodes: None,
): ):
with allure.step("Creating a container with a full network map"): with reporter.step("Creating a container with a full network map"):
select = len(self.cluster.cluster_nodes) select = len(self.cluster.cluster_nodes)
placement_rule = f"REP {select - 2} IN X CBF 2 SELECT {select} FROM * AS X" placement_rule = f"REP {select - 2} IN X CBF 2 SELECT {select} FROM * AS X"
cid_1 = create_container( cid_1 = create_container(
@ -303,7 +303,7 @@ class TestFailoverServer(ClusterTestBase):
rule=placement_rule, rule=placement_rule,
basic_acl=PUBLIC_ACL, basic_acl=PUBLIC_ACL,
) )
with allure.step("Put object"): with reporter.step("Put object"):
oid = put_object( oid = put_object(
wallet=default_wallet, wallet=default_wallet,
path=simple_file, path=simple_file,
@ -311,7 +311,7 @@ class TestFailoverServer(ClusterTestBase):
shell=self.shell, shell=self.shell,
endpoint=self.cluster.default_rpc_endpoint, endpoint=self.cluster.default_rpc_endpoint,
) )
with allure.step("Search nodes with object"): with reporter.step("Search nodes with object"):
object_nodes = get_object_nodes( object_nodes = get_object_nodes(
cluster=self.cluster, cluster=self.cluster,
wallet=default_wallet, wallet=default_wallet,
@ -320,9 +320,9 @@ class TestFailoverServer(ClusterTestBase):
shell=self.shell, shell=self.shell,
endpoint=self.cluster.default_rpc_endpoint, endpoint=self.cluster.default_rpc_endpoint,
) )
with allure.step("Turn off random node with object"): with reporter.step("Turn off random node with object"):
cluster_state_controller.stop_node_host(node=random.choice(object_nodes[1:]), mode="hard") cluster_state_controller.stop_node_host(node=random.choice(object_nodes[1:]), mode="hard")
with allure.step("Checking PUT operation"): with reporter.step("Checking PUT operation"):
oid_2 = put_object( oid_2 = put_object(
wallet=default_wallet, wallet=default_wallet,
path=simple_file, path=simple_file,
@ -330,7 +330,7 @@ class TestFailoverServer(ClusterTestBase):
shell=self.shell, shell=self.shell,
endpoint=self.cluster.default_rpc_endpoint, endpoint=self.cluster.default_rpc_endpoint,
) )
with allure.step("Checking GET operation"): with reporter.step("Checking GET operation"):
get_file = get_object( get_file = get_object(
wallet=default_wallet, wallet=default_wallet,
cid=cid_1, cid=cid_1,
@ -339,7 +339,7 @@ class TestFailoverServer(ClusterTestBase):
endpoint=object_nodes[0].storage_node.get_rpc_endpoint(), endpoint=object_nodes[0].storage_node.get_rpc_endpoint(),
) )
os.remove(get_file) os.remove(get_file)
with allure.step("Checking creating container"): with reporter.step("Checking creating container"):
_ = create_container( _ = create_container(
default_wallet, default_wallet,
shell=self.shell, shell=self.shell,

View file

@ -5,6 +5,7 @@ from time import sleep
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.common import MORPH_BLOCK_TIME from frostfs_testlib.resources.common import MORPH_BLOCK_TIME
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.s3 import S3ClientWrapper, VersioningStatus from frostfs_testlib.s3 import S3ClientWrapper, VersioningStatus
@ -43,14 +44,14 @@ def file_keeper():
keeper.restore_files() keeper.restore_files()
@allure.step("Return all stopped hosts") @reporter.step("Return all stopped hosts")
@pytest.fixture(scope="function", autouse=True) @pytest.fixture(scope="function", autouse=True)
def after_run_return_all_stopped_hosts(cluster_state_controller: ClusterStateController) -> str: def after_run_return_all_stopped_hosts(cluster_state_controller: ClusterStateController) -> str:
yield yield
cluster_state_controller.start_stopped_hosts() cluster_state_controller.start_stopped_hosts()
@allure.step("Return all stopped services after test") @reporter.step("Return all stopped services after test")
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def after_run_return_all_stopped_services(cluster_state_controller: ClusterStateController): def after_run_return_all_stopped_services(cluster_state_controller: ClusterStateController):
yield yield
@ -77,7 +78,7 @@ class TestFailoverStorage(ClusterTestBase):
source_file_path = generate_file(simple_object_size.value) source_file_path = generate_file(simple_object_size.value)
stopped_hosts_nodes = [] stopped_hosts_nodes = []
with allure.step(f"Create container and put object"): with reporter.step(f"Create container and put object"):
cid = create_container( cid = create_container(
wallet, wallet,
shell=self.shell, shell=self.shell,
@ -87,10 +88,10 @@ class TestFailoverStorage(ClusterTestBase):
) )
oid = put_object_to_random_node(wallet, source_file_path, cid, shell=self.shell, cluster=self.cluster) oid = put_object_to_random_node(wallet, source_file_path, cid, shell=self.shell, cluster=self.cluster)
with allure.step(f"Wait for replication and get nodes with object"): with reporter.step(f"Wait for replication and get nodes with object"):
nodes_with_object = wait_object_replication(cid, oid, 2, shell=self.shell, nodes=self.cluster.storage_nodes) nodes_with_object = wait_object_replication(cid, oid, 2, shell=self.shell, nodes=self.cluster.storage_nodes)
with allure.step(f"Stop 2 nodes with object and wait replication one by one"): with reporter.step(f"Stop 2 nodes with object and wait replication one by one"):
for storage_node in random.sample(nodes_with_object, 2): for storage_node in random.sample(nodes_with_object, 2):
stopped_hosts_nodes.append(storage_node) stopped_hosts_nodes.append(storage_node)
@ -105,16 +106,16 @@ class TestFailoverStorage(ClusterTestBase):
nodes=list(set(self.cluster.storage_nodes) - {*stopped_hosts_nodes}), nodes=list(set(self.cluster.storage_nodes) - {*stopped_hosts_nodes}),
) )
with allure.step("Check object data is not corrupted"): with reporter.step("Check object data is not corrupted"):
got_file_path = get_object( got_file_path = get_object(
wallet, cid, oid, endpoint=replicated_nodes[0].get_rpc_endpoint(), shell=self.shell wallet, cid, oid, endpoint=replicated_nodes[0].get_rpc_endpoint(), shell=self.shell
) )
assert get_file_hash(source_file_path) == get_file_hash(got_file_path) assert get_file_hash(source_file_path) == get_file_hash(got_file_path)
with allure.step("Return all hosts"): with reporter.step("Return all hosts"):
cluster_state_controller.start_stopped_hosts() cluster_state_controller.start_stopped_hosts()
with allure.step("Check object data is not corrupted"): with reporter.step("Check object data is not corrupted"):
replicated_nodes = wait_object_replication(cid, oid, 2, shell=self.shell, nodes=self.cluster.storage_nodes) replicated_nodes = wait_object_replication(cid, oid, 2, shell=self.shell, nodes=self.cluster.storage_nodes)
got_file_path = get_object( got_file_path = get_object(
wallet, cid, oid, shell=self.shell, endpoint=replicated_nodes[0].get_rpc_endpoint() wallet, cid, oid, shell=self.shell, endpoint=replicated_nodes[0].get_rpc_endpoint()
@ -131,38 +132,38 @@ class TestFailoverStorage(ClusterTestBase):
): ):
default_node = self.cluster.cluster_nodes[0] default_node = self.cluster.cluster_nodes[0]
with allure.step("Turn S3 GW off on default node"): with reporter.step("Turn S3 GW off on default node"):
cluster_state_controller.stop_service_of_type(default_node, S3Gate) cluster_state_controller.stop_service_of_type(default_node, S3Gate)
with allure.step("Turn off storage on default node"): with reporter.step("Turn off storage on default node"):
cluster_state_controller.stop_service_of_type(default_node, StorageNode) cluster_state_controller.stop_service_of_type(default_node, StorageNode)
with allure.step("Turn on S3 GW on default node"): with reporter.step("Turn on S3 GW on default node"):
cluster_state_controller.start_service_of_type(default_node, S3Gate) cluster_state_controller.start_service_of_type(default_node, S3Gate)
with allure.step("Turn on storage on default node"): with reporter.step("Turn on storage on default node"):
cluster_state_controller.start_service_of_type(default_node, StorageNode) cluster_state_controller.start_service_of_type(default_node, StorageNode)
with allure.step("Create bucket with REP 1 SELECT 1 policy"): with reporter.step("Create bucket with REP 1 SELECT 1 policy"):
bucket = s3_client.create_bucket( bucket = s3_client.create_bucket(
location_constraint="load-1-1", location_constraint="load-1-1",
) )
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
with allure.step("Put object into bucket"): with reporter.step("Put object into bucket"):
put_object = s3_client.put_object(bucket, file_path) put_object = s3_client.put_object(bucket, file_path)
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name]) s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
with allure.step("Turn off all storage nodes except default"): with reporter.step("Turn off all storage nodes except default"):
for node in self.cluster.cluster_nodes[1:]: for node in self.cluster.cluster_nodes[1:]:
with allure.step(f"Stop storage service on node: {node}"): with reporter.step(f"Stop storage service on node: {node}"):
cluster_state_controller.stop_service_of_type(node, StorageNode) cluster_state_controller.stop_service_of_type(node, StorageNode)
with allure.step("Check that object is available"): with reporter.step("Check that object is available"):
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name]) s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
with allure.step("Start storage nodes"): with reporter.step("Start storage nodes"):
cluster_state_controller.start_all_stopped_services() cluster_state_controller.start_all_stopped_services()
@ -173,11 +174,11 @@ class TestEmptyMap(ClusterTestBase):
A set of tests for makes map empty and verify that we can read objects after that A set of tests for makes map empty and verify that we can read objects after that
""" """
@allure.step("Teardown after EmptyMap offline test") @reporter.step("Teardown after EmptyMap offline test")
@pytest.fixture() @pytest.fixture()
def empty_map_offline_teardown(self): def empty_map_offline_teardown(self):
yield yield
with allure.step("Return all storage nodes to network map"): with reporter.step("Return all storage nodes to network map"):
for node in stopped_nodes: for node in stopped_nodes:
include_node_to_network_map(node, node, shell=self.shell, cluster=self.cluster) include_node_to_network_map(node, node, shell=self.shell, cluster=self.cluster)
stopped_nodes.remove(node) stopped_nodes.remove(node)
@ -212,31 +213,31 @@ class TestEmptyMap(ClusterTestBase):
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
with allure.step("Put object into bucket"): with reporter.step("Put object into bucket"):
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
with allure.step("Check that object exists in bucket"): with reporter.step("Check that object exists in bucket"):
s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_objects) s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_objects)
storage_nodes = self.cluster.storage_nodes storage_nodes = self.cluster.storage_nodes
with allure.step("Exclude all storage nodes from network map"): with reporter.step("Exclude all storage nodes from network map"):
for node in storage_nodes: for node in storage_nodes:
stopped_nodes.append(node) stopped_nodes.append(node)
exclude_node_from_network_map(node, node, shell=self.shell, cluster=self.cluster) exclude_node_from_network_map(node, node, shell=self.shell, cluster=self.cluster)
with allure.step("Return all storage nodes to network map"): with reporter.step("Return all storage nodes to network map"):
for node in storage_nodes: for node in storage_nodes:
include_node_to_network_map(node, node, shell=self.shell, cluster=self.cluster) include_node_to_network_map(node, node, shell=self.shell, cluster=self.cluster)
stopped_nodes.remove(node) stopped_nodes.remove(node)
with allure.step("Check that we can read object"): with reporter.step("Check that we can read object"):
s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_objects) s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_objects)
@allure.step("Teardown after EmptyMap stop service test") @reporter.step("Teardown after EmptyMap stop service test")
@pytest.fixture() @pytest.fixture()
def empty_map_stop_service_teardown(self, cluster_state_controller: ClusterStateController): def empty_map_stop_service_teardown(self, cluster_state_controller: ClusterStateController):
yield yield
with allure.step("Return all storage nodes to network map"): with reporter.step("Return all storage nodes to network map"):
cluster_state_controller.start_all_stopped_services() cluster_state_controller.start_all_stopped_services()
for node in stopped_nodes: for node in stopped_nodes:
check_node_in_map(node, shell=self.shell, alive_node=node) check_node_in_map(node, shell=self.shell, alive_node=node)
@ -275,31 +276,31 @@ class TestEmptyMap(ClusterTestBase):
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
with allure.step("Put object into bucket"): with reporter.step("Put object into bucket"):
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
with allure.step("Check that object exists in bucket"): with reporter.step("Check that object exists in bucket"):
s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_objects) s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_objects)
with allure.step("Stop all storage nodes"): with reporter.step("Stop all storage nodes"):
cluster_state_controller.stop_services_of_type(StorageNode) cluster_state_controller.stop_services_of_type(StorageNode)
with allure.step("Remove all nodes from network map"): with reporter.step("Remove all nodes from network map"):
remove_nodes_from_map_morph( remove_nodes_from_map_morph(
shell=self.shell, cluster=self.cluster, remove_nodes=self.cluster.services(StorageNode) shell=self.shell, cluster=self.cluster, remove_nodes=self.cluster.services(StorageNode)
) )
with allure.step("Return all storage nodes to network map"): with reporter.step("Return all storage nodes to network map"):
self.return_nodes_after_stop_with_check_empty_map(cluster_state_controller) self.return_nodes_after_stop_with_check_empty_map(cluster_state_controller)
with allure.step("Check that object exists in bucket"): with reporter.step("Check that object exists in bucket"):
s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_objects) s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_objects)
@allure.step("Return all nodes to cluster with check empty map first") @reporter.step("Return all nodes to cluster with check empty map first")
def return_nodes_after_stop_with_check_empty_map(self, cluster_state_controller: ClusterStateController) -> None: def return_nodes_after_stop_with_check_empty_map(self, cluster_state_controller: ClusterStateController) -> None:
first_node = self.cluster.cluster_nodes[0].service(StorageNode) first_node = self.cluster.cluster_nodes[0].service(StorageNode)
with allure.step("Start first node and check network map"): with reporter.step("Start first node and check network map"):
cluster_state_controller.start_service_of_type(self.cluster.cluster_nodes[0], StorageNode) cluster_state_controller.start_service_of_type(self.cluster.cluster_nodes[0], StorageNode)
wait_for_node_to_be_ready(first_node) wait_for_node_to_be_ready(first_node)
@ -331,33 +332,33 @@ class TestEmptyMap(ClusterTestBase):
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
object_versions = [] object_versions = []
with allure.step("Put object into one bucket"): with reporter.step("Put object into one bucket"):
put_object = s3_client.put_object(bucket, file_path) put_object = s3_client.put_object(bucket, file_path)
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name]) s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
object_versions.append(put_object) object_versions.append(put_object)
with allure.step("Stop all storage nodes"): with reporter.step("Stop all storage nodes"):
cluster_state_controller.stop_services_of_type(StorageNode) cluster_state_controller.stop_services_of_type(StorageNode)
with allure.step("Delete blobovnicza and fstree from all nodes"): with reporter.step("Delete blobovnicza and fstree from all nodes"):
for node in self.cluster.storage_nodes: for node in self.cluster.storage_nodes:
node.delete_blobovnicza() node.delete_blobovnicza()
node.delete_fstree() node.delete_fstree()
with allure.step("Start all storage nodes"): with reporter.step("Start all storage nodes"):
cluster_state_controller.start_all_stopped_services() cluster_state_controller.start_all_stopped_services()
# need to get Delete Marker first # need to get Delete Marker first
with allure.step("Delete the object from the bucket"): with reporter.step("Delete the object from the bucket"):
delete_object = s3_client.delete_object(bucket, file_name) delete_object = s3_client.delete_object(bucket, file_name)
object_versions.append(delete_object["VersionId"]) object_versions.append(delete_object["VersionId"])
# and now delete all versions of object (including Delete Markers) # and now delete all versions of object (including Delete Markers)
with allure.step("Delete all versions of the object from the bucket"): with reporter.step("Delete all versions of the object from the bucket"):
for version in object_versions: for version in object_versions:
delete_object = s3_client.delete_object(bucket, file_name, version_id=version) delete_object = s3_client.delete_object(bucket, file_name, version_id=version)
with allure.step("Delete bucket"): with reporter.step("Delete bucket"):
s3_client.delete_bucket(bucket) s3_client.delete_bucket(bucket)
@allure.title("Object loss from fstree/blobovnicza (versioning=disabled, s3_client={s3_client})") @allure.title("Object loss from fstree/blobovnicza (versioning=disabled, s3_client={s3_client})")
@ -372,25 +373,25 @@ class TestEmptyMap(ClusterTestBase):
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
with allure.step("Put object into one bucket"): with reporter.step("Put object into one bucket"):
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name]) s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
with allure.step("Stop all storage nodes"): with reporter.step("Stop all storage nodes"):
cluster_state_controller.stop_services_of_type(StorageNode) cluster_state_controller.stop_services_of_type(StorageNode)
with allure.step("Delete blobovnicza and fstree from all nodes"): with reporter.step("Delete blobovnicza and fstree from all nodes"):
for node in self.cluster.storage_nodes: for node in self.cluster.storage_nodes:
node.delete_blobovnicza() node.delete_blobovnicza()
node.delete_fstree() node.delete_fstree()
with allure.step("Start all storage nodes"): with reporter.step("Start all storage nodes"):
cluster_state_controller.start_all_stopped_services() cluster_state_controller.start_all_stopped_services()
with allure.step("Delete the object from the bucket"): with reporter.step("Delete the object from the bucket"):
s3_client.delete_object(bucket, file_name) s3_client.delete_object(bucket, file_name)
with allure.step("Delete bucket"): with reporter.step("Delete bucket"):
s3_client.delete_bucket(bucket) s3_client.delete_bucket(bucket)
@pytest.mark.skip(reason="Need to increase cache lifetime") @pytest.mark.skip(reason="Need to increase cache lifetime")
@ -415,36 +416,36 @@ class TestEmptyMap(ClusterTestBase):
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
with allure.step("Put object into one bucket"): with reporter.step("Put object into one bucket"):
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name]) s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
with allure.step("Stop all storage nodes"): with reporter.step("Stop all storage nodes"):
cluster_state_controller.stop_services_of_type(StorageNode) cluster_state_controller.stop_services_of_type(StorageNode)
with allure.step("Delete pilorama.db from all nodes"): with reporter.step("Delete pilorama.db from all nodes"):
for node in self.cluster.storage_nodes: for node in self.cluster.storage_nodes:
node.delete_pilorama() node.delete_pilorama()
with allure.step("Start all storage nodes"): with reporter.step("Start all storage nodes"):
cluster_state_controller.start_all_stopped_services() cluster_state_controller.start_all_stopped_services()
with allure.step("Check list objects first time"): with reporter.step("Check list objects first time"):
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
assert objects_list, f"Expected not empty bucket" assert objects_list, f"Expected not empty bucket"
with allure.step("Check list objects second time"): with reporter.step("Check list objects second time"):
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
with allure.step("Delete bucket"): with reporter.step("Delete bucket"):
s3_client.delete_bucket(bucket) s3_client.delete_bucket(bucket)
@pytest.mark.failover @pytest.mark.failover
@pytest.mark.failover_data_loss @pytest.mark.failover_data_loss
class TestStorageDataLoss(ClusterTestBase): class TestStorageDataLoss(ClusterTestBase):
@allure.step("Get list of all piloramas on node") @reporter.step("Get list of all piloramas on node")
def get_piloramas_list(self, node: StorageNode) -> list: def get_piloramas_list(self, node: StorageNode) -> list:
data_directory_path = node.get_data_directory() data_directory_path = node.get_data_directory()
@ -470,10 +471,10 @@ class TestStorageDataLoss(ClusterTestBase):
): ):
allure.dynamic.description(after_run_return_all_stopped_services) allure.dynamic.description(after_run_return_all_stopped_services)
with allure.step("Create bucket"): with reporter.step("Create bucket"):
bucket = s3_client.create_bucket() bucket = s3_client.create_bucket()
with allure.step("Put objects into bucket"): with reporter.step("Put objects into bucket"):
simple_object_path = generate_file(simple_object_size.value) simple_object_path = generate_file(simple_object_size.value)
simple_object_key = s3_helper.object_key_from_file_path(simple_object_path) simple_object_key = s3_helper.object_key_from_file_path(simple_object_path)
@ -483,44 +484,44 @@ class TestStorageDataLoss(ClusterTestBase):
s3_client.put_object(bucket, simple_object_path) s3_client.put_object(bucket, simple_object_path)
s3_client.put_object(bucket, complex_object_path) s3_client.put_object(bucket, complex_object_path)
with allure.step("Check objects are in bucket"): with reporter.step("Check objects are in bucket"):
s3_helper.check_objects_in_bucket( s3_helper.check_objects_in_bucket(
s3_client, bucket, expected_objects=[simple_object_key, complex_object_key] s3_client, bucket, expected_objects=[simple_object_key, complex_object_key]
) )
with allure.step("Stop storage services on all nodes"): with reporter.step("Stop storage services on all nodes"):
cluster_state_controller.stop_services_of_type(StorageNode) cluster_state_controller.stop_services_of_type(StorageNode)
with allure.step("Delete metabase from all nodes"): with reporter.step("Delete metabase from all nodes"):
for node in cluster_state_controller.cluster.storage_nodes: for node in cluster_state_controller.cluster.storage_nodes:
node.delete_metabase() node.delete_metabase()
with allure.step("Enable resync_metabase option for storage services"): with reporter.step("Enable resync_metabase option for storage services"):
for storage_node in cluster_state_controller.cluster.storage_nodes: for storage_node in cluster_state_controller.cluster.storage_nodes:
with allure.step(f"Enable resync_metabase option for {storage_node}"): with reporter.step(f"Enable resync_metabase option for {storage_node}"):
config_file_path, config = storage_node.get_shards_config() config_file_path, config = storage_node.get_shards_config()
if not config["storage"]["shard"]["default"]["resync_metabase"]: if not config["storage"]["shard"]["default"]["resync_metabase"]:
file_keeper.add(storage_node, config_file_path) file_keeper.add(storage_node, config_file_path)
config["storage"]["shard"]["default"]["resync_metabase"] = True config["storage"]["shard"]["default"]["resync_metabase"] = True
storage_node.save_config(config, config_file_path) storage_node.save_config(config, config_file_path)
with allure.step("Start storage services on all nodes"): with reporter.step("Start storage services on all nodes"):
cluster_state_controller.start_all_stopped_services() cluster_state_controller.start_all_stopped_services()
with allure.step("Wait for tree rebalance"): with reporter.step("Wait for tree rebalance"):
# TODO: Use product metric when we have proper ones for this check # TODO: Use product metric when we have proper ones for this check
sleep(30) sleep(30)
with allure.step("Delete objects from bucket"): with reporter.step("Delete objects from bucket"):
with allure.step("Delete simple object from bucket"): with reporter.step("Delete simple object from bucket"):
with expect_not_raises(): with expect_not_raises():
s3_client.delete_object(bucket, simple_object_key) s3_client.delete_object(bucket, simple_object_key)
with allure.step("Delete complex object from bucket"): with reporter.step("Delete complex object from bucket"):
with expect_not_raises(): with expect_not_raises():
s3_client.delete_object(bucket, complex_object_key) s3_client.delete_object(bucket, complex_object_key)
with allure.step("Delete bucket"): with reporter.step("Delete bucket"):
with expect_not_raises(): with expect_not_raises():
s3_client.delete_bucket(bucket) s3_client.delete_bucket(bucket)
@ -539,7 +540,7 @@ class TestStorageDataLoss(ClusterTestBase):
exception_messages = [] exception_messages = []
allure.dynamic.description(after_run_return_all_stopped_services) allure.dynamic.description(after_run_return_all_stopped_services)
with allure.step(f"Create container on node {node_under_test}"): with reporter.step(f"Create container on node {node_under_test}"):
locode = node_under_test.storage_node.get_un_locode() locode = node_under_test.storage_node.get_un_locode()
placement_rule = f"""REP 1 IN X placement_rule = f"""REP 1 IN X
CBF 1 CBF 1
@ -557,7 +558,7 @@ class TestStorageDataLoss(ClusterTestBase):
cluster_state_controller.cluster, cluster_state_controller.cluster,
) )
with allure.step(f"Put couple objects to container on node {node_under_test}"): with reporter.step(f"Put couple objects to container on node {node_under_test}"):
storage_objects: list[StorageObjectInfo] = [] storage_objects: list[StorageObjectInfo] = []
for _ in range(5): for _ in range(5):
storage_object = container.generate_object( storage_object = container.generate_object(
@ -566,19 +567,19 @@ class TestStorageDataLoss(ClusterTestBase):
) )
storage_objects.append(storage_object) storage_objects.append(storage_object)
with allure.step("Take shards snapshot"): with reporter.step("Take shards snapshot"):
shards_watcher.take_shards_snapshot() shards_watcher.take_shards_snapshot()
with allure.step(f"Stop storage service on node {node_under_test}"): with reporter.step(f"Stop storage service on node {node_under_test}"):
cluster_state_controller.stop_service_of_type(node_under_test, StorageNode) cluster_state_controller.stop_service_of_type(node_under_test, StorageNode)
with allure.step(f"Delete write cache from node {node_under_test}"): with reporter.step(f"Delete write cache from node {node_under_test}"):
node_under_test.storage_node.delete_write_cache() node_under_test.storage_node.delete_write_cache()
with allure.step(f"Start storage service on node {node_under_test}"): with reporter.step(f"Start storage service on node {node_under_test}"):
cluster_state_controller.start_all_stopped_services() cluster_state_controller.start_all_stopped_services()
with allure.step("Objects should be available"): with reporter.step("Objects should be available"):
for storage_object in storage_objects: for storage_object in storage_objects:
get_object( get_object(
storage_object.wallet_file_path, storage_object.wallet_file_path,
@ -588,26 +589,26 @@ class TestStorageDataLoss(ClusterTestBase):
node_under_test.storage_node.get_rpc_endpoint(), node_under_test.storage_node.get_rpc_endpoint(),
) )
with allure.step("No shards should have new errors"): with reporter.step("No shards should have new errors"):
shards_watcher.take_shards_snapshot() shards_watcher.take_shards_snapshot()
shards_with_errors = shards_watcher.get_shards_with_new_errors() shards_with_errors = shards_watcher.get_shards_with_new_errors()
if shards_with_errors: if shards_with_errors:
exception_messages.append(f"Shards have new errors: {shards_with_errors}") exception_messages.append(f"Shards have new errors: {shards_with_errors}")
with allure.step("No shards should have degraded status"): with reporter.step("No shards should have degraded status"):
snapshot = shards_watcher.get_shards_snapshot() snapshot = shards_watcher.get_shards_snapshot()
for shard in snapshot: for shard in snapshot:
status = snapshot[shard]["mode"] status = snapshot[shard]["mode"]
if status != "read-write": if status != "read-write":
exception_messages.append(f"Shard {shard} changed status to {status}") exception_messages.append(f"Shard {shard} changed status to {status}")
with allure.step("No related errors should be in log"): with reporter.step("No related errors should be in log"):
if node_under_test.host.is_message_in_logs( if node_under_test.host.is_message_in_logs(
message_regex=r"\Wno such file or directory\W", since=test_start_time message_regex=r"\Wno such file or directory\W", since=test_start_time
): ):
exception_messages.append(f"Node {node_under_test} have shard errors in logs") exception_messages.append(f"Node {node_under_test} have shard errors in logs")
with allure.step("Pass test if no errors found"): with reporter.step("Pass test if no errors found"):
assert not exception_messages, "\n".join(exception_messages) assert not exception_messages, "\n".join(exception_messages)
@allure.title( @allure.title(
@ -623,7 +624,7 @@ class TestStorageDataLoss(ClusterTestBase):
): ):
# TODO: need to check that s3 gate is connected to localhost (such metric will be supported in 1.3) # TODO: need to check that s3 gate is connected to localhost (such metric will be supported in 1.3)
with allure.step("Stop one node and wait for rebalance connection of s3 gate to storage service"): with reporter.step("Stop one node and wait for rebalance connection of s3 gate to storage service"):
current_node = self.cluster.cluster_nodes[0] current_node = self.cluster.cluster_nodes[0]
cluster_state_controller.stop_service_of_type(current_node, StorageNode) cluster_state_controller.stop_service_of_type(current_node, StorageNode)
# waiting for rebalance connection of s3 gate to storage service # waiting for rebalance connection of s3 gate to storage service
@ -631,7 +632,7 @@ class TestStorageDataLoss(ClusterTestBase):
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
with allure.step("Put object into one bucket"): with reporter.step("Put object into one bucket"):
put_object = s3_client.put_object(bucket, file_path) put_object = s3_client.put_object(bucket, file_path)
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name]) s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
@ -648,7 +649,7 @@ class TestStorageDataLoss(ClusterTestBase):
) )
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Check bucket versioning"): with reporter.step("Check bucket versioning"):
bucket_versioning = s3_client.get_bucket_versioning_status(bucket) bucket_versioning = s3_client.get_bucket_versioning_status(bucket)
assert bucket_versioning == "Enabled", "Bucket should have enabled versioning" assert bucket_versioning == "Enabled", "Bucket should have enabled versioning"
@ -656,59 +657,59 @@ class TestStorageDataLoss(ClusterTestBase):
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
object_versions = [] object_versions = []
with allure.step("Put object into one bucket"): with reporter.step("Put object into one bucket"):
put_object = s3_client.put_object(bucket, file_path) put_object = s3_client.put_object(bucket, file_path)
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name]) s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
object_versions.append(put_object) object_versions.append(put_object)
node_to_check = self.cluster.storage_nodes[0] node_to_check = self.cluster.storage_nodes[0]
piloramas_list_before_removing = {} piloramas_list_before_removing = {}
with allure.step("Get list of all pilorama.db"): with reporter.step("Get list of all pilorama.db"):
piloramas_list_before_removing = self.get_piloramas_list(node_to_check) piloramas_list_before_removing = self.get_piloramas_list(node_to_check)
with allure.step("Stop all storage nodes"): with reporter.step("Stop all storage nodes"):
cluster_state_controller.stop_services_of_type(StorageNode) cluster_state_controller.stop_services_of_type(StorageNode)
with allure.step("Delete pilorama.db from one node"): with reporter.step("Delete pilorama.db from one node"):
node_to_check.delete_pilorama() node_to_check.delete_pilorama()
with allure.step("Start all storage nodes"): with reporter.step("Start all storage nodes"):
cluster_state_controller.start_all_stopped_services() cluster_state_controller.start_all_stopped_services()
with allure.step("Tick epoch to trigger sync and then wait for 1 minute"): with reporter.step("Tick epoch to trigger sync and then wait for 1 minute"):
self.tick_epochs(1) self.tick_epochs(1)
sleep(120) sleep(120)
piloramas_list_afrer_removing = {} piloramas_list_afrer_removing = {}
with allure.step("Get list of all pilorama.db after sync"): with reporter.step("Get list of all pilorama.db after sync"):
piloramas_list_afrer_removing = self.get_piloramas_list(node_to_check) piloramas_list_afrer_removing = self.get_piloramas_list(node_to_check)
assert piloramas_list_afrer_removing == piloramas_list_before_removing, "List of pilorama.db is different" assert piloramas_list_afrer_removing == piloramas_list_before_removing, "List of pilorama.db is different"
with allure.step("Check bucket versioning"): with reporter.step("Check bucket versioning"):
bucket_versioning = s3_client.get_bucket_versioning_status(bucket) bucket_versioning = s3_client.get_bucket_versioning_status(bucket)
assert bucket_versioning == "Enabled", "Bucket should have enabled versioning" assert bucket_versioning == "Enabled", "Bucket should have enabled versioning"
with allure.step("Check list objects"): with reporter.step("Check list objects"):
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
assert objects_list, f"Expected not empty bucket" assert objects_list, f"Expected not empty bucket"
with allure.step("Delete the object from the bucket"): with reporter.step("Delete the object from the bucket"):
delete_object = s3_client.delete_object(bucket, file_name) delete_object = s3_client.delete_object(bucket, file_name)
assert "DeleteMarker" in delete_object.keys(), "Delete markers not found" assert "DeleteMarker" in delete_object.keys(), "Delete markers not found"
with allure.step("Check list objects"): with reporter.step("Check list objects"):
objects_list = s3_client.list_objects_versions(bucket) objects_list = s3_client.list_objects_versions(bucket)
assert objects_list, f"Expected not empty bucket" assert objects_list, f"Expected not empty bucket"
object_versions.append(delete_object["VersionId"]) object_versions.append(delete_object["VersionId"])
# and now delete all versions of object (including Delete Markers) # and now delete all versions of object (including Delete Markers)
with allure.step("Delete all versions of the object from the bucket"): with reporter.step("Delete all versions of the object from the bucket"):
for version in object_versions: for version in object_versions:
delete_object = s3_client.delete_object(bucket, file_name, version_id=version) delete_object = s3_client.delete_object(bucket, file_name, version_id=version)
with allure.step("Check list objects"): with reporter.step("Check list objects"):
objects_list = s3_client.list_objects_versions(bucket) objects_list = s3_client.list_objects_versions(bucket)
assert not objects_list, f"Expected empty bucket" assert not objects_list, f"Expected empty bucket"
with allure.step("Delete bucket"): with reporter.step("Delete bucket"):
s3_client.delete_bucket(bucket) s3_client.delete_bucket(bucket)

View file

@ -6,6 +6,7 @@ from typing import Optional, Tuple
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.cli import FrostfsCli from frostfs_testlib.cli import FrostfsCli
from frostfs_testlib.cli.netmap_parser import NetmapParser from frostfs_testlib.cli.netmap_parser import NetmapParser
from frostfs_testlib.resources.cli import FROSTFS_CLI_EXEC from frostfs_testlib.resources.cli import FROSTFS_CLI_EXEC
@ -86,7 +87,7 @@ class TestNodeManagement(ClusterTestBase):
node_shard_list(node) node_shard_list(node)
@allure.step("Tick epoch with retries") @reporter.step("Tick epoch with retries")
def tick_epoch_with_retries(self, attempts: int = 3, timeout: int = 3, wait_block: int = None): def tick_epoch_with_retries(self, attempts: int = 3, timeout: int = 3, wait_block: int = None):
for attempt in range(attempts): for attempt in range(attempts):
try: try:
@ -108,19 +109,19 @@ class TestNodeManagement(ClusterTestBase):
yield yield
self.return_nodes() self.return_nodes()
@allure.step("Return node to cluster") @reporter.step("Return node to cluster")
def return_nodes(self, alive_node: Optional[StorageNode] = None) -> None: def return_nodes(self, alive_node: Optional[StorageNode] = None) -> None:
for node in list(check_nodes): for node in list(check_nodes):
with allure.step(f"Start node {node}"): with reporter.step(f"Start node {node}"):
node.start_service() node.start_service()
with allure.step(f"Waiting status ready for node {node}"): with reporter.step(f"Waiting status ready for node {node}"):
wait_for_node_to_be_ready(node) wait_for_node_to_be_ready(node)
# We need to wait for node to establish notifications from morph-chain # We need to wait for node to establish notifications from morph-chain
# Otherwise it will hang up when we will try to set status # Otherwise it will hang up when we will try to set status
sleep(datetime_utils.parse_time(MORPH_BLOCK_TIME)) sleep(datetime_utils.parse_time(MORPH_BLOCK_TIME))
with allure.step(f"Move node {node} to online state"): with reporter.step(f"Move node {node} to online state"):
storage_node_set_status(node, status="online", retries=2) storage_node_set_status(node, status="online", retries=2)
check_nodes.remove(node) check_nodes.remove(node)
@ -175,7 +176,7 @@ class TestNodeManagement(ClusterTestBase):
self.return_nodes(alive_node) self.return_nodes(alive_node)
with allure.step("Check data could be replicated to new node"): with reporter.step("Check data could be replicated to new node"):
random_node = random.choice(list(set(storage_nodes) - {random_node, alive_node})) random_node = random.choice(list(set(storage_nodes) - {random_node, alive_node}))
# Add node to recovery list before messing with it # Add node to recovery list before messing with it
check_nodes.append(random_node) check_nodes.append(random_node)
@ -191,7 +192,7 @@ class TestNodeManagement(ClusterTestBase):
include_node_to_network_map(random_node, alive_node, shell=self.shell, cluster=self.cluster) include_node_to_network_map(random_node, alive_node, shell=self.shell, cluster=self.cluster)
wait_object_replication(cid, oid, 3, shell=self.shell, nodes=storage_nodes) wait_object_replication(cid, oid, 3, shell=self.shell, nodes=storage_nodes)
with allure.step("Check container could be created with new node"): with reporter.step("Check container could be created with new node"):
cid = create_container( cid = create_container(
wallet, wallet,
rule=placement_rule_4, rule=placement_rule_4,
@ -233,7 +234,7 @@ class TestNodeManagement(ClusterTestBase):
random_node = random.choice(nodes_with_object) random_node = random.choice(nodes_with_object)
for oid in (oid_simple, oid_complex): for oid in (oid_simple, oid_complex):
with allure.step(f"Drop object {oid}"): with reporter.step(f"Drop object {oid}"):
get_object_from_random_node(wallet, cid, oid, shell=self.shell, cluster=self.cluster) get_object_from_random_node(wallet, cid, oid, shell=self.shell, cluster=self.cluster)
head_object(wallet, cid, oid, shell=self.shell, endpoint=endpoint) head_object(wallet, cid, oid, shell=self.shell, endpoint=endpoint)
drop_object(random_node, cid, oid) drop_object(random_node, cid, oid)
@ -302,10 +303,10 @@ class TestNodeManagement(ClusterTestBase):
shell=self.shell, shell=self.shell,
endpoint=random_node.get_rpc_endpoint(), endpoint=random_node.get_rpc_endpoint(),
) )
with allure.step("Stop the random node"): with reporter.step("Stop the random node"):
check_nodes.append(random_node) check_nodes.append(random_node)
random_node.stop_service() random_node.stop_service()
with allure.step("Try to put an object and expect success"): with reporter.step("Try to put an object and expect success"):
put_object( put_object(
wallet, wallet,
source_file_path, source_file_path,
@ -315,7 +316,7 @@ class TestNodeManagement(ClusterTestBase):
) )
self.return_nodes(alive_node) self.return_nodes(alive_node)
@allure.step("Wait for object to be dropped") @reporter.step("Wait for object to be dropped")
def wait_for_obj_dropped(self, wallet: str, cid: str, oid: str, endpoint: str, checker) -> None: def wait_for_obj_dropped(self, wallet: str, cid: str, oid: str, endpoint: str, checker) -> None:
for _ in range(3): for _ in range(3):
try: try:
@ -370,7 +371,7 @@ class TestMaintenanceMode(ClusterTestBase):
put_object: {"path": file_path}, put_object: {"path": file_path},
} }
for index, operation, kw in enumerate(operations.items()): for index, operation, kw in enumerate(operations.items()):
with allure.step(f"Run {operation.__name__} object, waiting response - {matchs[index]}"): with reporter.step(f"Run {operation.__name__} object, waiting response - {matchs[index]}"):
default_kw.update(kw) default_kw.update(kw)
if operation == search_object and "Found" in matchs[index]: if operation == search_object and "Found" in matchs[index]:
operation(**default_kw) operation(**default_kw)
@ -387,7 +388,7 @@ class TestMaintenanceMode(ClusterTestBase):
cluster_state_controller: ClusterStateController, cluster_state_controller: ClusterStateController,
restore_online_mode_node: None, restore_online_mode_node: None,
): ):
with allure.step("Create container and create\put object"): with reporter.step("Create container and create\put object"):
cid = create_container( cid = create_container(
wallet=default_wallet, wallet=default_wallet,
shell=self.shell, shell=self.shell,
@ -410,7 +411,7 @@ class TestMaintenanceMode(ClusterTestBase):
shell=self.shell, shell=self.shell,
endpoint=self.change_node.storage_node.get_rpc_endpoint(), endpoint=self.change_node.storage_node.get_rpc_endpoint(),
) )
with allure.step("Enable MaintenanceModeAllowed:"): with reporter.step("Enable MaintenanceModeAllowed:"):
cluster_state_controller.set_mode_node( cluster_state_controller.set_mode_node(
cluster_node=self.change_node, wallet=default_wallet, status="maintenance" cluster_node=self.change_node, wallet=default_wallet, status="maintenance"
) )
@ -425,7 +426,7 @@ class TestMaintenanceMode(ClusterTestBase):
"node is under maintenance", "node is under maintenance",
], ],
} }
with allure.step("Run basic operations"): with reporter.step("Run basic operations"):
for cluster_node, matchs in node_and_match.items(): for cluster_node, matchs in node_and_match.items():
self.basic_operations( self.basic_operations(
wallet=default_wallet, wallet=default_wallet,
@ -450,20 +451,20 @@ class TestMaintenanceMode(ClusterTestBase):
self.change_node = node_under_test self.change_node = node_under_test
cluster_nodes = list(set(self.cluster.cluster_nodes) - {node_under_test}) cluster_nodes = list(set(self.cluster.cluster_nodes) - {node_under_test})
with allure.step("Tick epoch"): with reporter.step("Tick epoch"):
self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with allure.step("Set node mode to offline"): with reporter.step("Set node mode to offline"):
cluster_state_controller.set_mode_node( cluster_state_controller.set_mode_node(
cluster_node=node_under_test, cluster_node=node_under_test,
wallet=default_wallet, wallet=default_wallet,
status=ModeNode.OFFLINE.value, status=ModeNode.OFFLINE.value,
) )
with allure.step("Tick epoch to update the network map"): with reporter.step("Tick epoch to update the network map"):
self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with ((allure.step("Check node mode = offline, after update the network map"))): with reporter.step("Check node mode = offline, after update the network map"):
netmap = frostfs_cli.netmap.snapshot( netmap = frostfs_cli.netmap.snapshot(
rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet
).stdout ).stdout
@ -472,14 +473,14 @@ class TestMaintenanceMode(ClusterTestBase):
netmap_node.node for netmap_node in netmap netmap_node.node for netmap_node in netmap
], f"Node {node_under_test.host_ip} not in state offline" ], f"Node {node_under_test.host_ip} not in state offline"
with allure.step("Restart storage service"): with reporter.step("Restart storage service"):
cluster_state_controller.stop_storage_service(node_under_test) cluster_state_controller.stop_storage_service(node_under_test)
cluster_state_controller.start_storage_service(node_under_test) cluster_state_controller.start_storage_service(node_under_test)
with allure.step("Tick epoch after restart storage service and set mode to offline"): with reporter.step("Tick epoch after restart storage service and set mode to offline"):
self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with (allure.step("Check node mode = online, after restart storage service")): with (reporter.step("Check node mode = online, after restart storage service")):
netmap = frostfs_cli.netmap.snapshot( netmap = frostfs_cli.netmap.snapshot(
rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet
).stdout ).stdout
@ -488,21 +489,21 @@ class TestMaintenanceMode(ClusterTestBase):
f"Node actual state - {node_state}, " f"expect - {ModeNode.ONLINE.value}" f"Node actual state - {node_state}, " f"expect - {ModeNode.ONLINE.value}"
) )
with allure.step("Tick epoch"): with reporter.step("Tick epoch"):
self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with allure.step("Set node mode to maintenance"): with reporter.step("Set node mode to maintenance"):
cluster_state_controller.set_mode_node( cluster_state_controller.set_mode_node(
cluster_node=node_under_test, wallet=default_wallet, status=ModeNode.MAINTENANCE.value cluster_node=node_under_test, wallet=default_wallet, status=ModeNode.MAINTENANCE.value
) )
with allure.step("Restart storage service"): with reporter.step("Restart storage service"):
cluster_state_controller.stop_storage_service(node_under_test) cluster_state_controller.stop_storage_service(node_under_test)
cluster_state_controller.start_storage_service(node_under_test) cluster_state_controller.start_storage_service(node_under_test)
with allure.step("Tick epoch after restart storage service"): with reporter.step("Tick epoch after restart storage service"):
self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with allure.step("Check node mode = maintenance, after restart storage service and tick epoch"): with reporter.step("Check node mode = maintenance, after restart storage service and tick epoch"):
netmap = frostfs_cli.netmap.snapshot( netmap = frostfs_cli.netmap.snapshot(
rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet
).stdout ).stdout
@ -511,10 +512,10 @@ class TestMaintenanceMode(ClusterTestBase):
f"Node actual state - {node_state}, " f"expect - {ModeNode.MAINTENANCE.value}" f"Node actual state - {node_state}, " f"expect - {ModeNode.MAINTENANCE.value}"
) )
with allure.step("Tick epoch"): with reporter.step("Tick epoch"):
self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with allure.step("Set node mode to offline"): with reporter.step("Set node mode to offline"):
netmap = frostfs_cli.netmap.snapshot( netmap = frostfs_cli.netmap.snapshot(
rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet
).stdout ).stdout
@ -523,16 +524,16 @@ class TestMaintenanceMode(ClusterTestBase):
f"Node actual state - {node_state}, " f"expect - {ModeNode.OFFLINE.value}" f"Node actual state - {node_state}, " f"expect - {ModeNode.OFFLINE.value}"
) )
with allure.step("Stop storage service"): with reporter.step("Stop storage service"):
cluster_state_controller.stop_storage_service(node_under_test) cluster_state_controller.stop_storage_service(node_under_test)
with allure.step("Tick epoch"): with reporter.step("Tick epoch"):
self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with allure.step("Start storage service"): with reporter.step("Start storage service"):
cluster_state_controller.start_storage_service(node_under_test) cluster_state_controller.start_storage_service(node_under_test)
with allure.step("Check node mode = offline, after tick epoch and start storage service"): with reporter.step("Check node mode = offline, after tick epoch and start storage service"):
netmap = frostfs_cli.netmap.snapshot( netmap = frostfs_cli.netmap.snapshot(
rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet
).stdout ).stdout
@ -541,10 +542,10 @@ class TestMaintenanceMode(ClusterTestBase):
f"Node actual state - {node_state}, " f"expect - {ModeNode.OFFLINE.value}" f"Node actual state - {node_state}, " f"expect - {ModeNode.OFFLINE.value}"
) )
with allure.step("Tick epoch"): with reporter.step("Tick epoch"):
self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with allure.step("Check node mode = online, after start storage service and tick epoch"): with reporter.step("Check node mode = online, after start storage service and tick epoch"):
netmap = frostfs_cli.netmap.snapshot( netmap = frostfs_cli.netmap.snapshot(
rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet
).stdout ).stdout
@ -553,24 +554,24 @@ class TestMaintenanceMode(ClusterTestBase):
f"Node actual state - {node_state}, " f"expect - {ModeNode.ONLINE.value}" f"Node actual state - {node_state}, " f"expect - {ModeNode.ONLINE.value}"
) )
with allure.step("Tick epoch"): with reporter.step("Tick epoch"):
self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with allure.step("Set node mode to maintenance"): with reporter.step("Set node mode to maintenance"):
cluster_state_controller.set_mode_node( cluster_state_controller.set_mode_node(
cluster_node=node_under_test, wallet=default_wallet, status=ModeNode.MAINTENANCE.value cluster_node=node_under_test, wallet=default_wallet, status=ModeNode.MAINTENANCE.value
) )
with allure.step("Stop storage service"): with reporter.step("Stop storage service"):
cluster_state_controller.stop_storage_service(node_under_test) cluster_state_controller.stop_storage_service(node_under_test)
with allure.step("Tick epoch"): with reporter.step("Tick epoch"):
self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with allure.step("Start storage service"): with reporter.step("Start storage service"):
cluster_state_controller.start_storage_service(node_under_test) cluster_state_controller.start_storage_service(node_under_test)
with allure.step("Check node mode = maintenance"): with reporter.step("Check node mode = maintenance"):
netmap = frostfs_cli.netmap.snapshot( netmap = frostfs_cli.netmap.snapshot(
rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet
).stdout ).stdout
@ -580,10 +581,10 @@ class TestMaintenanceMode(ClusterTestBase):
node_state == ModeNode.MAINTENANCE.value.upper() node_state == ModeNode.MAINTENANCE.value.upper()
), f"Node actual state - {node_state}, expect - {ModeNode.MAINTENANCE.value}" ), f"Node actual state - {node_state}, expect - {ModeNode.MAINTENANCE.value}"
with allure.step("Tick epoch"): with reporter.step("Tick epoch"):
self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2) self.tick_epochs(epochs_to_tick=2, alive_node=cluster_nodes[0].storage_node, wait_block=2)
with allure.step("Check node mode = maintenance"): with reporter.step("Check node mode = maintenance"):
netmap = frostfs_cli.netmap.snapshot( netmap = frostfs_cli.netmap.snapshot(
rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet rpc_endpoint=cluster_nodes[0].storage_node.get_rpc_endpoint(), wallet=default_wallet
).stdout ).stdout
@ -605,24 +606,24 @@ class TestMaintenanceMode(ClusterTestBase):
self.change_node = node_under_test self.change_node = node_under_test
control_endpoint = node_under_test.service(StorageNode).get_control_endpoint() control_endpoint = node_under_test.service(StorageNode).get_control_endpoint()
with allure.step("Set MaintenanceModeAllowed = false"): with reporter.step("Set MaintenanceModeAllowed = false"):
cluster_state_controller.set_maintenance_mode_allowed("false", node_under_test) cluster_state_controller.set_maintenance_mode_allowed("false", node_under_test)
with allure.step("Set status node - maintenance"): with reporter.step("Set status node - maintenance"):
with pytest.raises(RuntimeError, match="maintenance mode is not allowed by the network"): with pytest.raises(RuntimeError, match="maintenance mode is not allowed by the network"):
frostfs_cli_remote.control.set_status(endpoint=control_endpoint, status="maintenance") frostfs_cli_remote.control.set_status(endpoint=control_endpoint, status="maintenance")
with allure.step("Set MaintenanceModeAllowed = true"): with reporter.step("Set MaintenanceModeAllowed = true"):
cluster_state_controller.set_maintenance_mode_allowed("true", node_under_test) cluster_state_controller.set_maintenance_mode_allowed("true", node_under_test)
with allure.step("Set status node - maintenance"): with reporter.step("Set status node - maintenance"):
output = frostfs_cli_remote.control.set_status(endpoint=control_endpoint, status="maintenance") output = frostfs_cli_remote.control.set_status(endpoint=control_endpoint, status="maintenance")
assert "update request successfully sent" in output.stdout, f"Response = {output}" assert "update request successfully sent" in output.stdout, f"Response = {output}"
with allure.step("Tick epoch"): with reporter.step("Tick epoch"):
self.tick_epoch(wait_block=2) self.tick_epoch(wait_block=2)
with allure.step("Check state node = maintenance "): with reporter.step("Check state node = maintenance "):
netmap_node = NetmapParser.snapshot_one_node( netmap_node = NetmapParser.snapshot_one_node(
frostfs_cli.netmap.snapshot( frostfs_cli.netmap.snapshot(
rpc_endpoint=node_under_test.storage_node.get_rpc_endpoint(), wallet=default_wallet rpc_endpoint=node_under_test.storage_node.get_rpc_endpoint(), wallet=default_wallet
@ -633,13 +634,13 @@ class TestMaintenanceMode(ClusterTestBase):
netmap_node.node_status == ModeNode.MAINTENANCE.value.upper() netmap_node.node_status == ModeNode.MAINTENANCE.value.upper()
), f"Node actual state - {netmap_node.node_status}, expect - {ModeNode.MAINTENANCE.value}" ), f"Node actual state - {netmap_node.node_status}, expect - {ModeNode.MAINTENANCE.value}"
with allure.step("Set status node - online "): with reporter.step("Set status node - online "):
frostfs_cli_remote.control.set_status(endpoint=control_endpoint, status="online") frostfs_cli_remote.control.set_status(endpoint=control_endpoint, status="online")
with allure.step("Tick epoch"): with reporter.step("Tick epoch"):
self.tick_epoch() self.tick_epoch()
with allure.step("Check state node: online"): with reporter.step("Check state node: online"):
netmap_node = NetmapParser.snapshot_one_node( netmap_node = NetmapParser.snapshot_one_node(
frostfs_cli.netmap.snapshot( frostfs_cli.netmap.snapshot(
rpc_endpoint=node_under_test.storage_node.get_rpc_endpoint(), wallet=default_wallet rpc_endpoint=node_under_test.storage_node.get_rpc_endpoint(), wallet=default_wallet

View file

@ -4,6 +4,7 @@ import sys
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.error_patterns import ( from frostfs_testlib.resources.error_patterns import (
INVALID_LENGTH_SPECIFIER, INVALID_LENGTH_SPECIFIER,
INVALID_OFFSET_SPECIFIER, INVALID_OFFSET_SPECIFIER,
@ -102,7 +103,7 @@ def storage_objects(
storage_objects = [] storage_objects = []
with allure.step("Put objects"): with reporter.step("Put objects"):
# We need to upload objects multiple times with different attributes # We need to upload objects multiple times with different attributes
for attributes in OBJECT_ATTRIBUTES: for attributes in OBJECT_ATTRIBUTES:
storage_object_id = put_object_to_random_node( storage_object_id = put_object_to_random_node(
@ -142,7 +143,7 @@ class TestObjectApi(ClusterTestBase):
Validate object storage policy Validate object storage policy
""" """
with allure.step("Validate storage policy for objects"): with reporter.step("Validate storage policy for objects"):
for storage_object in storage_objects: for storage_object in storage_objects:
if storage_object.size == simple_object_size.value: if storage_object.size == simple_object_size.value:
copies = get_simple_object_copies( copies = get_simple_object_copies(
@ -168,7 +169,7 @@ class TestObjectApi(ClusterTestBase):
Validate get object native API Validate get object native API
""" """
with allure.step("Get objects and compare hashes"): with reporter.step("Get objects and compare hashes"):
for storage_object in storage_objects: for storage_object in storage_objects:
file_path = get_object_from_random_node( file_path = get_object_from_random_node(
storage_object.wallet_file_path, storage_object.wallet_file_path,
@ -189,7 +190,7 @@ class TestObjectApi(ClusterTestBase):
storage_object_1 = storage_objects[0] storage_object_1 = storage_objects[0]
storage_object_2 = storage_objects[1] storage_object_2 = storage_objects[1]
with allure.step("Head object and validate"): with reporter.step("Head object and validate"):
head_object( head_object(
storage_object_1.wallet_file_path, storage_object_1.wallet_file_path,
storage_object_1.cid, storage_object_1.cid,
@ -222,7 +223,7 @@ class TestObjectApi(ClusterTestBase):
(COMMON_ATTRIBUTE, oids[1:3]), (COMMON_ATTRIBUTE, oids[1:3]),
] ]
with allure.step("Search objects"): with reporter.step("Search objects"):
# Search with no attributes # Search with no attributes
result = search_object( result = search_object(
wallet, wallet,
@ -256,7 +257,7 @@ class TestObjectApi(ClusterTestBase):
wallet = default_wallet wallet = default_wallet
cid = create_container(wallet, self.shell, self.cluster.default_rpc_endpoint) cid = create_container(wallet, self.shell, self.cluster.default_rpc_endpoint)
with allure.step("Upload file"): with reporter.step("Upload file"):
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)
@ -269,20 +270,20 @@ class TestObjectApi(ClusterTestBase):
file_hash=file_hash, file_hash=file_hash,
) )
with allure.step("Search object"): with reporter.step("Search object"):
# Root Search object should return root object oid # Root Search object should return root object oid
result = search_object(wallet, cid, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint, root=True) result = search_object(wallet, cid, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint, root=True)
assert result == [storage_object.oid] assert result == [storage_object.oid]
with allure.step("Delete file"): with reporter.step("Delete file"):
delete_objects([storage_object], self.shell, self.cluster) delete_objects([storage_object], self.shell, self.cluster)
with allure.step("Search deleted object with --root"): with reporter.step("Search deleted object with --root"):
# Root Search object should return nothing # Root Search object should return nothing
result = search_object(wallet, cid, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint, root=True) result = search_object(wallet, cid, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint, root=True)
assert len(result) == 0 assert len(result) == 0
with allure.step("Search deleted object with --phy should return only tombstones"): with reporter.step("Search deleted object with --phy should return only tombstones"):
# Physical Search object should return only tombstones # Physical Search object should return only tombstones
result = search_object(wallet, cid, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint, phy=True) result = search_object(wallet, cid, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint, phy=True)
assert storage_object.tombstone in result, "Search result should contain tombstone of removed object" assert storage_object.tombstone in result, "Search result should contain tombstone of removed object"
@ -317,7 +318,7 @@ class TestObjectApi(ClusterTestBase):
for range_start, range_len in file_ranges_to_test: for range_start, range_len in file_ranges_to_test:
range_cut = f"{range_start}:{range_len}" range_cut = f"{range_start}:{range_len}"
with allure.step(f"Get range hash ({range_cut})"): with reporter.step(f"Get range hash ({range_cut})"):
for oid in oids: for oid in oids:
range_hash = get_range_hash( range_hash = get_range_hash(
wallet, wallet,
@ -348,7 +349,7 @@ class TestObjectApi(ClusterTestBase):
for range_start, range_len in file_ranges_to_test: for range_start, range_len in file_ranges_to_test:
range_cut = f"{range_start}:{range_len}" range_cut = f"{range_start}:{range_len}"
with allure.step(f"Get range ({range_cut})"): with reporter.step(f"Get range ({range_cut})"):
for oid in oids: for oid in oids:
_, range_content = get_range( _, range_content = get_range(
wallet, wallet,
@ -399,7 +400,7 @@ class TestObjectApi(ClusterTestBase):
for range_start, range_len, expected_error in file_ranges_to_test: for range_start, range_len, expected_error in file_ranges_to_test:
range_cut = f"{range_start}:{range_len}" range_cut = f"{range_start}:{range_len}"
expected_error = expected_error.format(range=range_cut) if "{range}" in expected_error else expected_error expected_error = expected_error.format(range=range_cut) if "{range}" in expected_error else expected_error
with allure.step(f"Get range ({range_cut})"): with reporter.step(f"Get range ({range_cut})"):
for oid in oids: for oid in oids:
with pytest.raises(Exception, match=expected_error): with pytest.raises(Exception, match=expected_error):
get_range( get_range(
@ -446,7 +447,7 @@ class TestObjectApi(ClusterTestBase):
for range_start, range_len, expected_error in file_ranges_to_test: for range_start, range_len, expected_error in file_ranges_to_test:
range_cut = f"{range_start}:{range_len}" range_cut = f"{range_start}:{range_len}"
expected_error = expected_error.format(range=range_cut) if "{range}" in expected_error else expected_error expected_error = expected_error.format(range=range_cut) if "{range}" in expected_error else expected_error
with allure.step(f"Get range hash ({range_cut})"): with reporter.step(f"Get range hash ({range_cut})"):
for oid in oids: for oid in oids:
with pytest.raises(Exception, match=expected_error): with pytest.raises(Exception, match=expected_error):
get_range_hash( get_range_hash(

View file

@ -1,5 +1,6 @@
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.wellknown_acl import EACL_PUBLIC_READ_WRITE from frostfs_testlib.resources.wellknown_acl import EACL_PUBLIC_READ_WRITE
from frostfs_testlib.shell import Shell from frostfs_testlib.shell import Shell
from frostfs_testlib.steps.acl import form_bearertoken_file from frostfs_testlib.steps.acl import form_bearertoken_file
@ -94,7 +95,7 @@ class TestObjectApiWithBearerToken(ClusterTestBase):
bearer_token_file_all_allow: str, bearer_token_file_all_allow: str,
): ):
s3_gate_wallet = self.cluster.s3_gates[0] s3_gate_wallet = self.cluster.s3_gates[0]
with allure.step("Try to delete each object from first storage node"): with reporter.step("Try to delete each object from first storage node"):
for storage_object in storage_objects: for storage_object in storage_objects:
with expect_not_raises(): with expect_not_raises():
delete_object( delete_object(
@ -120,13 +121,13 @@ class TestObjectApiWithBearerToken(ClusterTestBase):
bearer_token_file_all_allow: str, bearer_token_file_all_allow: str,
): ):
s3_gate_wallet = self.cluster.s3_gates[0] s3_gate_wallet = self.cluster.s3_gates[0]
with allure.step("Put one object to container"): with reporter.step("Put one object to container"):
epoch = self.get_epoch() epoch = self.get_epoch()
storage_object = user_container.generate_object( storage_object = user_container.generate_object(
object_size.value, epoch + 3, bearer_token=bearer_token_file_all_allow object_size.value, epoch + 3, bearer_token=bearer_token_file_all_allow
) )
with allure.step("Try to fetch object from each storage node"): with reporter.step("Try to fetch object from each storage node"):
for node in self.cluster.storage_nodes: for node in self.cluster.storage_nodes:
with expect_not_raises(): with expect_not_raises():
get_object( get_object(

View file

@ -2,13 +2,10 @@ import logging
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.error_patterns import OBJECT_NOT_FOUND from frostfs_testlib.resources.error_patterns import OBJECT_NOT_FOUND
from frostfs_testlib.steps.cli.container import create_container from frostfs_testlib.steps.cli.container import create_container
from frostfs_testlib.steps.cli.object import ( from frostfs_testlib.steps.cli.object import get_object_from_random_node, head_object, put_object_to_random_node
get_object_from_random_node,
head_object,
put_object_to_random_node,
)
from frostfs_testlib.steps.epoch import get_epoch from frostfs_testlib.steps.epoch import get_epoch
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
@ -36,31 +33,29 @@ class TestObjectApiLifetime(ClusterTestBase):
file_hash = get_file_hash(file_path) file_hash = get_file_hash(file_path)
epoch = get_epoch(self.shell, self.cluster) epoch = get_epoch(self.shell, self.cluster)
oid = put_object_to_random_node( oid = put_object_to_random_node(wallet, file_path, cid, self.shell, self.cluster, expire_at=epoch + 1)
wallet, file_path, cid, self.shell, self.cluster, expire_at=epoch + 1
)
got_file = get_object_from_random_node(wallet, cid, oid, self.shell, self.cluster) got_file = get_object_from_random_node(wallet, cid, oid, self.shell, self.cluster)
assert get_file_hash(got_file) == file_hash assert get_file_hash(got_file) == file_hash
with allure.step("Tick two epochs"): with reporter.step("Tick two epochs"):
for _ in range(2): for _ in range(2):
self.tick_epoch() self.tick_epoch()
# Wait for GC, because object with expiration is counted as alive until GC removes it # Wait for GC, because object with expiration is counted as alive until GC removes it
wait_for_gc_pass_on_storage_nodes() wait_for_gc_pass_on_storage_nodes()
with allure.step("Check object deleted because it expires on epoch"): with reporter.step("Check object deleted because it expires on epoch"):
with pytest.raises(Exception, match=OBJECT_NOT_FOUND): with pytest.raises(Exception, match=OBJECT_NOT_FOUND):
head_object(wallet, cid, oid, self.shell, self.cluster.default_rpc_endpoint) head_object(wallet, cid, oid, self.shell, self.cluster.default_rpc_endpoint)
with pytest.raises(Exception, match=OBJECT_NOT_FOUND): with pytest.raises(Exception, match=OBJECT_NOT_FOUND):
get_object_from_random_node(wallet, cid, oid, self.shell, self.cluster) get_object_from_random_node(wallet, cid, oid, self.shell, self.cluster)
with allure.step("Tick additional epoch"): with reporter.step("Tick additional epoch"):
self.tick_epoch() self.tick_epoch()
wait_for_gc_pass_on_storage_nodes() wait_for_gc_pass_on_storage_nodes()
with allure.step("Check object deleted because it expires on previous epoch"): with reporter.step("Check object deleted because it expires on previous epoch"):
with pytest.raises(Exception, match=OBJECT_NOT_FOUND): with pytest.raises(Exception, match=OBJECT_NOT_FOUND):
head_object(wallet, cid, oid, self.shell, self.cluster.default_rpc_endpoint) head_object(wallet, cid, oid, self.shell, self.cluster.default_rpc_endpoint)
with pytest.raises(Exception, match=OBJECT_NOT_FOUND): with pytest.raises(Exception, match=OBJECT_NOT_FOUND):

View file

@ -3,6 +3,7 @@ import re
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.common import STORAGE_GC_TIME from frostfs_testlib.resources.common import STORAGE_GC_TIME
from frostfs_testlib.resources.error_patterns import ( from frostfs_testlib.resources.error_patterns import (
LIFETIME_REQUIRED, LIFETIME_REQUIRED,
@ -41,7 +42,7 @@ FIXTURE_OBJECT_LIFETIME = 10
scope="module", scope="module",
) )
def user_wallet(wallet_factory: WalletFactory): def user_wallet(wallet_factory: WalletFactory):
with allure.step("Create user wallet with container"): with reporter.step("Create user wallet with container"):
wallet_file = wallet_factory.create_wallet() wallet_file = wallet_factory.create_wallet()
return wallet_file return wallet_file
@ -66,7 +67,7 @@ def locked_storage_object(
""" """
Intention of this fixture is to provide storage object which is NOT expected to be deleted during test act phase Intention of this fixture is to provide storage object which is NOT expected to be deleted during test act phase
""" """
with allure.step("Creating locked object"): with reporter.step("Creating locked object"):
current_epoch = ensure_fresh_epoch(client_shell, cluster) current_epoch = ensure_fresh_epoch(client_shell, cluster)
expiration_epoch = current_epoch + FIXTURE_LOCK_LIFETIME expiration_epoch = current_epoch + FIXTURE_LOCK_LIFETIME
@ -87,12 +88,12 @@ def locked_storage_object(
yield storage_object yield storage_object
with allure.step("Delete created locked object"): with reporter.step("Delete created locked object"):
current_epoch = get_epoch(client_shell, cluster) current_epoch = get_epoch(client_shell, cluster)
epoch_diff = expiration_epoch - current_epoch + 1 epoch_diff = expiration_epoch - current_epoch + 1
if epoch_diff > 0: if epoch_diff > 0:
with allure.step(f"Tick {epoch_diff} epochs"): with reporter.step(f"Tick {epoch_diff} epochs"):
for _ in range(epoch_diff): for _ in range(epoch_diff):
tick_epoch(client_shell, cluster) tick_epoch(client_shell, cluster)
try: try:
@ -142,7 +143,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
Intention of this fixture is to provide new storage object for tests which may delete or corrupt the object or it's complementary objects Intention of this fixture is to provide new storage object for tests which may delete or corrupt the object or it's complementary objects
So we need a new one each time we ask for it So we need a new one each time we ask for it
""" """
with allure.step("Creating locked object"): with reporter.step("Creating locked object"):
current_epoch = self.get_epoch() current_epoch = self.get_epoch()
storage_object = user_container.generate_object( storage_object = user_container.generate_object(
@ -278,7 +279,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
current_epoch = self.ensure_fresh_epoch() current_epoch = self.ensure_fresh_epoch()
storage_object = user_container.generate_object(object_size.value, expire_at=current_epoch + 1) storage_object = user_container.generate_object(object_size.value, expire_at=current_epoch + 1)
with allure.step("Lock object for couple epochs"): with reporter.step("Lock object for couple epochs"):
lock_object( lock_object(
storage_object.wallet_file_path, storage_object.wallet_file_path,
storage_object.cid, storage_object.cid,
@ -296,7 +297,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
expire_at=current_epoch + 2, expire_at=current_epoch + 2,
) )
with allure.step("Check object is not deleted at expiration time"): with reporter.step("Check object is not deleted at expiration time"):
self.tick_epochs(2) self.tick_epochs(2)
# Must wait to ensure object is not deleted # Must wait to ensure object is not deleted
wait_for_gc_pass_on_storage_nodes() wait_for_gc_pass_on_storage_nodes()
@ -309,7 +310,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
self.cluster.default_rpc_endpoint, self.cluster.default_rpc_endpoint,
) )
with allure.step("Wait for object to be deleted after third epoch"): with reporter.step("Wait for object to be deleted after third epoch"):
self.tick_epoch() self.tick_epoch()
check_object_not_found( check_object_not_found(
storage_object.wallet_file_path, storage_object.wallet_file_path,
@ -332,7 +333,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
current_epoch = ensure_fresh_epoch(self.shell, self.cluster) current_epoch = ensure_fresh_epoch(self.shell, self.cluster)
storage_objects: list[StorageObjectInfo] = [] storage_objects: list[StorageObjectInfo] = []
with allure.step("Generate three objects"): with reporter.step("Generate three objects"):
for _ in range(3): for _ in range(3):
storage_objects.append(user_container.generate_object(object_size.value, expire_at=current_epoch + 5)) storage_objects.append(user_container.generate_object(object_size.value, expire_at=current_epoch + 5))
@ -346,7 +347,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
) )
for storage_object in storage_objects: for storage_object in storage_objects:
with allure.step(f"Try to delete object {storage_object.oid}"): with reporter.step(f"Try to delete object {storage_object.oid}"):
with pytest.raises(Exception, match=OBJECT_IS_LOCKED): with pytest.raises(Exception, match=OBJECT_IS_LOCKED):
delete_object( delete_object(
storage_object.wallet_file_path, storage_object.wallet_file_path,
@ -356,7 +357,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
self.cluster.default_rpc_endpoint, self.cluster.default_rpc_endpoint,
) )
with allure.step("Tick two epochs"): with reporter.step("Tick two epochs"):
self.tick_epoch() self.tick_epoch()
self.tick_epoch() self.tick_epoch()
@ -477,7 +478,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
chunk_object_ids = get_storage_object_chunks(locked_storage_object, self.shell, self.cluster) chunk_object_ids = get_storage_object_chunks(locked_storage_object, self.shell, self.cluster)
for chunk_object_id in chunk_object_ids: for chunk_object_id in chunk_object_ids:
with allure.step(f"Try to delete chunk object {chunk_object_id}"): with reporter.step(f"Try to delete chunk object {chunk_object_id}"):
with pytest.raises(Exception, match=OBJECT_IS_LOCKED): with pytest.raises(Exception, match=OBJECT_IS_LOCKED):
delete_object( delete_object(
locked_storage_object.wallet_file_path, locked_storage_object.wallet_file_path,
@ -504,7 +505,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
self.cluster.storage_nodes, self.cluster.storage_nodes,
) )
with allure.step(f"Drop link object with id {link_object_id} from nodes"): with reporter.step(f"Drop link object with id {link_object_id} from nodes"):
nodes_with_object = get_nodes_with_object( nodes_with_object = get_nodes_with_object(
new_locked_storage_object.cid, new_locked_storage_object.cid,
link_object_id, link_object_id,
@ -527,7 +528,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
chunk_objects = get_storage_object_chunks(new_locked_storage_object, self.shell, self.cluster) chunk_objects = get_storage_object_chunks(new_locked_storage_object, self.shell, self.cluster)
for chunk_object_id in chunk_objects: for chunk_object_id in chunk_objects:
with allure.step(f"Drop chunk object with id {chunk_object_id} from nodes"): with reporter.step(f"Drop chunk object with id {chunk_object_id} from nodes"):
nodes_with_object = get_nodes_with_object( nodes_with_object = get_nodes_with_object(
new_locked_storage_object.cid, new_locked_storage_object.cid,
chunk_object_id, chunk_object_id,
@ -575,7 +576,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
self.cluster.storage_nodes, self.cluster.storage_nodes,
is_direct=False, is_direct=False,
) )
with allure.step(f"Try to delete link object {link_object_id}"): with reporter.step(f"Try to delete link object {link_object_id}"):
with pytest.raises(Exception, match=OBJECT_IS_LOCKED): with pytest.raises(Exception, match=OBJECT_IS_LOCKED):
delete_object( delete_object(
locked_storage_object.wallet_file_path, locked_storage_object.wallet_file_path,
@ -594,7 +595,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
current_epoch = self.ensure_fresh_epoch() current_epoch = self.ensure_fresh_epoch()
storage_object = user_container.generate_object(object_size.value, expire_at=current_epoch + 1) storage_object = user_container.generate_object(object_size.value, expire_at=current_epoch + 1)
with allure.step("Apply first lock to object for 3 epochs"): with reporter.step("Apply first lock to object for 3 epochs"):
lock_object_id_0 = lock_object( lock_object_id_0 = lock_object(
storage_object.wallet_file_path, storage_object.wallet_file_path,
storage_object.cid, storage_object.cid,
@ -606,7 +607,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
self.tick_epochs(2) self.tick_epochs(2)
with allure.step("Check first lock is still available"): with reporter.step("Check first lock is still available"):
verify_object_available( verify_object_available(
storage_object.wallet_file_path, storage_object.wallet_file_path,
storage_object.cid, storage_object.cid,
@ -615,7 +616,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
self.cluster.default_rpc_endpoint, self.cluster.default_rpc_endpoint,
) )
with allure.step("Apply second lock to object for 3 more epochs"): with reporter.step("Apply second lock to object for 3 more epochs"):
lock_object_id_1 = lock_object( lock_object_id_1 = lock_object(
storage_object.wallet_file_path, storage_object.wallet_file_path,
storage_object.cid, storage_object.cid,
@ -627,7 +628,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
self.tick_epochs(2) self.tick_epochs(2)
with allure.step("Verify first lock is expired and removed"): with reporter.step("Verify first lock is expired and removed"):
check_object_not_found( check_object_not_found(
storage_object.wallet_file_path, storage_object.wallet_file_path,
storage_object.cid, storage_object.cid,
@ -636,7 +637,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
self.cluster.default_rpc_endpoint, self.cluster.default_rpc_endpoint,
) )
with allure.step("Verify second lock is still available"): with reporter.step("Verify second lock is still available"):
verify_object_available( verify_object_available(
storage_object.wallet_file_path, storage_object.wallet_file_path,
storage_object.cid, storage_object.cid,
@ -645,7 +646,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
self.cluster.default_rpc_endpoint, self.cluster.default_rpc_endpoint,
) )
with allure.step("Apply third lock to object for 3 more epochs"): with reporter.step("Apply third lock to object for 3 more epochs"):
lock_object( lock_object(
storage_object.wallet_file_path, storage_object.wallet_file_path,
storage_object.cid, storage_object.cid,
@ -655,7 +656,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
expire_at=current_epoch + 7, expire_at=current_epoch + 7,
) )
with allure.step("Verify object is deleted after all locks are expired"): with reporter.step("Verify object is deleted after all locks are expired"):
self.tick_epochs(4) self.tick_epochs(4)
check_object_not_found( check_object_not_found(
storage_object.wallet_file_path, storage_object.wallet_file_path,
@ -676,7 +677,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
current_epoch = self.ensure_fresh_epoch() current_epoch = self.ensure_fresh_epoch()
storage_objects: list[StorageObjectInfo] = [] storage_objects: list[StorageObjectInfo] = []
with allure.step("Generate two objects"): with reporter.step("Generate two objects"):
for epoch_i in range(2): for epoch_i in range(2):
storage_objects.append( storage_objects.append(
user_container.generate_object(object_size.value, expire_at=current_epoch + epoch_i + 3) user_container.generate_object(object_size.value, expire_at=current_epoch + epoch_i + 3)
@ -684,7 +685,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
self.tick_epoch() self.tick_epoch()
with allure.step("Lock objects for 4 epochs"): with reporter.step("Lock objects for 4 epochs"):
lock_object( lock_object(
storage_objects[0].wallet_file_path, storage_objects[0].wallet_file_path,
storage_objects[0].cid, storage_objects[0].cid,
@ -694,10 +695,10 @@ class TestObjectLockWithGrpc(ClusterTestBase):
expire_at=current_epoch + 4, expire_at=current_epoch + 4,
) )
with allure.step("Verify objects are available during next three epochs"): with reporter.step("Verify objects are available during next three epochs"):
for epoch_i in range(3): for epoch_i in range(3):
self.tick_epoch() self.tick_epoch()
with allure.step(f"Check objects at epoch {current_epoch + epoch_i + 2}"): with reporter.step(f"Check objects at epoch {current_epoch + epoch_i + 2}"):
for storage_object in storage_objects: for storage_object in storage_objects:
verify_object_available( verify_object_available(
storage_object.wallet_file_path, storage_object.wallet_file_path,
@ -707,7 +708,7 @@ class TestObjectLockWithGrpc(ClusterTestBase):
self.cluster.default_rpc_endpoint, self.cluster.default_rpc_endpoint,
) )
with allure.step("Verify objects are deleted after lock was expired"): with reporter.step("Verify objects are deleted after lock was expired"):
self.tick_epoch() self.tick_epoch()
for storage_object in storage_objects: for storage_object in storage_objects:
check_object_not_found( check_object_not_found(

View file

@ -4,6 +4,7 @@ from time import sleep
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.shell import Shell from frostfs_testlib.shell import Shell
from frostfs_testlib.steps.cli.container import create_container, delete_container from frostfs_testlib.steps.cli.container import create_container, delete_container
from frostfs_testlib.steps.cli.object import delete_object, head_object, put_object from frostfs_testlib.steps.cli.object import delete_object, head_object, put_object
@ -56,7 +57,7 @@ class TestReplication(ClusterTestBase):
file_path = generate_file(object_size.value) file_path = generate_file(object_size.value)
with allure.step("Put object"): with reporter.step("Put object"):
oid = put_object( oid = put_object(
wallet=default_wallet, wallet=default_wallet,
path=file_path, path=file_path,
@ -69,7 +70,7 @@ class TestReplication(ClusterTestBase):
) )
cluster_state_controller.start_node_host(node_for_rep) cluster_state_controller.start_node_host(node_for_rep)
with allure.step(f"Wait for replication."): with reporter.step(f"Wait for replication."):
object_nodes = wait_object_replication( object_nodes = wait_object_replication(
cid=cid, cid=cid,
oid=oid, oid=oid,
@ -78,7 +79,7 @@ class TestReplication(ClusterTestBase):
nodes=self.cluster.storage_nodes, nodes=self.cluster.storage_nodes,
) )
with allure.step("Check attributes"): with reporter.step("Check attributes"):
for node in object_nodes: for node in object_nodes:
header_info = head_object( header_info = head_object(
wallet=default_wallet, wallet=default_wallet,
@ -97,7 +98,7 @@ class TestReplication(ClusterTestBase):
f"expected attribute value: {attribute_value}" f"expected attribute value: {attribute_value}"
) )
with allure.step("Cleanup"): with reporter.step("Cleanup"):
delete_object( delete_object(
wallet=default_wallet, wallet=default_wallet,
cid=cid, cid=cid,

View file

@ -2,6 +2,7 @@ import logging
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.steps.acl import ( from frostfs_testlib.steps.acl import (
bearer_token_base64_from_file, bearer_token_base64_from_file,
@ -44,7 +45,7 @@ class Test_http_bearer(ClusterTestBase):
@pytest.fixture(scope="class") @pytest.fixture(scope="class")
def eacl_deny_for_others(self, user_container: str) -> None: def eacl_deny_for_others(self, user_container: str) -> None:
with allure.step(f"Set deny all operations for {EACLRole.OTHERS} via eACL"): with reporter.step(f"Set deny all operations for {EACLRole.OTHERS} via eACL"):
eacl = EACLRule(access=EACLAccess.DENY, role=EACLRole.OTHERS, operation=EACLOperation.PUT) eacl = EACLRule(access=EACLAccess.DENY, role=EACLRole.OTHERS, operation=EACLOperation.PUT)
set_eacl( set_eacl(
self.wallet, self.wallet,
@ -57,7 +58,7 @@ class Test_http_bearer(ClusterTestBase):
@pytest.fixture(scope="class") @pytest.fixture(scope="class")
def bearer_token_no_limit_for_others(self, user_container: str) -> str: def bearer_token_no_limit_for_others(self, user_container: str) -> str:
with allure.step(f"Create bearer token for {EACLRole.OTHERS} with all operations allowed"): with reporter.step(f"Create bearer token for {EACLRole.OTHERS} with all operations allowed"):
bearer = form_bearertoken_file( bearer = form_bearertoken_file(
self.wallet, self.wallet,
user_container, user_container,
@ -98,7 +99,7 @@ class Test_http_bearer(ClusterTestBase):
eacl_deny_for_others eacl_deny_for_others
bearer = bearer_token_no_limit_for_others bearer = bearer_token_no_limit_for_others
file_path = generate_file(object_size.value) file_path = generate_file(object_size.value)
with allure.step(f"Put object with bearer token for {EACLRole.OTHERS}, then get and verify hashes"): with reporter.step(f"Put object with bearer token for {EACLRole.OTHERS}, then get and verify hashes"):
headers = [f" -H 'Authorization: Bearer {bearer}'"] headers = [f" -H 'Authorization: Bearer {bearer}'"]
oid = upload_via_http_gate_curl( oid = upload_via_http_gate_curl(
cid=user_container, cid=user_container,

View file

@ -1,7 +1,6 @@
import os
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.steps.cli.container import create_container from frostfs_testlib.steps.cli.container import create_container
from frostfs_testlib.steps.cli.object import put_object_to_random_node from frostfs_testlib.steps.cli.object import put_object_to_random_node
@ -69,7 +68,7 @@ class TestHttpGate(ClusterTestBase):
file_path_simple = generate_file(simple_object_size.value) file_path_simple = generate_file(simple_object_size.value)
file_path_large = generate_file(complex_object_size.value) file_path_large = generate_file(complex_object_size.value)
with allure.step("Put objects using gRPC"): with reporter.step("Put objects using gRPC"):
oid_simple = put_object_to_random_node( oid_simple = put_object_to_random_node(
wallet=self.wallet, wallet=self.wallet,
path=file_path_simple, path=file_path_simple,
@ -135,7 +134,7 @@ class TestHttpPut(ClusterTestBase):
file_path_simple = generate_file(simple_object_size.value) file_path_simple = generate_file(simple_object_size.value)
file_path_large = generate_file(complex_object_size.value) file_path_large = generate_file(complex_object_size.value)
with allure.step("Put objects using HTTP"): with reporter.step("Put objects using HTTP"):
oid_simple = upload_via_http_gate( oid_simple = upload_via_http_gate(
cid=cid, path=file_path_simple, endpoint=self.cluster.default_http_gate_endpoint cid=cid, path=file_path_simple, endpoint=self.cluster.default_http_gate_endpoint
) )
@ -191,7 +190,7 @@ class TestHttpPut(ClusterTestBase):
) )
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
with allure.step("Put objects using HTTP with attribute"): with reporter.step("Put objects using HTTP with attribute"):
headers = attr_into_header(attributes) headers = attr_into_header(attributes)
oid = upload_via_http_gate( oid = upload_via_http_gate(
cid=cid, cid=cid,
@ -231,7 +230,7 @@ class TestHttpPut(ClusterTestBase):
valid_until = min_valid_epoch + gap_until valid_until = min_valid_epoch + gap_until
headers = {"X-Attribute-System-Expiration-Epoch": str(valid_until)} headers = {"X-Attribute-System-Expiration-Epoch": str(valid_until)}
with allure.step("Put objects using HTTP with attribute Expiration-Epoch"): with reporter.step("Put objects using HTTP with attribute Expiration-Epoch"):
oid = upload_via_http_gate( oid = upload_via_http_gate(
cid=cid, cid=cid,
path=file_path, path=file_path,
@ -243,7 +242,7 @@ class TestHttpPut(ClusterTestBase):
oids_to_be_valid.append(oid) oids_to_be_valid.append(oid)
else: else:
oids_to_be_expired.append(oid) oids_to_be_expired.append(oid)
with allure.step("This object can be got"): with reporter.step("This object can be got"):
get_via_http_gate( get_via_http_gate(
cid=cid, cid=cid,
oid=oid, oid=oid,
@ -257,7 +256,7 @@ class TestHttpPut(ClusterTestBase):
wait_for_gc_pass_on_storage_nodes() wait_for_gc_pass_on_storage_nodes()
for oid in oids_to_be_expired: for oid in oids_to_be_expired:
with allure.step(f"{oid} shall be expired and cannot be got"): with reporter.step(f"{oid} shall be expired and cannot be got"):
try_to_get_object_and_expect_error( try_to_get_object_and_expect_error(
cid=cid, cid=cid,
oid=oid, oid=oid,
@ -266,7 +265,7 @@ class TestHttpPut(ClusterTestBase):
http_hostname=self.cluster.default_http_hostname[0], http_hostname=self.cluster.default_http_hostname[0],
) )
for oid in oids_to_be_valid: for oid in oids_to_be_valid:
with allure.step(f"{oid} shall be valid and can be got"): with reporter.step(f"{oid} shall be valid and can be got"):
get_via_http_gate( get_via_http_gate(
cid=cid, cid=cid,
oid=oid, oid=oid,
@ -310,7 +309,7 @@ class TestHttpPut(ClusterTestBase):
http_hostname=self.cluster.default_http_hostname[0], http_hostname=self.cluster.default_http_hostname[0],
) )
with allure.step("Verify hashes"): with reporter.step("Verify hashes"):
assert get_file_hash(f"{dir_path}/file1") == get_file_hash(file_path_simple) assert get_file_hash(f"{dir_path}/file1") == get_file_hash(file_path_simple)
assert get_file_hash(f"{dir_path}/file2") == get_file_hash(file_path_large) assert get_file_hash(f"{dir_path}/file2") == get_file_hash(file_path_large)
@ -331,7 +330,7 @@ class TestHttpPut(ClusterTestBase):
file_path = generate_file(complex_object_size.value) file_path = generate_file(complex_object_size.value)
with allure.step("Put objects using HTTP"): with reporter.step("Put objects using HTTP"):
oid_gate = upload_via_http_gate(cid=cid, path=file_path, endpoint=self.cluster.default_http_gate_endpoint) oid_gate = upload_via_http_gate(cid=cid, path=file_path, endpoint=self.cluster.default_http_gate_endpoint)
oid_curl = upload_via_http_gate_curl( oid_curl = upload_via_http_gate_curl(
cid=cid, cid=cid,
@ -376,7 +375,7 @@ class TestHttpPut(ClusterTestBase):
file_path_simple = generate_file(simple_object_size.value) file_path_simple = generate_file(simple_object_size.value)
file_path_large = generate_file(complex_object_size.value) file_path_large = generate_file(complex_object_size.value)
with allure.step("Put objects using curl utility"): with reporter.step("Put objects using curl utility"):
oid_simple = upload_via_http_gate_curl( oid_simple = upload_via_http_gate_curl(
cid=cid, filepath=file_path_simple, endpoint=self.cluster.default_http_gate_endpoint cid=cid, filepath=file_path_simple, endpoint=self.cluster.default_http_gate_endpoint
) )

View file

@ -3,6 +3,7 @@ import os
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.steps.cli.container import ( from frostfs_testlib.steps.cli.container import (
create_container, create_container,
@ -88,7 +89,7 @@ class Test_http_headers(ClusterTestBase):
storage_object_1 = storage_objects_with_attributes[0] storage_object_1 = storage_objects_with_attributes[0]
with allure.step( with reporter.step(
f'Download object#1 via wget with attributes Chapter2: {storage_object_1.attributes["Chapter2"]} and compare hashes' f'Download object#1 via wget with attributes Chapter2: {storage_object_1.attributes["Chapter2"]} and compare hashes'
): ):
get_object_by_attr_and_verify_hashes( get_object_by_attr_and_verify_hashes(
@ -115,7 +116,7 @@ class Test_http_headers(ClusterTestBase):
storage_object_1 = storage_objects_with_attributes[0] storage_object_1 = storage_objects_with_attributes[0]
storage_object_2 = storage_objects_with_attributes[1] storage_object_2 = storage_objects_with_attributes[1]
with allure.step( with reporter.step(
f'Download object#2 via wget with attributes [chapter2={storage_object_2.attributes["chapter2"]}] / [Ch@pter1={storage_object_2.attributes["Ch@pter1"]}] and compare hashes' f'Download object#2 via wget with attributes [chapter2={storage_object_2.attributes["chapter2"]}] / [Ch@pter1={storage_object_2.attributes["Ch@pter1"]}] and compare hashes'
): ):
selected_attributes_object2 = [ selected_attributes_object2 = [
@ -131,7 +132,7 @@ class Test_http_headers(ClusterTestBase):
endpoint=self.cluster.default_http_gate_endpoint, endpoint=self.cluster.default_http_gate_endpoint,
http_hostname=self.cluster.default_http_hostname[0], http_hostname=self.cluster.default_http_hostname[0],
) )
with allure.step("Delete object#2 and verify is the container deleted"): with reporter.step("Delete object#2 and verify is the container deleted"):
delete_object( delete_object(
wallet=self.wallet, wallet=self.wallet,
cid=storage_object_2.cid, cid=storage_object_2.cid,
@ -148,7 +149,7 @@ class Test_http_headers(ClusterTestBase):
) )
storage_objects_with_attributes.remove(storage_object_2) storage_objects_with_attributes.remove(storage_object_2)
with allure.step( with reporter.step(
f'Download object#1 with attributes [Writer={storage_object_1.attributes["Writer"]}] and compare hashes' f'Download object#1 with attributes [Writer={storage_object_1.attributes["Writer"]}] and compare hashes'
): ):
key_value_pair = {"Writer": storage_object_1.attributes["Writer"]} key_value_pair = {"Writer": storage_object_1.attributes["Writer"]}
@ -175,7 +176,7 @@ class Test_http_headers(ClusterTestBase):
""" """
storage_object_1 = storage_objects_with_attributes[0] storage_object_1 = storage_objects_with_attributes[0]
with allure.step( with reporter.step(
"[Negative] Allocate and attemt to put object#3 via http with attributes: [Writer=Leo Tolstoy, Writer=peace, peace=peace]" "[Negative] Allocate and attemt to put object#3 via http with attributes: [Writer=Leo Tolstoy, Writer=peace, peace=peace]"
): ):
file_path_3 = generate_file(storage_object_1.size) file_path_3 = generate_file(storage_object_1.size)
@ -190,7 +191,7 @@ class Test_http_headers(ClusterTestBase):
headers=headers, headers=headers,
error_pattern=error_pattern, error_pattern=error_pattern,
) )
with allure.step("Delete container and verify container deletion"): with reporter.step("Delete container and verify container deletion"):
delete_container( delete_container(
wallet=self.wallet, wallet=self.wallet,
cid=storage_object_1.cid, cid=storage_object_1.cid,
@ -208,7 +209,7 @@ class Test_http_headers(ClusterTestBase):
assert storage_object_1.cid not in list_containers( assert storage_object_1.cid not in list_containers(
self.wallet, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint self.wallet, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint
) )
with allure.step("[Negative] Try to download (wget) object via wget with attributes [peace=peace]"): with reporter.step("[Negative] Try to download (wget) object via wget with attributes [peace=peace]"):
request = f"/get/{storage_object_1.cid}/peace/peace" request = f"/get/{storage_object_1.cid}/peace/peace"
error_pattern = "404 Not Found" error_pattern = "404 Not Found"
try_to_get_object_via_passed_request_and_expect_error( try_to_get_object_via_passed_request_and_expect_error(

View file

@ -2,6 +2,7 @@ import logging
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.s3 import AwsCliClient, S3ClientWrapper from frostfs_testlib.s3 import AwsCliClient, S3ClientWrapper
from frostfs_testlib.steps.cli.container import create_container from frostfs_testlib.steps.cli.container import create_container
@ -51,7 +52,7 @@ class Test_http_object(ClusterTestBase):
Hashes must be the same. Hashes must be the same.
""" """
with allure.step("Create public container"): with reporter.step("Create public container"):
cid = create_container( cid = create_container(
self.wallet, self.wallet,
shell=self.shell, shell=self.shell,
@ -73,7 +74,7 @@ class Test_http_object(ClusterTestBase):
key_value1 = obj_key1 + "=" + obj_value1 key_value1 = obj_key1 + "=" + obj_value1
key_value2 = obj_key2 + "=" + obj_value2 key_value2 = obj_key2 + "=" + obj_value2
with allure.step("Put objects using gRPC [--attributes chapter1=peace,chapter2=war]"): with reporter.step("Put objects using gRPC [--attributes chapter1=peace,chapter2=war]"):
oid = put_object_to_random_node( oid = put_object_to_random_node(
wallet=self.wallet, wallet=self.wallet,
path=file_path, path=file_path,
@ -82,7 +83,7 @@ class Test_http_object(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
attributes=f"{key_value1},{key_value2}", attributes=f"{key_value1},{key_value2}",
) )
with allure.step("Get object and verify hashes [ get/$CID/$OID ]"): with reporter.step("Get object and verify hashes [ get/$CID/$OID ]"):
verify_object_hash( verify_object_hash(
oid=oid, oid=oid,
file_name=file_path, file_name=file_path,
@ -94,7 +95,7 @@ class Test_http_object(ClusterTestBase):
http_hostname=self.cluster.default_http_hostname[0], http_hostname=self.cluster.default_http_hostname[0],
) )
with allure.step("[Negative] try to get object: [get/$CID/chapter1/peace]"): with reporter.step("[Negative] try to get object: [get/$CID/chapter1/peace]"):
attrs = {obj_key1: obj_value1, obj_key2: obj_value2} attrs = {obj_key1: obj_value1, obj_key2: obj_value2}
request = f"/get/{cid}/{obj_key1}/{obj_value1}" request = f"/get/{cid}/{obj_key1}/{obj_value1}"
expected_err_msg = "Failed to get object via HTTP gate:" expected_err_msg = "Failed to get object via HTTP gate:"
@ -108,7 +109,7 @@ class Test_http_object(ClusterTestBase):
http_hostname=self.cluster.default_http_hostname[0], http_hostname=self.cluster.default_http_hostname[0],
) )
with allure.step("Download the object with attribute [get_by_attribute/$CID/chapter1/peace]"): with reporter.step("Download the object with attribute [get_by_attribute/$CID/chapter1/peace]"):
get_object_by_attr_and_verify_hashes( get_object_by_attr_and_verify_hashes(
oid=oid, oid=oid,
file_name=file_path, file_name=file_path,
@ -117,7 +118,7 @@ class Test_http_object(ClusterTestBase):
endpoint=self.cluster.default_http_gate_endpoint, endpoint=self.cluster.default_http_gate_endpoint,
http_hostname=self.cluster.default_http_hostname[0], http_hostname=self.cluster.default_http_hostname[0],
) )
with allure.step("[Negative] try to get object: get_by_attribute/$CID/$OID"): with reporter.step("[Negative] try to get object: get_by_attribute/$CID/$OID"):
request = f"/get_by_attribute/{cid}/{oid}" request = f"/get_by_attribute/{cid}/{oid}"
try_to_get_object_via_passed_request_and_expect_error( try_to_get_object_via_passed_request_and_expect_error(
cid=cid, cid=cid,
@ -159,5 +160,5 @@ class Test_http_object(ClusterTestBase):
http_hostname=self.cluster.default_http_hostname[0], http_hostname=self.cluster.default_http_hostname[0],
request_path=request, request_path=request,
) )
with allure.step("Verify hashes"): with reporter.step("Verify hashes"):
assert_hashes_are_equal(file_path, obj_http, obj_s3) assert_hashes_are_equal(file_path, obj_http, obj_s3)

View file

@ -2,6 +2,7 @@ import logging
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.steps.cli.container import create_container from frostfs_testlib.steps.cli.container import create_container
from frostfs_testlib.steps.http.http_gate import upload_via_http_gate_curl, verify_object_hash from frostfs_testlib.steps.http.http_gate import upload_via_http_gate_curl, verify_object_hash
@ -37,7 +38,7 @@ class Test_http_streaming(ClusterTestBase):
Expected result: Expected result:
Hashes must be the same. Hashes must be the same.
""" """
with allure.step("Create public container and verify container creation"): with reporter.step("Create public container and verify container creation"):
cid = create_container( cid = create_container(
self.wallet, self.wallet,
shell=self.shell, shell=self.shell,
@ -45,11 +46,11 @@ class Test_http_streaming(ClusterTestBase):
rule=self.PLACEMENT_RULE, rule=self.PLACEMENT_RULE,
basic_acl=PUBLIC_ACL, basic_acl=PUBLIC_ACL,
) )
with allure.step("Allocate big object"): with reporter.step("Allocate big object"):
# Generate file # Generate file
file_path = generate_file(complex_object_size.value) file_path = generate_file(complex_object_size.value)
with allure.step("Put objects using curl utility and Get object and verify hashes [ get/$CID/$OID ]"): with reporter.step("Put objects using curl utility and Get object and verify hashes [ get/$CID/$OID ]"):
oid = upload_via_http_gate_curl( oid = upload_via_http_gate_curl(
cid=cid, filepath=file_path, endpoint=self.cluster.default_http_gate_endpoint cid=cid, filepath=file_path, endpoint=self.cluster.default_http_gate_endpoint
) )

View file

@ -5,6 +5,7 @@ from typing import Optional
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.error_patterns import OBJECT_NOT_FOUND from frostfs_testlib.resources.error_patterns import OBJECT_NOT_FOUND
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.steps.cli.container import create_container from frostfs_testlib.steps.cli.container import create_container
@ -139,7 +140,7 @@ class Test_http_system_header(ClusterTestBase):
def test_unable_put_expired_epoch(self, user_container: str, simple_object_size: ObjectSize): def test_unable_put_expired_epoch(self, user_container: str, simple_object_size: ObjectSize):
headers = attr_into_str_header_curl({"System-Expiration-Epoch": str(get_epoch(self.shell, self.cluster) - 1)}) headers = attr_into_str_header_curl({"System-Expiration-Epoch": str(get_epoch(self.shell, self.cluster) - 1)})
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
with allure.step("Put object using HTTP with attribute Expiration-Epoch where epoch is expired"): with reporter.step("Put object using HTTP with attribute Expiration-Epoch where epoch is expired"):
upload_via_http_gate_curl( upload_via_http_gate_curl(
cid=user_container, cid=user_container,
filepath=file_path, filepath=file_path,
@ -152,7 +153,9 @@ class Test_http_system_header(ClusterTestBase):
def test_unable_put_negative_duration(self, user_container: str, simple_object_size: ObjectSize): def test_unable_put_negative_duration(self, user_container: str, simple_object_size: ObjectSize):
headers = attr_into_str_header_curl({"System-Expiration-Duration": "-1h"}) headers = attr_into_str_header_curl({"System-Expiration-Duration": "-1h"})
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
with allure.step("Put object using HTTP with attribute System-Expiration-Duration where duration is negative"): with reporter.step(
"Put object using HTTP with attribute System-Expiration-Duration where duration is negative"
):
upload_via_http_gate_curl( upload_via_http_gate_curl(
cid=user_container, cid=user_container,
filepath=file_path, filepath=file_path,
@ -165,7 +168,7 @@ class Test_http_system_header(ClusterTestBase):
def test_unable_put_expired_timestamp(self, user_container: str, simple_object_size: ObjectSize): def test_unable_put_expired_timestamp(self, user_container: str, simple_object_size: ObjectSize):
headers = attr_into_str_header_curl({"System-Expiration-Timestamp": "1635075727"}) headers = attr_into_str_header_curl({"System-Expiration-Timestamp": "1635075727"})
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
with allure.step( with reporter.step(
"Put object using HTTP with attribute System-Expiration-Timestamp where duration is in the past" "Put object using HTTP with attribute System-Expiration-Timestamp where duration is in the past"
): ):
upload_via_http_gate_curl( upload_via_http_gate_curl(
@ -200,21 +203,21 @@ class Test_http_system_header(ClusterTestBase):
) )
attributes = {SYSTEM_EXPIRATION_EPOCH: expected_epoch, SYSTEM_EXPIRATION_DURATION: "1m"} attributes = {SYSTEM_EXPIRATION_EPOCH: expected_epoch, SYSTEM_EXPIRATION_DURATION: "1m"}
file_path = generate_file(object_size.value) file_path = generate_file(object_size.value)
with allure.step( with reporter.step(
f"Put objects using HTTP with attributes and head command should display {EXPIRATION_EPOCH_HEADER}: {expected_epoch} attr" f"Put objects using HTTP with attributes and head command should display {EXPIRATION_EPOCH_HEADER}: {expected_epoch} attr"
): ):
oid, head_info = self.oid_header_info_for_object( oid, head_info = self.oid_header_info_for_object(
file_path=file_path, attributes=attributes, user_container=user_container file_path=file_path, attributes=attributes, user_container=user_container
) )
self.validation_for_http_header_attr(head_info=head_info, expected_epoch=expected_epoch) self.validation_for_http_header_attr(head_info=head_info, expected_epoch=expected_epoch)
with allure.step("Check that object becomes unavailable when epoch is expired"): with reporter.step("Check that object becomes unavailable when epoch is expired"):
for _ in range(0, epoch_count + 1): for _ in range(0, epoch_count + 1):
self.tick_epoch() self.tick_epoch()
assert ( assert (
get_epoch(self.shell, self.cluster) == expected_epoch + 1 get_epoch(self.shell, self.cluster) == expected_epoch + 1
), f"Epochs should be equal: {get_epoch(self.shell, self.cluster)} != {expected_epoch + 1}" ), f"Epochs should be equal: {get_epoch(self.shell, self.cluster)} != {expected_epoch + 1}"
with allure.step("Check object deleted because it expires-on epoch"): with reporter.step("Check object deleted because it expires-on epoch"):
wait_for_epochs_align(self.shell, self.cluster) wait_for_epochs_align(self.shell, self.cluster)
try_to_get_object_and_expect_error( try_to_get_object_and_expect_error(
cid=user_container, cid=user_container,
@ -240,21 +243,21 @@ class Test_http_system_header(ClusterTestBase):
SYSTEM_EXPIRATION_TIMESTAMP: self.epoch_count_into_timestamp(epoch_duration=epoch_duration, epoch=1), SYSTEM_EXPIRATION_TIMESTAMP: self.epoch_count_into_timestamp(epoch_duration=epoch_duration, epoch=1),
} }
file_path = generate_file(object_size.value) file_path = generate_file(object_size.value)
with allure.step( with reporter.step(
f"Put objects using HTTP with attributes and head command should display {EXPIRATION_EPOCH_HEADER}: {expected_epoch} attr" f"Put objects using HTTP with attributes and head command should display {EXPIRATION_EPOCH_HEADER}: {expected_epoch} attr"
): ):
oid, head_info = self.oid_header_info_for_object( oid, head_info = self.oid_header_info_for_object(
file_path=file_path, attributes=attributes, user_container=user_container file_path=file_path, attributes=attributes, user_container=user_container
) )
self.validation_for_http_header_attr(head_info=head_info, expected_epoch=expected_epoch) self.validation_for_http_header_attr(head_info=head_info, expected_epoch=expected_epoch)
with allure.step("Check that object becomes unavailable when epoch is expired"): with reporter.step("Check that object becomes unavailable when epoch is expired"):
for _ in range(0, epoch_count + 1): for _ in range(0, epoch_count + 1):
self.tick_epoch() self.tick_epoch()
assert ( assert (
get_epoch(self.shell, self.cluster) == expected_epoch + 1 get_epoch(self.shell, self.cluster) == expected_epoch + 1
), f"Epochs should be equal: {get_epoch(self.shell, self.cluster)} != {expected_epoch + 1}" ), f"Epochs should be equal: {get_epoch(self.shell, self.cluster)} != {expected_epoch + 1}"
with allure.step("Check object deleted because it expires-on epoch"): with reporter.step("Check object deleted because it expires-on epoch"):
wait_for_epochs_align(self.shell, self.cluster) wait_for_epochs_align(self.shell, self.cluster)
try_to_get_object_and_expect_error( try_to_get_object_and_expect_error(
cid=user_container, cid=user_container,
@ -282,21 +285,21 @@ class Test_http_system_header(ClusterTestBase):
), ),
} }
file_path = generate_file(object_size.value) file_path = generate_file(object_size.value)
with allure.step( with reporter.step(
f"Put objects using HTTP with attributes and head command should display {EXPIRATION_EPOCH_HEADER}: {expected_epoch} attr" f"Put objects using HTTP with attributes and head command should display {EXPIRATION_EPOCH_HEADER}: {expected_epoch} attr"
): ):
oid, head_info = self.oid_header_info_for_object( oid, head_info = self.oid_header_info_for_object(
file_path=file_path, attributes=attributes, user_container=user_container file_path=file_path, attributes=attributes, user_container=user_container
) )
self.validation_for_http_header_attr(head_info=head_info, expected_epoch=expected_epoch) self.validation_for_http_header_attr(head_info=head_info, expected_epoch=expected_epoch)
with allure.step("Check that object becomes unavailable when epoch is expired"): with reporter.step("Check that object becomes unavailable when epoch is expired"):
for _ in range(0, epoch_count + 1): for _ in range(0, epoch_count + 1):
self.tick_epoch() self.tick_epoch()
assert ( assert (
get_epoch(self.shell, self.cluster) == expected_epoch + 1 get_epoch(self.shell, self.cluster) == expected_epoch + 1
), f"Epochs should be equal: {get_epoch(self.shell, self.cluster)} != {expected_epoch + 1}" ), f"Epochs should be equal: {get_epoch(self.shell, self.cluster)} != {expected_epoch + 1}"
with allure.step("Check object deleted because it expires-on epoch"): with reporter.step("Check object deleted because it expires-on epoch"):
wait_for_epochs_align(self.shell, self.cluster) wait_for_epochs_align(self.shell, self.cluster)
try_to_get_object_and_expect_error( try_to_get_object_and_expect_error(
cid=user_container, cid=user_container,
@ -331,7 +334,7 @@ class Test_http_system_header(ClusterTestBase):
) )
} }
file_path = generate_file(object_size.value) file_path = generate_file(object_size.value)
with allure.step( with reporter.step(
f"Put objects using HTTP with attributes and head command should display {EXPIRATION_EPOCH_HEADER}: {expected_epoch} attr" f"Put objects using HTTP with attributes and head command should display {EXPIRATION_EPOCH_HEADER}: {expected_epoch} attr"
): ):
oid, head_info = self.oid_header_info_for_object( oid, head_info = self.oid_header_info_for_object(
@ -340,7 +343,7 @@ class Test_http_system_header(ClusterTestBase):
user_container=user_container, user_container=user_container,
) )
self.validation_for_http_header_attr(head_info=head_info, expected_epoch=expected_epoch) self.validation_for_http_header_attr(head_info=head_info, expected_epoch=expected_epoch)
with allure.step("Check that object becomes unavailable when epoch is expired"): with reporter.step("Check that object becomes unavailable when epoch is expired"):
for _ in range(0, epoch_count + 1): for _ in range(0, epoch_count + 1):
self.tick_epoch() self.tick_epoch()
# check that {EXPIRATION_EXPIRATION_RFC} absents in header output # check that {EXPIRATION_EXPIRATION_RFC} absents in header output
@ -348,7 +351,7 @@ class Test_http_system_header(ClusterTestBase):
get_epoch(self.shell, self.cluster) == expected_epoch + 1 get_epoch(self.shell, self.cluster) == expected_epoch + 1
), f"Epochs should be equal: {get_epoch(self.shell, self.cluster)} != {expected_epoch + 1}" ), f"Epochs should be equal: {get_epoch(self.shell, self.cluster)} != {expected_epoch + 1}"
with allure.step("Check object deleted because it expires-on epoch"): with reporter.step("Check object deleted because it expires-on epoch"):
wait_for_epochs_align(self.shell, self.cluster) wait_for_epochs_align(self.shell, self.cluster)
try_to_get_object_and_expect_error( try_to_get_object_and_expect_error(
cid=user_container, cid=user_container,

View file

@ -1,5 +1,6 @@
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.s3 import AwsCliClient, Boto3ClientWrapper, S3ClientWrapper from frostfs_testlib.s3 import AwsCliClient, Boto3ClientWrapper, S3ClientWrapper
from frostfs_testlib.steps.s3 import s3_helper from frostfs_testlib.steps.s3 import s3_helper
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
@ -15,22 +16,22 @@ class TestS3GateACL:
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
with allure.step("Put object into bucket, Check ACL is empty"): with reporter.step("Put object into bucket, Check ACL is empty"):
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
obj_acl = s3_client.get_object_acl(bucket, file_name) obj_acl = s3_client.get_object_acl(bucket, file_name)
assert obj_acl == [], f"Expected ACL is empty, got {obj_acl}" assert obj_acl == [], f"Expected ACL is empty, got {obj_acl}"
with allure.step("Put object ACL = public-read"): with reporter.step("Put object ACL = public-read"):
s3_client.put_object_acl(bucket, file_name, "public-read") s3_client.put_object_acl(bucket, file_name, "public-read")
obj_acl = s3_client.get_object_acl(bucket, file_name) obj_acl = s3_client.get_object_acl(bucket, file_name)
s3_helper.assert_s3_acl(acl_grants=obj_acl, permitted_users="AllUsers") s3_helper.assert_s3_acl(acl_grants=obj_acl, permitted_users="AllUsers")
with allure.step("Put object ACL = private"): with reporter.step("Put object ACL = private"):
s3_client.put_object_acl(bucket, file_name, "private") s3_client.put_object_acl(bucket, file_name, "private")
obj_acl = s3_client.get_object_acl(bucket, file_name) obj_acl = s3_client.get_object_acl(bucket, file_name)
s3_helper.assert_s3_acl(acl_grants=obj_acl, permitted_users="CanonicalUser") s3_helper.assert_s3_acl(acl_grants=obj_acl, permitted_users="CanonicalUser")
with allure.step("Put object with grant-read uri=http://acs.amazonaws.com/groups/global/AllUsers"): with reporter.step("Put object with grant-read uri=http://acs.amazonaws.com/groups/global/AllUsers"):
s3_client.put_object_acl( s3_client.put_object_acl(
bucket, bucket,
file_name, file_name,
@ -42,17 +43,17 @@ class TestS3GateACL:
@allure.title("Bucket ACL (s3_client={s3_client})") @allure.title("Bucket ACL (s3_client={s3_client})")
@pytest.mark.parametrize("s3_client", [AwsCliClient, Boto3ClientWrapper], indirect=True) @pytest.mark.parametrize("s3_client", [AwsCliClient, Boto3ClientWrapper], indirect=True)
def test_s3_bucket_ACL(self, s3_client: S3ClientWrapper): def test_s3_bucket_ACL(self, s3_client: S3ClientWrapper):
with allure.step("Create bucket with ACL = public-read-write"): with reporter.step("Create bucket with ACL = public-read-write"):
bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True, acl="public-read-write") bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True, acl="public-read-write")
bucket_acl = s3_client.get_bucket_acl(bucket) bucket_acl = s3_client.get_bucket_acl(bucket)
s3_helper.assert_s3_acl(acl_grants=bucket_acl, permitted_users="AllUsers") s3_helper.assert_s3_acl(acl_grants=bucket_acl, permitted_users="AllUsers")
with allure.step("Change bucket ACL to private"): with reporter.step("Change bucket ACL to private"):
s3_client.put_bucket_acl(bucket, acl="private") s3_client.put_bucket_acl(bucket, acl="private")
bucket_acl = s3_client.get_bucket_acl(bucket) bucket_acl = s3_client.get_bucket_acl(bucket)
s3_helper.assert_s3_acl(acl_grants=bucket_acl, permitted_users="CanonicalUser") s3_helper.assert_s3_acl(acl_grants=bucket_acl, permitted_users="CanonicalUser")
with allure.step("Change bucket acl to --grant-write uri=http://acs.amazonaws.com/groups/global/AllUsers"): with reporter.step("Change bucket acl to --grant-write uri=http://acs.amazonaws.com/groups/global/AllUsers"):
s3_client.put_bucket_acl( s3_client.put_bucket_acl(
bucket, bucket,
grant_write="uri=http://acs.amazonaws.com/groups/global/AllUsers", grant_write="uri=http://acs.amazonaws.com/groups/global/AllUsers",

View file

@ -2,6 +2,7 @@ from datetime import datetime, timedelta
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.s3 import S3ClientWrapper from frostfs_testlib.s3 import S3ClientWrapper
from frostfs_testlib.steps.s3 import s3_helper from frostfs_testlib.steps.s3 import s3_helper
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
@ -14,22 +15,22 @@ class TestS3GateBucket:
@allure.title("Create Bucket with different ACL (s3_client={s3_client})") @allure.title("Create Bucket with different ACL (s3_client={s3_client})")
def test_s3_create_bucket_with_ACL(self, s3_client: S3ClientWrapper): def test_s3_create_bucket_with_ACL(self, s3_client: S3ClientWrapper):
with allure.step("Create bucket with ACL private"): with reporter.step("Create bucket with ACL private"):
bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True, acl="private") bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True, acl="private")
bucket_acl = s3_client.get_bucket_acl(bucket) bucket_acl = s3_client.get_bucket_acl(bucket)
s3_helper.assert_s3_acl(acl_grants=bucket_acl, permitted_users="CanonicalUser") s3_helper.assert_s3_acl(acl_grants=bucket_acl, permitted_users="CanonicalUser")
with allure.step("Create bucket with ACL = public-read"): with reporter.step("Create bucket with ACL = public-read"):
bucket_1 = s3_client.create_bucket(object_lock_enabled_for_bucket=True, acl="public-read") bucket_1 = s3_client.create_bucket(object_lock_enabled_for_bucket=True, acl="public-read")
bucket_acl_1 = s3_client.get_bucket_acl(bucket_1) bucket_acl_1 = s3_client.get_bucket_acl(bucket_1)
s3_helper.assert_s3_acl(acl_grants=bucket_acl_1, permitted_users="AllUsers") s3_helper.assert_s3_acl(acl_grants=bucket_acl_1, permitted_users="AllUsers")
with allure.step("Create bucket with ACL public-read-write"): with reporter.step("Create bucket with ACL public-read-write"):
bucket_2 = s3_client.create_bucket(object_lock_enabled_for_bucket=True, acl="public-read-write") bucket_2 = s3_client.create_bucket(object_lock_enabled_for_bucket=True, acl="public-read-write")
bucket_acl_2 = s3_client.get_bucket_acl(bucket_2) bucket_acl_2 = s3_client.get_bucket_acl(bucket_2)
s3_helper.assert_s3_acl(acl_grants=bucket_acl_2, permitted_users="AllUsers") s3_helper.assert_s3_acl(acl_grants=bucket_acl_2, permitted_users="AllUsers")
with allure.step("Create bucket with ACL = authenticated-read"): with reporter.step("Create bucket with ACL = authenticated-read"):
bucket_3 = s3_client.create_bucket(object_lock_enabled_for_bucket=True, acl="authenticated-read") bucket_3 = s3_client.create_bucket(object_lock_enabled_for_bucket=True, acl="authenticated-read")
bucket_acl_3 = s3_client.get_bucket_acl(bucket_3) bucket_acl_3 = s3_client.get_bucket_acl(bucket_3)
s3_helper.assert_s3_acl(acl_grants=bucket_acl_3, permitted_users="AllUsers") s3_helper.assert_s3_acl(acl_grants=bucket_acl_3, permitted_users="AllUsers")
@ -37,7 +38,7 @@ class TestS3GateBucket:
@allure.title("Create Bucket with different ACL by grant (s3_client={s3_client})") @allure.title("Create Bucket with different ACL by grant (s3_client={s3_client})")
def test_s3_create_bucket_with_grands(self, s3_client: S3ClientWrapper): def test_s3_create_bucket_with_grands(self, s3_client: S3ClientWrapper):
with allure.step("Create bucket with --grant-read"): with reporter.step("Create bucket with --grant-read"):
bucket = s3_client.create_bucket( bucket = s3_client.create_bucket(
object_lock_enabled_for_bucket=True, object_lock_enabled_for_bucket=True,
grant_read="uri=http://acs.amazonaws.com/groups/global/AllUsers", grant_read="uri=http://acs.amazonaws.com/groups/global/AllUsers",
@ -45,7 +46,7 @@ class TestS3GateBucket:
bucket_acl = s3_client.get_bucket_acl(bucket) bucket_acl = s3_client.get_bucket_acl(bucket)
s3_helper.assert_s3_acl(acl_grants=bucket_acl, permitted_users="AllUsers") s3_helper.assert_s3_acl(acl_grants=bucket_acl, permitted_users="AllUsers")
with allure.step("Create bucket with --grant-wtite"): with reporter.step("Create bucket with --grant-wtite"):
bucket_1 = s3_client.create_bucket( bucket_1 = s3_client.create_bucket(
object_lock_enabled_for_bucket=True, object_lock_enabled_for_bucket=True,
grant_write="uri=http://acs.amazonaws.com/groups/global/AllUsers", grant_write="uri=http://acs.amazonaws.com/groups/global/AllUsers",
@ -53,7 +54,7 @@ class TestS3GateBucket:
bucket_acl_1 = s3_client.get_bucket_acl(bucket_1) bucket_acl_1 = s3_client.get_bucket_acl(bucket_1)
s3_helper.assert_s3_acl(acl_grants=bucket_acl_1, permitted_users="AllUsers") s3_helper.assert_s3_acl(acl_grants=bucket_acl_1, permitted_users="AllUsers")
with allure.step("Create bucket with --grant-full-control"): with reporter.step("Create bucket with --grant-full-control"):
bucket_2 = s3_client.create_bucket( bucket_2 = s3_client.create_bucket(
object_lock_enabled_for_bucket=True, object_lock_enabled_for_bucket=True,
grant_full_control="uri=http://acs.amazonaws.com/groups/global/AllUsers", grant_full_control="uri=http://acs.amazonaws.com/groups/global/AllUsers",
@ -66,7 +67,7 @@ class TestS3GateBucket:
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
with allure.step("Create bucket with --no-object-lock-enabled-for-bucket"): with reporter.step("Create bucket with --no-object-lock-enabled-for-bucket"):
bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=False) bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=False)
date_obj = datetime.utcnow() + timedelta(days=1) date_obj = datetime.utcnow() + timedelta(days=1)
with pytest.raises(Exception, match=r".*Object Lock configuration does not exist for this bucket.*"): with pytest.raises(Exception, match=r".*Object Lock configuration does not exist for this bucket.*"):
@ -78,7 +79,7 @@ class TestS3GateBucket:
object_lock_mode="COMPLIANCE", object_lock_mode="COMPLIANCE",
object_lock_retain_until_date=date_obj.strftime("%Y-%m-%dT%H:%M:%S"), object_lock_retain_until_date=date_obj.strftime("%Y-%m-%dT%H:%M:%S"),
) )
with allure.step("Create bucket with --object-lock-enabled-for-bucket"): with reporter.step("Create bucket with --object-lock-enabled-for-bucket"):
bucket_1 = s3_client.create_bucket(object_lock_enabled_for_bucket=True) bucket_1 = s3_client.create_bucket(object_lock_enabled_for_bucket=True)
date_obj_1 = datetime.utcnow() + timedelta(days=1) date_obj_1 = datetime.utcnow() + timedelta(days=1)
s3_client.put_object( s3_client.put_object(
@ -98,21 +99,21 @@ class TestS3GateBucket:
file_name_2 = s3_helper.object_key_from_file_path(file_path_2) file_name_2 = s3_helper.object_key_from_file_path(file_path_2)
bucket = s3_client.create_bucket() bucket = s3_client.create_bucket()
with allure.step("Put two objects into bucket"): with reporter.step("Put two objects into bucket"):
s3_client.put_object(bucket, file_path_1) s3_client.put_object(bucket, file_path_1)
s3_client.put_object(bucket, file_path_2) s3_client.put_object(bucket, file_path_2)
s3_helper.check_objects_in_bucket(s3_client, bucket, [file_name_1, file_name_2]) s3_helper.check_objects_in_bucket(s3_client, bucket, [file_name_1, file_name_2])
with allure.step("Try to delete not empty bucket and get error"): with reporter.step("Try to delete not empty bucket and get error"):
with pytest.raises(Exception, match=r".*The bucket you tried to delete is not empty.*"): with pytest.raises(Exception, match=r".*The bucket you tried to delete is not empty.*"):
s3_client.delete_bucket(bucket) s3_client.delete_bucket(bucket)
with allure.step("Delete object in bucket"): with reporter.step("Delete object in bucket"):
s3_client.delete_object(bucket, file_name_1) s3_client.delete_object(bucket, file_name_1)
s3_client.delete_object(bucket, file_name_2) s3_client.delete_object(bucket, file_name_2)
s3_helper.check_objects_in_bucket(s3_client, bucket, []) s3_helper.check_objects_in_bucket(s3_client, bucket, [])
with allure.step("Delete empty bucket"): with reporter.step("Delete empty bucket"):
s3_client.delete_bucket(bucket) s3_client.delete_bucket(bucket)
with pytest.raises(Exception, match=r".*Not Found.*"): with pytest.raises(Exception, match=r".*Not Found.*"):
s3_client.head_bucket(bucket) s3_client.head_bucket(bucket)

View file

@ -4,6 +4,7 @@ from random import choice, choices
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.common import ASSETS_DIR from frostfs_testlib.resources.common import ASSETS_DIR
from frostfs_testlib.s3 import AwsCliClient, Boto3ClientWrapper, S3ClientWrapper, VersioningStatus from frostfs_testlib.s3 import AwsCliClient, Boto3ClientWrapper, S3ClientWrapper, VersioningStatus
from frostfs_testlib.shell import Shell from frostfs_testlib.shell import Shell
@ -42,43 +43,43 @@ class TestS3Gate:
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
with allure.step("Create buckets"): with reporter.step("Create buckets"):
bucket_1 = s3_client.create_bucket(object_lock_enabled_for_bucket=True) bucket_1 = s3_client.create_bucket(object_lock_enabled_for_bucket=True)
s3_helper.set_bucket_versioning(s3_client, bucket_1, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket_1, VersioningStatus.ENABLED)
bucket_2 = s3_client.create_bucket() bucket_2 = s3_client.create_bucket()
with allure.step("Check buckets are presented in the system"): with reporter.step("Check buckets are presented in the system"):
buckets = s3_client.list_buckets() buckets = s3_client.list_buckets()
assert bucket_1 in buckets, f"Expected bucket {bucket_1} is in the list" assert bucket_1 in buckets, f"Expected bucket {bucket_1} is in the list"
assert bucket_2 in buckets, f"Expected bucket {bucket_2} is in the list" assert bucket_2 in buckets, f"Expected bucket {bucket_2} is in the list"
with allure.step("Bucket must be empty"): with reporter.step("Bucket must be empty"):
for bucket in (bucket_1, bucket_2): for bucket in (bucket_1, bucket_2):
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
with allure.step("Check buckets are visible with S3 head command"): with reporter.step("Check buckets are visible with S3 head command"):
s3_client.head_bucket(bucket_1) s3_client.head_bucket(bucket_1)
s3_client.head_bucket(bucket_2) s3_client.head_bucket(bucket_2)
with allure.step("Check we can put/list object with S3 commands"): with reporter.step("Check we can put/list object with S3 commands"):
version_id = s3_client.put_object(bucket_1, file_path) version_id = s3_client.put_object(bucket_1, file_path)
s3_client.head_object(bucket_1, file_name) s3_client.head_object(bucket_1, file_name)
bucket_objects = s3_client.list_objects(bucket_1) bucket_objects = s3_client.list_objects(bucket_1)
assert file_name in bucket_objects, f"Expected file {file_name} in objects list {bucket_objects}" assert file_name in bucket_objects, f"Expected file {file_name} in objects list {bucket_objects}"
with allure.step("Try to delete not empty bucket and get error"): with reporter.step("Try to delete not empty bucket and get error"):
with pytest.raises(Exception, match=r".*The bucket you tried to delete is not empty.*"): with pytest.raises(Exception, match=r".*The bucket you tried to delete is not empty.*"):
s3_client.delete_bucket(bucket_1) s3_client.delete_bucket(bucket_1)
s3_client.head_bucket(bucket_1) s3_client.head_bucket(bucket_1)
with allure.step(f"Delete empty bucket {bucket_2}"): with reporter.step(f"Delete empty bucket {bucket_2}"):
s3_client.delete_bucket(bucket_2) s3_client.delete_bucket(bucket_2)
tick_epoch(client_shell, cluster) tick_epoch(client_shell, cluster)
with allure.step(f"Check bucket {bucket_2} deleted"): with reporter.step(f"Check bucket {bucket_2} deleted"):
with pytest.raises(Exception, match=r".*Not Found.*"): with pytest.raises(Exception, match=r".*Not Found.*"):
s3_client.head_bucket(bucket_2) s3_client.head_bucket(bucket_2)
@ -86,15 +87,15 @@ class TestS3Gate:
assert bucket_1 in buckets, f"Expected bucket {bucket_1} is in the list" assert bucket_1 in buckets, f"Expected bucket {bucket_1} is in the list"
assert bucket_2 not in buckets, f"Expected bucket {bucket_2} is not in the list" assert bucket_2 not in buckets, f"Expected bucket {bucket_2} is not in the list"
with allure.step(f"Delete object from {bucket_1}"): with reporter.step(f"Delete object from {bucket_1}"):
s3_client.delete_object(bucket_1, file_name, version_id) s3_client.delete_object(bucket_1, file_name, version_id)
s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=[]) s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=[])
with allure.step(f"Delete bucket {bucket_1}"): with reporter.step(f"Delete bucket {bucket_1}"):
s3_client.delete_bucket(bucket_1) s3_client.delete_bucket(bucket_1)
tick_epoch(client_shell, cluster) tick_epoch(client_shell, cluster)
with allure.step(f"Check bucket {bucket_1} deleted"): with reporter.step(f"Check bucket {bucket_1} deleted"):
with pytest.raises(Exception, match=r".*Not Found.*"): with pytest.raises(Exception, match=r".*Not Found.*"):
s3_client.head_bucket(bucket_1) s3_client.head_bucket(bucket_1)
@ -119,7 +120,7 @@ class TestS3Gate:
bucket_1, bucket_2 = two_buckets bucket_1, bucket_2 = two_buckets
for bucket in (bucket_1, bucket_2): for bucket in (bucket_1, bucket_2):
with allure.step("Bucket must be empty"): with reporter.step("Bucket must be empty"):
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
@ -129,7 +130,7 @@ class TestS3Gate:
bucket_objects = s3_client.list_objects(bucket) bucket_objects = s3_client.list_objects(bucket)
assert file_name in bucket_objects, f"Expected file {file_name} in objects list {bucket_objects}" assert file_name in bucket_objects, f"Expected file {file_name} in objects list {bucket_objects}"
with allure.step("Check object's attributes"): with reporter.step("Check object's attributes"):
for attrs in (["ETag"], ["ObjectSize", "StorageClass"]): for attrs in (["ETag"], ["ObjectSize", "StorageClass"]):
s3_client.get_object_attributes(bucket, file_name, attrs) s3_client.get_object_attributes(bucket, file_name, attrs)
@ -148,10 +149,10 @@ class TestS3Gate:
s3_client.sync(bucket=bucket, dir_path=os.path.dirname(file_path_1)) s3_client.sync(bucket=bucket, dir_path=os.path.dirname(file_path_1))
with allure.step("Check objects are synced"): with reporter.step("Check objects are synced"):
objects = s3_client.list_objects(bucket) objects = s3_client.list_objects(bucket)
with allure.step("Check these are the same objects"): with reporter.step("Check these are the same objects"):
assert set(key_to_path.keys()) == set(objects), f"Expected all objects saved. Got {objects}" assert set(key_to_path.keys()) == set(objects), f"Expected all objects saved. Got {objects}"
for obj_key in objects: for obj_key in objects:
got_object = s3_client.get_object(bucket, obj_key) got_object = s3_client.get_object(bucket, obj_key)
@ -170,12 +171,12 @@ class TestS3Gate:
obj_key = os.path.basename(file_name_simple) obj_key = os.path.basename(file_name_simple)
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put several versions of object into bucket"): with reporter.step("Put several versions of object into bucket"):
version_id_1 = s3_client.put_object(bucket, file_name_simple) version_id_1 = s3_client.put_object(bucket, file_name_simple)
generate_file_with_content(simple_object_size.value, file_path=file_name_simple, content=version_2_content) generate_file_with_content(simple_object_size.value, file_path=file_name_simple, content=version_2_content)
version_id_2 = s3_client.put_object(bucket, file_name_simple) version_id_2 = s3_client.put_object(bucket, file_name_simple)
with allure.step("Check bucket shows all versions"): with reporter.step("Check bucket shows all versions"):
versions = s3_client.list_objects_versions(bucket) versions = s3_client.list_objects_versions(bucket)
obj_versions = {version.get("VersionId") for version in versions if version.get("Key") == obj_key} obj_versions = {version.get("VersionId") for version in versions if version.get("Key") == obj_key}
assert obj_versions == { assert obj_versions == {
@ -183,7 +184,7 @@ class TestS3Gate:
version_id_2, version_id_2,
}, f"Expected object has versions: {version_id_1, version_id_2}" }, f"Expected object has versions: {version_id_1, version_id_2}"
with allure.step("Show information about particular version"): with reporter.step("Show information about particular version"):
for version_id in (version_id_1, version_id_2): for version_id in (version_id_1, version_id_2):
response = s3_client.head_object(bucket, obj_key, version_id=version_id) response = s3_client.head_object(bucket, obj_key, version_id=version_id)
assert "LastModified" in response, "Expected LastModified field" assert "LastModified" in response, "Expected LastModified field"
@ -191,20 +192,20 @@ class TestS3Gate:
assert response.get("VersionId") == version_id, f"Expected VersionId is {version_id}" assert response.get("VersionId") == version_id, f"Expected VersionId is {version_id}"
assert response.get("ContentLength") != 0, "Expected ContentLength is not zero" assert response.get("ContentLength") != 0, "Expected ContentLength is not zero"
with allure.step("Check object's attributes"): with reporter.step("Check object's attributes"):
for version_id in (version_id_1, version_id_2): for version_id in (version_id_1, version_id_2):
got_attrs = s3_client.get_object_attributes(bucket, obj_key, ["ETag"], version_id=version_id) got_attrs = s3_client.get_object_attributes(bucket, obj_key, ["ETag"], version_id=version_id)
if got_attrs: if got_attrs:
assert got_attrs.get("VersionId") == version_id, f"Expected VersionId is {version_id}" assert got_attrs.get("VersionId") == version_id, f"Expected VersionId is {version_id}"
with allure.step("Delete object and check it was deleted"): with reporter.step("Delete object and check it was deleted"):
response = s3_client.delete_object(bucket, obj_key) response = s3_client.delete_object(bucket, obj_key)
version_id_delete = response.get("VersionId") version_id_delete = response.get("VersionId")
with pytest.raises(Exception, match=r".*Not Found.*"): with pytest.raises(Exception, match=r".*Not Found.*"):
s3_client.head_object(bucket, obj_key) s3_client.head_object(bucket, obj_key)
with allure.step("Get content for all versions and check it is correct"): with reporter.step("Get content for all versions and check it is correct"):
for version, content in ( for version, content in (
(version_id_2, version_2_content), (version_id_2, version_2_content),
(version_id_1, version_1_content), (version_id_1, version_1_content),
@ -213,7 +214,7 @@ class TestS3Gate:
got_content = get_file_content(file_name) got_content = get_file_content(file_name)
assert got_content == content, f"Expected object content is\n{content}\nGot\n{got_content}" assert got_content == content, f"Expected object content is\n{content}\nGot\n{got_content}"
with allure.step("Restore previous object version"): with reporter.step("Restore previous object version"):
s3_client.delete_object(bucket, obj_key, version_id=version_id_delete) s3_client.delete_object(bucket, obj_key, version_id=version_id_delete)
file_name = s3_client.get_object(bucket, obj_key) file_name = s3_client.get_object(bucket, obj_key)
@ -238,7 +239,7 @@ class TestS3Gate:
uploads = s3_client.list_multipart_uploads(bucket) uploads = s3_client.list_multipart_uploads(bucket)
assert not uploads, f"Expected there is no uploads in bucket {bucket}" assert not uploads, f"Expected there is no uploads in bucket {bucket}"
with allure.step("Create and abort multipart upload"): with reporter.step("Create and abort multipart upload"):
upload_id = s3_client.create_multipart_upload(bucket, object_key) upload_id = s3_client.create_multipart_upload(bucket, object_key)
uploads = s3_client.list_multipart_uploads(bucket) uploads = s3_client.list_multipart_uploads(bucket)
assert uploads, f"Expected there one upload in bucket {bucket}" assert uploads, f"Expected there one upload in bucket {bucket}"
@ -249,13 +250,13 @@ class TestS3Gate:
uploads = s3_client.list_multipart_uploads(bucket) uploads = s3_client.list_multipart_uploads(bucket)
assert not uploads, f"Expected there is no uploads in bucket {bucket}" assert not uploads, f"Expected there is no uploads in bucket {bucket}"
with allure.step("Create new multipart upload and upload several parts"): with reporter.step("Create new multipart upload and upload several parts"):
upload_id = s3_client.create_multipart_upload(bucket, object_key) upload_id = s3_client.create_multipart_upload(bucket, object_key)
for part_id, file_path in enumerate(part_files, start=1): for part_id, file_path in enumerate(part_files, start=1):
etag = s3_client.upload_part(bucket, object_key, upload_id, part_id, file_path) etag = s3_client.upload_part(bucket, object_key, upload_id, part_id, file_path)
parts.append((part_id, etag)) parts.append((part_id, etag))
with allure.step("Check all parts are visible in bucket"): with reporter.step("Check all parts are visible in bucket"):
got_parts = s3_client.list_parts(bucket, object_key, upload_id) got_parts = s3_client.list_parts(bucket, object_key, upload_id)
assert len(got_parts) == len(part_files), f"Expected {parts_count} parts, got\n{got_parts}" assert len(got_parts) == len(part_files), f"Expected {parts_count} parts, got\n{got_parts}"
@ -264,7 +265,7 @@ class TestS3Gate:
uploads = s3_client.list_multipart_uploads(bucket) uploads = s3_client.list_multipart_uploads(bucket)
assert not uploads, f"Expected there is no uploads in bucket {bucket}" assert not uploads, f"Expected there is no uploads in bucket {bucket}"
with allure.step("Check we can get whole object from bucket"): with reporter.step("Check we can get whole object from bucket"):
got_object = s3_client.get_object(bucket, object_key) got_object = s3_client.get_object(bucket, object_key)
assert get_file_hash(got_object) == get_file_hash(file_name_large) assert get_file_hash(got_object) == get_file_hash(file_name_large)
@ -333,12 +334,12 @@ class TestS3Gate:
bucket_1, bucket_2 = two_buckets bucket_1, bucket_2 = two_buckets
with allure.step(f"Generate {max_obj_count} files"): with reporter.step(f"Generate {max_obj_count} files"):
for _ in range(max_obj_count): for _ in range(max_obj_count):
file_paths.append(generate_file(choice(obj_sizes).value)) file_paths.append(generate_file(choice(obj_sizes).value))
for bucket in (bucket_1, bucket_2): for bucket in (bucket_1, bucket_2):
with allure.step(f"Bucket {bucket} must be empty as it just created"): with reporter.step(f"Bucket {bucket} must be empty as it just created"):
objects_list = s3_client.list_objects_v2(bucket) objects_list = s3_client.list_objects_v2(bucket)
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
@ -346,18 +347,18 @@ class TestS3Gate:
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
put_objects.append(s3_helper.object_key_from_file_path(file_path)) put_objects.append(s3_helper.object_key_from_file_path(file_path))
with allure.step(f"Check all objects put in bucket {bucket} successfully"): with reporter.step(f"Check all objects put in bucket {bucket} successfully"):
bucket_objects = s3_client.list_objects_v2(bucket) bucket_objects = s3_client.list_objects_v2(bucket)
assert set(put_objects) == set( assert set(put_objects) == set(
bucket_objects bucket_objects
), f"Expected all objects {put_objects} in objects list {bucket_objects}" ), f"Expected all objects {put_objects} in objects list {bucket_objects}"
with allure.step("Delete some objects from bucket_1 one by one"): with reporter.step("Delete some objects from bucket_1 one by one"):
objects_to_delete_b1 = choices(put_objects, k=max_delete_objects) objects_to_delete_b1 = choices(put_objects, k=max_delete_objects)
for obj in objects_to_delete_b1: for obj in objects_to_delete_b1:
s3_client.delete_object(bucket_1, obj) s3_client.delete_object(bucket_1, obj)
with allure.step("Check deleted objects are not visible in bucket bucket_1"): with reporter.step("Check deleted objects are not visible in bucket bucket_1"):
bucket_objects = s3_client.list_objects_v2(bucket_1) bucket_objects = s3_client.list_objects_v2(bucket_1)
assert set(put_objects).difference(set(objects_to_delete_b1)) == set( assert set(put_objects).difference(set(objects_to_delete_b1)) == set(
bucket_objects bucket_objects
@ -366,11 +367,11 @@ class TestS3Gate:
with pytest.raises(Exception, match="The specified key does not exist"): with pytest.raises(Exception, match="The specified key does not exist"):
s3_client.get_object(bucket_1, object_key) s3_client.get_object(bucket_1, object_key)
with allure.step("Delete some objects from bucket_2 at once"): with reporter.step("Delete some objects from bucket_2 at once"):
objects_to_delete_b2 = choices(put_objects, k=max_delete_objects) objects_to_delete_b2 = choices(put_objects, k=max_delete_objects)
s3_client.delete_objects(bucket_2, objects_to_delete_b2) s3_client.delete_objects(bucket_2, objects_to_delete_b2)
with allure.step("Check deleted objects are not visible in bucket bucket_2"): with reporter.step("Check deleted objects are not visible in bucket bucket_2"):
objects_list = s3_client.list_objects_v2(bucket_2) objects_list = s3_client.list_objects_v2(bucket_2)
assert set(put_objects).difference(set(objects_to_delete_b2)) == set( assert set(put_objects).difference(set(objects_to_delete_b2)) == set(
objects_list objects_list
@ -397,25 +398,25 @@ class TestS3Gate:
file_name_large = s3_helper.object_key_from_file_path(file_path_large) file_name_large = s3_helper.object_key_from_file_path(file_path_large)
bucket_objects = [file_name_simple, file_name_large] bucket_objects = [file_name_simple, file_name_large]
with allure.step("Bucket must be empty"): with reporter.step("Bucket must be empty"):
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
with allure.step("Put objects into bucket"): with reporter.step("Put objects into bucket"):
for file_path in (file_path_simple, file_path_large): for file_path in (file_path_simple, file_path_large):
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
with allure.step("Copy one object into the same bucket"): with reporter.step("Copy one object into the same bucket"):
copy_obj_path = s3_client.copy_object(bucket, file_name_simple) copy_obj_path = s3_client.copy_object(bucket, file_name_simple)
bucket_objects.append(copy_obj_path) bucket_objects.append(copy_obj_path)
s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_objects) s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_objects)
with allure.step("Check copied object has the same content"): with reporter.step("Check copied object has the same content"):
got_copied_file = s3_client.get_object(bucket, copy_obj_path) got_copied_file = s3_client.get_object(bucket, copy_obj_path)
assert get_file_hash(file_path_simple) == get_file_hash(got_copied_file), "Hashes must be the same" assert get_file_hash(file_path_simple) == get_file_hash(got_copied_file), "Hashes must be the same"
with allure.step("Delete one object from bucket"): with reporter.step("Delete one object from bucket"):
s3_client.delete_object(bucket, file_name_simple) s3_client.delete_object(bucket, file_name_simple)
bucket_objects.remove(file_name_simple) bucket_objects.remove(file_name_simple)
@ -446,32 +447,32 @@ class TestS3Gate:
bucket_1, bucket_2 = two_buckets bucket_1, bucket_2 = two_buckets
with allure.step("Buckets must be empty"): with reporter.step("Buckets must be empty"):
for bucket in (bucket_1, bucket_2): for bucket in (bucket_1, bucket_2):
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
with allure.step("Put objects into one bucket"): with reporter.step("Put objects into one bucket"):
for file_path in (file_path_simple, file_path_large): for file_path in (file_path_simple, file_path_large):
s3_client.put_object(bucket_1, file_path) s3_client.put_object(bucket_1, file_path)
with allure.step("Copy object from first bucket into second"): with reporter.step("Copy object from first bucket into second"):
copy_obj_path_b2 = s3_client.copy_object(bucket_1, file_name_large, bucket=bucket_2) copy_obj_path_b2 = s3_client.copy_object(bucket_1, file_name_large, bucket=bucket_2)
s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects) s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects)
s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[copy_obj_path_b2]) s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[copy_obj_path_b2])
with allure.step("Check copied object has the same content"): with reporter.step("Check copied object has the same content"):
got_copied_file_b2 = s3_client.get_object(bucket_2, copy_obj_path_b2) got_copied_file_b2 = s3_client.get_object(bucket_2, copy_obj_path_b2)
assert get_file_hash(file_path_large) == get_file_hash(got_copied_file_b2), "Hashes must be the same" assert get_file_hash(file_path_large) == get_file_hash(got_copied_file_b2), "Hashes must be the same"
with allure.step("Delete one object from first bucket"): with reporter.step("Delete one object from first bucket"):
s3_client.delete_object(bucket_1, file_name_simple) s3_client.delete_object(bucket_1, file_name_simple)
bucket_1_objects.remove(file_name_simple) bucket_1_objects.remove(file_name_simple)
s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects) s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects)
s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[copy_obj_path_b2]) s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[copy_obj_path_b2])
with allure.step("Delete one object from second bucket and check it is empty"): with reporter.step("Delete one object from second bucket and check it is empty"):
s3_client.delete_object(bucket_2, copy_obj_path_b2) s3_client.delete_object(bucket_2, copy_obj_path_b2)
s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[]) s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[])
@ -480,12 +481,12 @@ class TestS3Gate:
logger.warning("Attributes check is not supported for boto3 implementation") logger.warning("Attributes check is not supported for boto3 implementation")
return return
with allure.step("Check object's attributes"): with reporter.step("Check object's attributes"):
obj_parts = s3_client.get_object_attributes(bucket, object_key, ["ObjectParts"], full_output=False) obj_parts = s3_client.get_object_attributes(bucket, object_key, ["ObjectParts"], full_output=False)
assert obj_parts.get("TotalPartsCount") == parts_count, f"Expected TotalPartsCount is {parts_count}" assert obj_parts.get("TotalPartsCount") == parts_count, f"Expected TotalPartsCount is {parts_count}"
assert len(obj_parts.get("Parts")) == parts_count, f"Expected Parts cunt is {parts_count}" assert len(obj_parts.get("Parts")) == parts_count, f"Expected Parts cunt is {parts_count}"
with allure.step("Check object's attribute max-parts"): with reporter.step("Check object's attribute max-parts"):
max_parts = 2 max_parts = 2
obj_parts = s3_client.get_object_attributes( obj_parts = s3_client.get_object_attributes(
bucket, bucket,
@ -498,7 +499,7 @@ class TestS3Gate:
assert obj_parts.get("MaxParts") == max_parts, f"Expected MaxParts is {parts_count}" assert obj_parts.get("MaxParts") == max_parts, f"Expected MaxParts is {parts_count}"
assert len(obj_parts.get("Parts")) == max_parts, f"Expected Parts count is {parts_count}" assert len(obj_parts.get("Parts")) == max_parts, f"Expected Parts count is {parts_count}"
with allure.step("Check object's attribute part-number-marker"): with reporter.step("Check object's attribute part-number-marker"):
part_number_marker = 3 part_number_marker = 3
obj_parts = s3_client.get_object_attributes( obj_parts = s3_client.get_object_attributes(
bucket, bucket,

View file

@ -3,6 +3,7 @@ from datetime import datetime, timedelta
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.s3 import S3ClientWrapper from frostfs_testlib.s3 import S3ClientWrapper
from frostfs_testlib.steps.s3 import s3_helper from frostfs_testlib.steps.s3 import s3_helper
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
@ -21,7 +22,7 @@ class TestS3GateLocking:
bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True) bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True)
with allure.step("Put several versions of object into bucket"): with reporter.step("Put several versions of object into bucket"):
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
file_name_1 = generate_file_with_content(simple_object_size.value, file_path=file_path) file_name_1 = generate_file_with_content(simple_object_size.value, file_path=file_path)
version_id_2 = s3_client.put_object(bucket, file_name_1) version_id_2 = s3_client.put_object(bucket, file_name_1)
@ -29,7 +30,7 @@ class TestS3GateLocking:
if version_id: if version_id:
version_id = version_id_2 version_id = version_id_2
with allure.step(f"Put retention period {retention_period}min to object {file_name}"): with reporter.step(f"Put retention period {retention_period}min to object {file_name}"):
date_obj = datetime.utcnow() + timedelta(minutes=retention_period) date_obj = datetime.utcnow() + timedelta(minutes=retention_period)
retention = { retention = {
"Mode": "COMPLIANCE", "Mode": "COMPLIANCE",
@ -38,21 +39,21 @@ class TestS3GateLocking:
s3_client.put_object_retention(bucket, file_name, retention, version_id) s3_client.put_object_retention(bucket, file_name, retention, version_id)
s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "OFF") s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "OFF")
with allure.step(f"Put legal hold to object {file_name}"): with reporter.step(f"Put legal hold to object {file_name}"):
s3_client.put_object_legal_hold(bucket, file_name, "ON", version_id) s3_client.put_object_legal_hold(bucket, file_name, "ON", version_id)
s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "ON") s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "ON")
with allure.step("Fail with deleting object with legal hold and retention period"): with reporter.step("Fail with deleting object with legal hold and retention period"):
if version_id: if version_id:
with pytest.raises(Exception): with pytest.raises(Exception):
# An error occurred (AccessDenied) when calling the DeleteObject operation (reached max retries: 0): Access Denied. # An error occurred (AccessDenied) when calling the DeleteObject operation (reached max retries: 0): Access Denied.
s3_client.delete_object(bucket, file_name, version_id) s3_client.delete_object(bucket, file_name, version_id)
with allure.step("Check retention period is no longer set on the uploaded object"): with reporter.step("Check retention period is no longer set on the uploaded object"):
time.sleep((retention_period + 1) * 60) time.sleep((retention_period + 1) * 60)
s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "ON") s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "ON")
with allure.step("Fail with deleting object with legal hold and retention period"): with reporter.step("Fail with deleting object with legal hold and retention period"):
if version_id: if version_id:
with pytest.raises(Exception): with pytest.raises(Exception):
# An error occurred (AccessDenied) when calling the DeleteObject operation (reached max retries: 0): Access Denied. # An error occurred (AccessDenied) when calling the DeleteObject operation (reached max retries: 0): Access Denied.
@ -69,13 +70,13 @@ class TestS3GateLocking:
bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True) bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True)
with allure.step("Put object into bucket"): with reporter.step("Put object into bucket"):
obj_version = s3_client.put_object(bucket, file_path) obj_version = s3_client.put_object(bucket, file_path)
if version_id: if version_id:
version_id = obj_version version_id = obj_version
s3_helper.check_objects_in_bucket(s3_client, bucket, [file_name]) s3_helper.check_objects_in_bucket(s3_client, bucket, [file_name])
with allure.step(f"Put retention period {retention_period}min to object {file_name}"): with reporter.step(f"Put retention period {retention_period}min to object {file_name}"):
date_obj = datetime.utcnow() + timedelta(minutes=retention_period) date_obj = datetime.utcnow() + timedelta(minutes=retention_period)
retention = { retention = {
"Mode": "COMPLIANCE", "Mode": "COMPLIANCE",
@ -84,7 +85,7 @@ class TestS3GateLocking:
s3_client.put_object_retention(bucket, file_name, retention, version_id) s3_client.put_object_retention(bucket, file_name, retention, version_id)
s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "OFF") s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "OFF")
with allure.step(f"Try to change retention period {retention_period_1}min to object {file_name}"): with reporter.step(f"Try to change retention period {retention_period_1}min to object {file_name}"):
date_obj = datetime.utcnow() + timedelta(minutes=retention_period_1) date_obj = datetime.utcnow() + timedelta(minutes=retention_period_1)
retention = { retention = {
"Mode": "COMPLIANCE", "Mode": "COMPLIANCE",
@ -103,13 +104,13 @@ class TestS3GateLocking:
bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True) bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True)
with allure.step("Put object into bucket"): with reporter.step("Put object into bucket"):
obj_version = s3_client.put_object(bucket, file_path) obj_version = s3_client.put_object(bucket, file_path)
if version_id: if version_id:
version_id = obj_version version_id = obj_version
s3_helper.check_objects_in_bucket(s3_client, bucket, [file_name]) s3_helper.check_objects_in_bucket(s3_client, bucket, [file_name])
with allure.step(f"Put retention period {retention_period}min to object {file_name}"): with reporter.step(f"Put retention period {retention_period}min to object {file_name}"):
date_obj = datetime.utcnow() + timedelta(minutes=retention_period) date_obj = datetime.utcnow() + timedelta(minutes=retention_period)
retention = { retention = {
"Mode": "GOVERNANCE", "Mode": "GOVERNANCE",
@ -118,7 +119,7 @@ class TestS3GateLocking:
s3_client.put_object_retention(bucket, file_name, retention, version_id) s3_client.put_object_retention(bucket, file_name, retention, version_id)
s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "GOVERNANCE", date_obj, "OFF") s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "GOVERNANCE", date_obj, "OFF")
with allure.step(f"Try to change retention period {retention_period_1}min to object {file_name}"): with reporter.step(f"Try to change retention period {retention_period_1}min to object {file_name}"):
date_obj = datetime.utcnow() + timedelta(minutes=retention_period_1) date_obj = datetime.utcnow() + timedelta(minutes=retention_period_1)
retention = { retention = {
"Mode": "GOVERNANCE", "Mode": "GOVERNANCE",
@ -127,7 +128,7 @@ class TestS3GateLocking:
with pytest.raises(Exception): with pytest.raises(Exception):
s3_client.put_object_retention(bucket, file_name, retention, version_id) s3_client.put_object_retention(bucket, file_name, retention, version_id)
with allure.step(f"Try to change retention period {retention_period_1}min to object {file_name}"): with reporter.step(f"Try to change retention period {retention_period_1}min to object {file_name}"):
date_obj = datetime.utcnow() + timedelta(minutes=retention_period_1) date_obj = datetime.utcnow() + timedelta(minutes=retention_period_1)
retention = { retention = {
"Mode": "GOVERNANCE", "Mode": "GOVERNANCE",
@ -136,7 +137,7 @@ class TestS3GateLocking:
with pytest.raises(Exception): with pytest.raises(Exception):
s3_client.put_object_retention(bucket, file_name, retention, version_id) s3_client.put_object_retention(bucket, file_name, retention, version_id)
with allure.step(f"Put new retention period {retention_period_2}min to object {file_name}"): with reporter.step(f"Put new retention period {retention_period_2}min to object {file_name}"):
date_obj = datetime.utcnow() + timedelta(minutes=retention_period_2) date_obj = datetime.utcnow() + timedelta(minutes=retention_period_2)
retention = { retention = {
"Mode": "GOVERNANCE", "Mode": "GOVERNANCE",
@ -154,13 +155,13 @@ class TestS3GateLocking:
bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=False) bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=False)
with allure.step("Put object into bucket"): with reporter.step("Put object into bucket"):
obj_version = s3_client.put_object(bucket, file_path) obj_version = s3_client.put_object(bucket, file_path)
if version_id: if version_id:
version_id = obj_version version_id = obj_version
s3_helper.check_objects_in_bucket(s3_client, bucket, [file_name]) s3_helper.check_objects_in_bucket(s3_client, bucket, [file_name])
with allure.step(f"Put legal hold to object {file_name}"): with reporter.step(f"Put legal hold to object {file_name}"):
with pytest.raises(Exception): with pytest.raises(Exception):
s3_client.put_object_legal_hold(bucket, file_name, "ON", version_id) s3_client.put_object_legal_hold(bucket, file_name, "ON", version_id)
@ -175,18 +176,18 @@ class TestS3GateLockingBucket:
bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True) bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True)
with allure.step("PutObjectLockConfiguration with ObjectLockEnabled=False"): with reporter.step("PutObjectLockConfiguration with ObjectLockEnabled=False"):
s3_client.put_object_lock_configuration(bucket, configuration) s3_client.put_object_lock_configuration(bucket, configuration)
with allure.step("PutObjectLockConfiguration with ObjectLockEnabled=True"): with reporter.step("PutObjectLockConfiguration with ObjectLockEnabled=True"):
configuration["ObjectLockEnabled"] = "Enabled" configuration["ObjectLockEnabled"] = "Enabled"
s3_client.put_object_lock_configuration(bucket, configuration) s3_client.put_object_lock_configuration(bucket, configuration)
with allure.step("GetObjectLockConfiguration"): with reporter.step("GetObjectLockConfiguration"):
config = s3_client.get_object_lock_configuration(bucket) config = s3_client.get_object_lock_configuration(bucket)
configuration["Rule"]["DefaultRetention"]["Years"] = 0 configuration["Rule"]["DefaultRetention"]["Years"] = 0
assert config == configuration, f"Configurations must be equal {configuration}" assert config == configuration, f"Configurations must be equal {configuration}"
with allure.step("Put object into bucket"): with reporter.step("Put object into bucket"):
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", None, "OFF", 1) s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", None, "OFF", 1)

View file

@ -1,5 +1,6 @@
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.s3 import S3ClientWrapper, VersioningStatus from frostfs_testlib.s3 import S3ClientWrapper, VersioningStatus
from frostfs_testlib.steps.cli.container import list_objects, search_container_by_name from frostfs_testlib.steps.cli.container import list_objects, search_container_by_name
from frostfs_testlib.steps.s3 import s3_helper from frostfs_testlib.steps.s3 import s3_helper
@ -24,7 +25,7 @@ class TestS3GateMultipart(ClusterTestBase):
part_files = split_file(file_name_large, parts_count) part_files = split_file(file_name_large, parts_count)
parts = [] parts = []
with allure.step("Upload first part"): with reporter.step("Upload first part"):
upload_id = s3_client.create_multipart_upload(bucket, object_key) upload_id = s3_client.create_multipart_upload(bucket, object_key)
uploads = s3_client.list_multipart_uploads(bucket) uploads = s3_client.list_multipart_uploads(bucket)
etag = s3_client.upload_part(bucket, object_key, upload_id, 1, part_files[0]) etag = s3_client.upload_part(bucket, object_key, upload_id, 1, part_files[0])
@ -32,7 +33,7 @@ class TestS3GateMultipart(ClusterTestBase):
got_parts = s3_client.list_parts(bucket, object_key, upload_id) got_parts = s3_client.list_parts(bucket, object_key, upload_id)
assert len(got_parts) == 1, f"Expected {1} parts, got\n{got_parts}" assert len(got_parts) == 1, f"Expected {1} parts, got\n{got_parts}"
with allure.step("Upload last parts"): with reporter.step("Upload last parts"):
for part_id, file_path in enumerate(part_files[1:], start=2): for part_id, file_path in enumerate(part_files[1:], start=2):
etag = s3_client.upload_part(bucket, object_key, upload_id, part_id, file_path) etag = s3_client.upload_part(bucket, object_key, upload_id, part_id, file_path)
parts.append((part_id, etag)) parts.append((part_id, etag))
@ -40,11 +41,11 @@ class TestS3GateMultipart(ClusterTestBase):
s3_client.complete_multipart_upload(bucket, object_key, upload_id, parts) s3_client.complete_multipart_upload(bucket, object_key, upload_id, parts)
assert len(got_parts) == len(part_files), f"Expected {parts_count} parts, got\n{got_parts}" assert len(got_parts) == len(part_files), f"Expected {parts_count} parts, got\n{got_parts}"
with allure.step("Check upload list is empty"): with reporter.step("Check upload list is empty"):
uploads = s3_client.list_multipart_uploads(bucket) uploads = s3_client.list_multipart_uploads(bucket)
assert not uploads, f"Expected there is no uploads in bucket {bucket}" assert not uploads, f"Expected there is no uploads in bucket {bucket}"
with allure.step("Check we can get whole object from bucket"): with reporter.step("Check we can get whole object from bucket"):
got_object = s3_client.get_object(bucket, object_key) got_object = s3_client.get_object(bucket, object_key)
assert get_file_hash(got_object) == get_file_hash(file_name_large) assert get_file_hash(got_object) == get_file_hash(file_name_large)
@ -64,36 +65,36 @@ class TestS3GateMultipart(ClusterTestBase):
files_count = len(to_upload) files_count = len(to_upload)
upload_key = "multipart_abort" upload_key = "multipart_abort"
with allure.step(f"Get related container_id for bucket '{bucket}'"): with reporter.step(f"Get related container_id for bucket '{bucket}'"):
container_id = search_container_by_name( container_id = search_container_by_name(
default_wallet, bucket, self.shell, self.cluster.default_rpc_endpoint default_wallet, bucket, self.shell, self.cluster.default_rpc_endpoint
) )
with allure.step("Create multipart upload"): with reporter.step("Create multipart upload"):
upload_id = s3_client.create_multipart_upload(bucket, upload_key) upload_id = s3_client.create_multipart_upload(bucket, upload_key)
with allure.step(f"Upload {files_count} files to multipart upload"): with reporter.step(f"Upload {files_count} files to multipart upload"):
for i, file in enumerate(to_upload, 1): for i, file in enumerate(to_upload, 1):
s3_client.upload_part(bucket, upload_key, upload_id, i, file) s3_client.upload_part(bucket, upload_key, upload_id, i, file)
with allure.step(f"Check that we have {files_count} files in bucket"): with reporter.step(f"Check that we have {files_count} files in bucket"):
parts = s3_client.list_parts(bucket, upload_key, upload_id) parts = s3_client.list_parts(bucket, upload_key, upload_id)
assert len(parts) == files_count, f"Expected {files_count} parts, got\n{parts}" assert len(parts) == files_count, f"Expected {files_count} parts, got\n{parts}"
with allure.step(f"Check that we have {files_count} files in container '{container_id}'"): with reporter.step(f"Check that we have {files_count} files in container '{container_id}'"):
objects = list_objects(default_wallet, self.shell, container_id, self.cluster.default_rpc_endpoint) objects = list_objects(default_wallet, self.shell, container_id, self.cluster.default_rpc_endpoint)
assert len(objects) == files_count, f"Expected {files_count} objects in container, got\n{objects}" assert len(objects) == files_count, f"Expected {files_count} objects in container, got\n{objects}"
with allure.step("Abort multipart upload"): with reporter.step("Abort multipart upload"):
s3_client.abort_multipart_upload(bucket, upload_key, upload_id) s3_client.abort_multipart_upload(bucket, upload_key, upload_id)
uploads = s3_client.list_multipart_uploads(bucket) uploads = s3_client.list_multipart_uploads(bucket)
assert not uploads, f"Expected no uploads in bucket {bucket}" assert not uploads, f"Expected no uploads in bucket {bucket}"
with allure.step("Check that we have no files in bucket since upload was aborted"): with reporter.step("Check that we have no files in bucket since upload was aborted"):
with pytest.raises(Exception, match=self.NO_SUCH_UPLOAD): with pytest.raises(Exception, match=self.NO_SUCH_UPLOAD):
s3_client.list_parts(bucket, upload_key, upload_id) s3_client.list_parts(bucket, upload_key, upload_id)
with allure.step("Check that we have no files in container since upload was aborted"): with reporter.step("Check that we have no files in container since upload was aborted"):
objects = list_objects(default_wallet, self.shell, container_id, self.cluster.default_rpc_endpoint) objects = list_objects(default_wallet, self.shell, container_id, self.cluster.default_rpc_endpoint)
assert len(objects) == 0, f"Expected no objects in container, got\n{objects}" assert len(objects) == 0, f"Expected no objects in container, got\n{objects}"
@ -107,27 +108,27 @@ class TestS3GateMultipart(ClusterTestBase):
parts = [] parts = []
objs = [] objs = []
with allure.step(f"Put {parts_count} objects in bucket"): with reporter.step(f"Put {parts_count} objects in bucket"):
for part in part_files: for part in part_files:
s3_client.put_object(bucket, part) s3_client.put_object(bucket, part)
objs.append(s3_helper.object_key_from_file_path(part)) objs.append(s3_helper.object_key_from_file_path(part))
s3_helper.check_objects_in_bucket(s3_client, bucket, objs) s3_helper.check_objects_in_bucket(s3_client, bucket, objs)
with allure.step("Create multipart upload object"): with reporter.step("Create multipart upload object"):
upload_id = s3_client.create_multipart_upload(bucket, object_key) upload_id = s3_client.create_multipart_upload(bucket, object_key)
uploads = s3_client.list_multipart_uploads(bucket) uploads = s3_client.list_multipart_uploads(bucket)
assert uploads, f"Expected there are uploads in bucket {bucket}" assert uploads, f"Expected there are uploads in bucket {bucket}"
with allure.step("Upload parts to multipart upload"): with reporter.step("Upload parts to multipart upload"):
for part_id, obj_key in enumerate(objs, start=1): for part_id, obj_key in enumerate(objs, start=1):
etag = s3_client.upload_part_copy(bucket, object_key, upload_id, part_id, f"{bucket}/{obj_key}") etag = s3_client.upload_part_copy(bucket, object_key, upload_id, part_id, f"{bucket}/{obj_key}")
parts.append((part_id, etag)) parts.append((part_id, etag))
got_parts = s3_client.list_parts(bucket, object_key, upload_id) got_parts = s3_client.list_parts(bucket, object_key, upload_id)
with allure.step("Complete multipart upload"): with reporter.step("Complete multipart upload"):
s3_client.complete_multipart_upload(bucket, object_key, upload_id, parts) s3_client.complete_multipart_upload(bucket, object_key, upload_id, parts)
assert len(got_parts) == len(part_files), f"Expected {parts_count} parts, got\n{got_parts}" assert len(got_parts) == len(part_files), f"Expected {parts_count} parts, got\n{got_parts}"
with allure.step("Check we can get whole object from bucket"): with reporter.step("Check we can get whole object from bucket"):
got_object = s3_client.get_object(bucket, object_key) got_object = s3_client.get_object(bucket, object_key)
assert get_file_hash(got_object) == get_file_hash(file_name_large) assert get_file_hash(got_object) == get_file_hash(file_name_large)

View file

@ -7,6 +7,7 @@ from typing import Literal
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.common import ASSETS_DIR, DEFAULT_WALLET_PASS from frostfs_testlib.resources.common import ASSETS_DIR, DEFAULT_WALLET_PASS
from frostfs_testlib.resources.error_patterns import S3_MALFORMED_XML_REQUEST from frostfs_testlib.resources.error_patterns import S3_MALFORMED_XML_REQUEST
from frostfs_testlib.s3 import AwsCliClient, S3ClientWrapper, VersioningStatus from frostfs_testlib.s3 import AwsCliClient, S3ClientWrapper, VersioningStatus
@ -43,10 +44,10 @@ class TestS3GateObject:
objects_list = s3_client.list_objects(bucket_1) objects_list = s3_client.list_objects(bucket_1)
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
with allure.step("Put object into one bucket"): with reporter.step("Put object into one bucket"):
s3_client.put_object(bucket_1, file_path) s3_client.put_object(bucket_1, file_path)
with allure.step("Copy one object into the same bucket"): with reporter.step("Copy one object into the same bucket"):
copy_obj_path = s3_client.copy_object(bucket_1, file_name) copy_obj_path = s3_client.copy_object(bucket_1, file_name)
bucket_1_objects.append(copy_obj_path) bucket_1_objects.append(copy_obj_path)
s3_helper.check_objects_in_bucket(s3_client, bucket_1, bucket_1_objects) s3_helper.check_objects_in_bucket(s3_client, bucket_1, bucket_1_objects)
@ -54,22 +55,22 @@ class TestS3GateObject:
objects_list = s3_client.list_objects(bucket_2) objects_list = s3_client.list_objects(bucket_2)
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
with allure.step("Copy object from first bucket into second"): with reporter.step("Copy object from first bucket into second"):
copy_obj_path_b2 = s3_client.copy_object(bucket_1, file_name, bucket=bucket_2) copy_obj_path_b2 = s3_client.copy_object(bucket_1, file_name, bucket=bucket_2)
s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects) s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects)
s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[copy_obj_path_b2]) s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[copy_obj_path_b2])
with allure.step("Check copied object has the same content"): with reporter.step("Check copied object has the same content"):
got_copied_file_b2 = s3_client.get_object(bucket_2, copy_obj_path_b2) got_copied_file_b2 = s3_client.get_object(bucket_2, copy_obj_path_b2)
assert get_file_hash(file_path) == get_file_hash(got_copied_file_b2), "Hashes must be the same" assert get_file_hash(file_path) == get_file_hash(got_copied_file_b2), "Hashes must be the same"
with allure.step("Delete one object from first bucket"): with reporter.step("Delete one object from first bucket"):
s3_client.delete_object(bucket_1, file_name) s3_client.delete_object(bucket_1, file_name)
bucket_1_objects.remove(file_name) bucket_1_objects.remove(file_name)
s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects) s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects)
s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[copy_obj_path_b2]) s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[copy_obj_path_b2])
with allure.step("Copy one object into the same bucket"): with reporter.step("Copy one object into the same bucket"):
with pytest.raises(Exception): with pytest.raises(Exception):
s3_client.copy_object(bucket_1, file_name) s3_client.copy_object(bucket_1, file_name)
@ -87,28 +88,28 @@ class TestS3GateObject:
bucket_1, bucket_2 = two_buckets bucket_1, bucket_2 = two_buckets
s3_helper.set_bucket_versioning(s3_client, bucket_1, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket_1, VersioningStatus.ENABLED)
with allure.step("Put object into bucket"): with reporter.step("Put object into bucket"):
s3_client.put_object(bucket_1, file_name_simple) s3_client.put_object(bucket_1, file_name_simple)
bucket_1_objects = [obj_key] bucket_1_objects = [obj_key]
s3_helper.check_objects_in_bucket(s3_client, bucket_1, [obj_key]) s3_helper.check_objects_in_bucket(s3_client, bucket_1, [obj_key])
with allure.step("Copy one object into the same bucket"): with reporter.step("Copy one object into the same bucket"):
copy_obj_path = s3_client.copy_object(bucket_1, obj_key) copy_obj_path = s3_client.copy_object(bucket_1, obj_key)
bucket_1_objects.append(copy_obj_path) bucket_1_objects.append(copy_obj_path)
s3_helper.check_objects_in_bucket(s3_client, bucket_1, bucket_1_objects) s3_helper.check_objects_in_bucket(s3_client, bucket_1, bucket_1_objects)
s3_helper.set_bucket_versioning(s3_client, bucket_2, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket_2, VersioningStatus.ENABLED)
with allure.step("Copy object from first bucket into second"): with reporter.step("Copy object from first bucket into second"):
copy_obj_path_b2 = s3_client.copy_object(bucket_1, obj_key, bucket=bucket_2) copy_obj_path_b2 = s3_client.copy_object(bucket_1, obj_key, bucket=bucket_2)
s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects) s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects)
s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[copy_obj_path_b2]) s3_helper.check_objects_in_bucket(s3_client, bucket_2, expected_objects=[copy_obj_path_b2])
with allure.step("Delete one object from first bucket and check object in bucket"): with reporter.step("Delete one object from first bucket and check object in bucket"):
s3_client.delete_object(bucket_1, obj_key) s3_client.delete_object(bucket_1, obj_key)
bucket_1_objects.remove(obj_key) bucket_1_objects.remove(obj_key)
s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects) s3_helper.check_objects_in_bucket(s3_client, bucket_1, expected_objects=bucket_1_objects)
with allure.step("Copy one object into the same bucket"): with reporter.step("Copy one object into the same bucket"):
with pytest.raises(Exception): with pytest.raises(Exception):
s3_client.copy_object(bucket_1, obj_key) s3_client.copy_object(bucket_1, obj_key)
@ -120,11 +121,11 @@ class TestS3GateObject:
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put several versions of object into bucket"): with reporter.step("Put several versions of object into bucket"):
s3_client.put_object(bucket, file_name_simple) s3_client.put_object(bucket, file_name_simple)
s3_helper.check_objects_in_bucket(s3_client, bucket, [obj_key]) s3_helper.check_objects_in_bucket(s3_client, bucket, [obj_key])
with allure.step("Copy object and check acl attribute"): with reporter.step("Copy object and check acl attribute"):
copy_obj_path = s3_client.copy_object(bucket, obj_key, acl="public-read-write") copy_obj_path = s3_client.copy_object(bucket, obj_key, acl="public-read-write")
obj_acl = s3_client.get_object_acl(bucket, copy_obj_path) obj_acl = s3_client.get_object_acl(bucket, copy_obj_path)
s3_helper.assert_s3_acl(acl_grants=obj_acl, permitted_users="CanonicalUser") s3_helper.assert_s3_acl(acl_grants=obj_acl, permitted_users="CanonicalUser")
@ -138,25 +139,25 @@ class TestS3GateObject:
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put object into bucket"): with reporter.step("Put object into bucket"):
s3_client.put_object(bucket, file_path, metadata=object_metadata) s3_client.put_object(bucket, file_path, metadata=object_metadata)
bucket_1_objects = [file_name] bucket_1_objects = [file_name]
s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_1_objects) s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_1_objects)
with allure.step("Copy one object"): with reporter.step("Copy one object"):
copy_obj_path = s3_client.copy_object(bucket, file_name) copy_obj_path = s3_client.copy_object(bucket, file_name)
bucket_1_objects.append(copy_obj_path) bucket_1_objects.append(copy_obj_path)
s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_1_objects) s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_1_objects)
obj_head = s3_client.head_object(bucket, copy_obj_path) obj_head = s3_client.head_object(bucket, copy_obj_path)
assert obj_head.get("Metadata") == object_metadata, f"Metadata must be {object_metadata}" assert obj_head.get("Metadata") == object_metadata, f"Metadata must be {object_metadata}"
with allure.step("Copy one object with metadata"): with reporter.step("Copy one object with metadata"):
copy_obj_path = s3_client.copy_object(bucket, file_name, metadata_directive="COPY") copy_obj_path = s3_client.copy_object(bucket, file_name, metadata_directive="COPY")
bucket_1_objects.append(copy_obj_path) bucket_1_objects.append(copy_obj_path)
obj_head = s3_client.head_object(bucket, copy_obj_path) obj_head = s3_client.head_object(bucket, copy_obj_path)
assert obj_head.get("Metadata") == object_metadata, f"Metadata must be {object_metadata}" assert obj_head.get("Metadata") == object_metadata, f"Metadata must be {object_metadata}"
with allure.step("Copy one object with new metadata"): with reporter.step("Copy one object with new metadata"):
object_metadata_1 = {f"{uuid.uuid4()}": f"{uuid.uuid4()}"} object_metadata_1 = {f"{uuid.uuid4()}": f"{uuid.uuid4()}"}
copy_obj_path = s3_client.copy_object( copy_obj_path = s3_client.copy_object(
bucket, bucket,
@ -177,13 +178,13 @@ class TestS3GateObject:
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put several versions of object into bucket"): with reporter.step("Put several versions of object into bucket"):
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
s3_client.put_object_tagging(bucket, file_name_simple, tags=object_tagging) s3_client.put_object_tagging(bucket, file_name_simple, tags=object_tagging)
bucket_1_objects = [file_name_simple] bucket_1_objects = [file_name_simple]
s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_1_objects) s3_helper.check_objects_in_bucket(s3_client, bucket, bucket_1_objects)
with allure.step("Copy one object without tag"): with reporter.step("Copy one object without tag"):
copy_obj_path = s3_client.copy_object(bucket, file_name_simple) copy_obj_path = s3_client.copy_object(bucket, file_name_simple)
got_tags = s3_client.get_object_tagging(bucket, copy_obj_path) got_tags = s3_client.get_object_tagging(bucket, copy_obj_path)
assert got_tags, f"Expected tags, got {got_tags}" assert got_tags, f"Expected tags, got {got_tags}"
@ -191,7 +192,7 @@ class TestS3GateObject:
for tag in expected_tags: for tag in expected_tags:
assert tag in got_tags, f"Expected tag {tag} in {got_tags}" assert tag in got_tags, f"Expected tag {tag} in {got_tags}"
with allure.step("Copy one object with tag"): with reporter.step("Copy one object with tag"):
copy_obj_path_1 = s3_client.copy_object(bucket, file_name_simple, tagging_directive="COPY") copy_obj_path_1 = s3_client.copy_object(bucket, file_name_simple, tagging_directive="COPY")
got_tags = s3_client.get_object_tagging(bucket, copy_obj_path_1) got_tags = s3_client.get_object_tagging(bucket, copy_obj_path_1)
assert got_tags, f"Expected tags, got {got_tags}" assert got_tags, f"Expected tags, got {got_tags}"
@ -199,7 +200,7 @@ class TestS3GateObject:
for tag in expected_tags: for tag in expected_tags:
assert tag in got_tags, f"Expected tag {tag} in {got_tags}" assert tag in got_tags, f"Expected tag {tag} in {got_tags}"
with allure.step("Copy one object with new tag"): with reporter.step("Copy one object with new tag"):
tag_key = "tag1" tag_key = "tag1"
tag_value = uuid.uuid4() tag_value = uuid.uuid4()
new_tag = f"{tag_key}={tag_value}" new_tag = f"{tag_key}={tag_value}"
@ -230,14 +231,14 @@ class TestS3GateObject:
obj_key = os.path.basename(file_name_simple) obj_key = os.path.basename(file_name_simple)
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put several versions of object into bucket"): with reporter.step("Put several versions of object into bucket"):
version_id_1 = s3_client.put_object(bucket, file_name_simple) version_id_1 = s3_client.put_object(bucket, file_name_simple)
file_name_1 = generate_file_with_content( file_name_1 = generate_file_with_content(
simple_object_size.value, file_path=file_name_simple, content=version_2_content simple_object_size.value, file_path=file_name_simple, content=version_2_content
) )
version_id_2 = s3_client.put_object(bucket, file_name_1) version_id_2 = s3_client.put_object(bucket, file_name_1)
with allure.step("Check bucket shows all versions"): with reporter.step("Check bucket shows all versions"):
versions = s3_client.list_objects_versions(bucket) versions = s3_client.list_objects_versions(bucket)
obj_versions = {version.get("VersionId") for version in versions if version.get("Key") == obj_key} obj_versions = {version.get("VersionId") for version in versions if version.get("Key") == obj_key}
assert obj_versions == { assert obj_versions == {
@ -245,26 +246,26 @@ class TestS3GateObject:
version_id_2, version_id_2,
}, f"Object should have versions: {version_id_1, version_id_2}" }, f"Object should have versions: {version_id_1, version_id_2}"
with allure.step("Delete 1 version of object"): with reporter.step("Delete 1 version of object"):
delete_obj = s3_client.delete_object(bucket, obj_key, version_id=version_id_1) delete_obj = s3_client.delete_object(bucket, obj_key, version_id=version_id_1)
versions = s3_client.list_objects_versions(bucket) versions = s3_client.list_objects_versions(bucket)
obj_versions = {version.get("VersionId") for version in versions if version.get("Key") == obj_key} obj_versions = {version.get("VersionId") for version in versions if version.get("Key") == obj_key}
assert obj_versions == {version_id_2}, f"Object should have versions: {version_id_2}" assert obj_versions == {version_id_2}, f"Object should have versions: {version_id_2}"
assert "DeleteMarker" not in delete_obj.keys(), "Delete markers should not be created" assert "DeleteMarker" not in delete_obj.keys(), "Delete markers should not be created"
with allure.step("Delete second version of object"): with reporter.step("Delete second version of object"):
delete_obj = s3_client.delete_object(bucket, obj_key, version_id=version_id_2) delete_obj = s3_client.delete_object(bucket, obj_key, version_id=version_id_2)
versions = s3_client.list_objects_versions(bucket) versions = s3_client.list_objects_versions(bucket)
obj_versions = {version.get("VersionId") for version in versions if version.get("Key") == obj_key} obj_versions = {version.get("VersionId") for version in versions if version.get("Key") == obj_key}
assert not obj_versions, "Expected object not found" assert not obj_versions, "Expected object not found"
assert "DeleteMarker" not in delete_obj.keys(), "Delete markers should not be created" assert "DeleteMarker" not in delete_obj.keys(), "Delete markers should not be created"
with allure.step("Put new object into bucket"): with reporter.step("Put new object into bucket"):
file_name_simple = generate_file(complex_object_size.value) file_name_simple = generate_file(complex_object_size.value)
obj_key = os.path.basename(file_name_simple) obj_key = os.path.basename(file_name_simple)
s3_client.put_object(bucket, file_name_simple) s3_client.put_object(bucket, file_name_simple)
with allure.step("Delete last object"): with reporter.step("Delete last object"):
delete_obj = s3_client.delete_object(bucket, obj_key) delete_obj = s3_client.delete_object(bucket, obj_key)
versions = s3_client.list_objects_versions(bucket, True) versions = s3_client.list_objects_versions(bucket, True)
assert versions.get("DeleteMarkers", None), "Expected delete Marker" assert versions.get("DeleteMarkers", None), "Expected delete Marker"
@ -281,7 +282,7 @@ class TestS3GateObject:
obj_key = os.path.basename(file_name_1) obj_key = os.path.basename(file_name_1)
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put several versions of object into bucket"): with reporter.step("Put several versions of object into bucket"):
version_id_1 = s3_client.put_object(bucket, file_name_1) version_id_1 = s3_client.put_object(bucket, file_name_1)
file_name_2 = generate_file_with_content( file_name_2 = generate_file_with_content(
simple_object_size.value, file_path=file_name_1, content=version_2_content simple_object_size.value, file_path=file_name_1, content=version_2_content
@ -297,18 +298,18 @@ class TestS3GateObject:
version_id_4 = s3_client.put_object(bucket, file_name_4) version_id_4 = s3_client.put_object(bucket, file_name_4)
version_ids = {version_id_1, version_id_2, version_id_3, version_id_4} version_ids = {version_id_1, version_id_2, version_id_3, version_id_4}
with allure.step("Check bucket shows all versions"): with reporter.step("Check bucket shows all versions"):
versions = s3_client.list_objects_versions(bucket) versions = s3_client.list_objects_versions(bucket)
obj_versions = {version.get("VersionId") for version in versions if version.get("Key") == obj_key} obj_versions = {version.get("VersionId") for version in versions if version.get("Key") == obj_key}
assert obj_versions == version_ids, f"Object should have versions: {version_ids}" assert obj_versions == version_ids, f"Object should have versions: {version_ids}"
with allure.step("Delete two objects from bucket one by one"): with reporter.step("Delete two objects from bucket one by one"):
version_to_delete_b1 = sample([version_id_1, version_id_2, version_id_3, version_id_4], k=2) version_to_delete_b1 = sample([version_id_1, version_id_2, version_id_3, version_id_4], k=2)
version_to_save = list(set(version_ids) - set(version_to_delete_b1)) version_to_save = list(set(version_ids) - set(version_to_delete_b1))
for ver in version_to_delete_b1: for ver in version_to_delete_b1:
s3_client.delete_object(bucket, obj_key, ver) s3_client.delete_object(bucket, obj_key, ver)
with allure.step("Check bucket shows all versions"): with reporter.step("Check bucket shows all versions"):
versions = s3_client.list_objects_versions(bucket) versions = s3_client.list_objects_versions(bucket)
obj_versions = [version.get("VersionId") for version in versions if version.get("Key") == obj_key] obj_versions = [version.get("VersionId") for version in versions if version.get("Key") == obj_key]
assert obj_versions.sort() == version_to_save.sort(), f"Object should have versions: {version_to_save}" assert obj_versions.sort() == version_to_save.sort(), f"Object should have versions: {version_to_save}"
@ -321,22 +322,22 @@ class TestS3GateObject:
obj_key = os.path.basename(file_name_simple) obj_key = os.path.basename(file_name_simple)
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put several versions of object into bucket"): with reporter.step("Put several versions of object into bucket"):
version_id_1 = s3_client.put_object(bucket, file_name_simple) version_id_1 = s3_client.put_object(bucket, file_name_simple)
file_name_1 = generate_file_with_content( file_name_1 = generate_file_with_content(
simple_object_size.value, file_path=file_name_simple, content=version_2_content simple_object_size.value, file_path=file_name_simple, content=version_2_content
) )
version_id_2 = s3_client.put_object(bucket, file_name_1) version_id_2 = s3_client.put_object(bucket, file_name_1)
with allure.step("Get first version of object"): with reporter.step("Get first version of object"):
object_1 = s3_client.get_object(bucket, obj_key, version_id_1, full_output=True) object_1 = s3_client.get_object(bucket, obj_key, version_id_1, full_output=True)
assert object_1.get("VersionId") == version_id_1, f"Get object with version {version_id_1}" assert object_1.get("VersionId") == version_id_1, f"Get object with version {version_id_1}"
with allure.step("Get second version of object"): with reporter.step("Get second version of object"):
object_2 = s3_client.get_object(bucket, obj_key, version_id_2, full_output=True) object_2 = s3_client.get_object(bucket, obj_key, version_id_2, full_output=True)
assert object_2.get("VersionId") == version_id_2, f"Get object with version {version_id_2}" assert object_2.get("VersionId") == version_id_2, f"Get object with version {version_id_2}"
with allure.step("Get object"): with reporter.step("Get object"):
object_3 = s3_client.get_object(bucket, obj_key, full_output=True) object_3 = s3_client.get_object(bucket, obj_key, full_output=True)
assert object_3.get("VersionId") == version_id_2, f"Get object with version {version_id_2}" assert object_3.get("VersionId") == version_id_2, f"Get object with version {version_id_2}"
@ -352,12 +353,12 @@ class TestS3GateObject:
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
file_hash = get_file_hash(file_path) file_hash = get_file_hash(file_path)
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put several versions of object into bucket"): with reporter.step("Put several versions of object into bucket"):
version_id_1 = s3_client.put_object(bucket, file_path) version_id_1 = s3_client.put_object(bucket, file_path)
file_name_1 = generate_file_with_content(simple_object_size.value, file_path=file_path) file_name_1 = generate_file_with_content(simple_object_size.value, file_path=file_path)
version_id_2 = s3_client.put_object(bucket, file_name_1) version_id_2 = s3_client.put_object(bucket, file_name_1)
with allure.step("Get first version of object"): with reporter.step("Get first version of object"):
object_1_part_1 = s3_client.get_object( object_1_part_1 = s3_client.get_object(
bucket, bucket,
file_name, file_name,
@ -385,7 +386,7 @@ class TestS3GateObject:
con_file = concat_files([object_1_part_1, object_1_part_2, object_1_part_3]) con_file = concat_files([object_1_part_1, object_1_part_2, object_1_part_3])
assert get_file_hash(con_file) == file_hash, "Hashes must be the same" assert get_file_hash(con_file) == file_hash, "Hashes must be the same"
with allure.step("Get second version of object"): with reporter.step("Get second version of object"):
object_2_part_1 = s3_client.get_object( object_2_part_1 = s3_client.get_object(
bucket, bucket,
file_name, file_name,
@ -410,7 +411,7 @@ class TestS3GateObject:
con_file_1 = concat_files([object_2_part_1, object_2_part_2, object_2_part_3]) con_file_1 = concat_files([object_2_part_1, object_2_part_2, object_2_part_3])
assert get_file_hash(con_file_1) == get_file_hash(file_name_1), "Hashes must be the same" assert get_file_hash(con_file_1) == get_file_hash(file_name_1), "Hashes must be the same"
with allure.step("Get object"): with reporter.step("Get object"):
object_3_part_1 = s3_client.get_object( object_3_part_1 = s3_client.get_object(
bucket, file_name, object_range=[0, int(simple_object_size.value / 3)] bucket, file_name, object_range=[0, int(simple_object_size.value / 3)]
) )
@ -450,7 +451,7 @@ class TestS3GateObject:
objects_in_bucket = [] objects_in_bucket = []
objects_count = 3 objects_count = 3
with allure.step(f"Put {objects_count} into bucket"): with reporter.step(f"Put {objects_count} into bucket"):
for _ in range(objects_count): for _ in range(objects_count):
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
@ -459,11 +460,11 @@ class TestS3GateObject:
# Extend deletion list to 1001 elements with same keys for test speed # Extend deletion list to 1001 elements with same keys for test speed
objects_to_delete = self.copy_extend_list(objects_in_bucket, 1001) objects_to_delete = self.copy_extend_list(objects_in_bucket, 1001)
with allure.step("Send delete request with 1001 objects and expect error"): with reporter.step("Send delete request with 1001 objects and expect error"):
with pytest.raises(Exception, match=S3_MALFORMED_XML_REQUEST): with pytest.raises(Exception, match=S3_MALFORMED_XML_REQUEST):
s3_client.delete_objects(bucket, objects_to_delete) s3_client.delete_objects(bucket, objects_to_delete)
with allure.step("Send delete request with 1000 objects without error"): with reporter.step("Send delete request with 1000 objects without error"):
with expect_not_raises(): with expect_not_raises():
s3_client.delete_objects(bucket, objects_to_delete[:1000]) s3_client.delete_objects(bucket, objects_to_delete[:1000])
@ -481,12 +482,12 @@ class TestS3GateObject:
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put several versions of object into bucket"): with reporter.step("Put several versions of object into bucket"):
version_id_1 = s3_client.put_object(bucket, file_path, metadata=object_metadata) version_id_1 = s3_client.put_object(bucket, file_path, metadata=object_metadata)
file_name_1 = generate_file_with_content(simple_object_size.value, file_path=file_path) file_name_1 = generate_file_with_content(simple_object_size.value, file_path=file_path)
version_id_2 = s3_client.put_object(bucket, file_name_1) version_id_2 = s3_client.put_object(bucket, file_name_1)
with allure.step("Get head of first version of object"): with reporter.step("Get head of first version of object"):
response = s3_client.head_object(bucket, file_name) response = s3_client.head_object(bucket, file_name)
assert "LastModified" in response, "Expected LastModified field" assert "LastModified" in response, "Expected LastModified field"
assert "ETag" in response, "Expected ETag field" assert "ETag" in response, "Expected ETag field"
@ -494,7 +495,7 @@ class TestS3GateObject:
assert response.get("VersionId") == version_id_2, f"Expected VersionId is {version_id_2}" assert response.get("VersionId") == version_id_2, f"Expected VersionId is {version_id_2}"
assert response.get("ContentLength") != 0, "Expected ContentLength is not zero" assert response.get("ContentLength") != 0, "Expected ContentLength is not zero"
with allure.step("Get head ob first version of object"): with reporter.step("Get head ob first version of object"):
response = s3_client.head_object(bucket, file_name, version_id=version_id_1) response = s3_client.head_object(bucket, file_name, version_id=version_id_1)
assert "LastModified" in response, "Expected LastModified field" assert "LastModified" in response, "Expected LastModified field"
assert "ETag" in response, "Expected ETag field" assert "ETag" in response, "Expected ETag field"
@ -517,11 +518,11 @@ class TestS3GateObject:
file_name_2 = s3_helper.object_key_from_file_path(file_path_2) file_name_2 = s3_helper.object_key_from_file_path(file_path_2)
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put several versions of object into bucket"): with reporter.step("Put several versions of object into bucket"):
s3_client.put_object(bucket, file_path_1) s3_client.put_object(bucket, file_path_1)
s3_client.put_object(bucket, file_path_2) s3_client.put_object(bucket, file_path_2)
with allure.step("Get list of object"): with reporter.step("Get list of object"):
if list_type == "v1": if list_type == "v1":
list_obj = s3_client.list_objects(bucket) list_obj = s3_client.list_objects(bucket)
elif list_type == "v2": elif list_type == "v2":
@ -531,7 +532,7 @@ class TestS3GateObject:
list_obj.sort() == [file_name, file_name_2].sort() list_obj.sort() == [file_name, file_name_2].sort()
), f"bucket should have object key {file_name, file_name_2}" ), f"bucket should have object key {file_name, file_name_2}"
with allure.step("Delete object"): with reporter.step("Delete object"):
delete_obj = s3_client.delete_object(bucket, file_name) delete_obj = s3_client.delete_object(bucket, file_name)
if list_type == "v1": if list_type == "v1":
list_obj_1 = s3_client.list_objects(bucket, full_output=True) list_obj_1 = s3_client.list_objects(bucket, full_output=True)
@ -562,7 +563,7 @@ class TestS3GateObject:
tag_2 = f"{tag_key_2}={tag_value_2}" tag_2 = f"{tag_key_2}={tag_value_2}"
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.SUSPENDED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.SUSPENDED)
with allure.step("Put first object into bucket"): with reporter.step("Put first object into bucket"):
s3_client.put_object(bucket, file_path_1, metadata=object_1_metadata, tagging=tag_1) s3_client.put_object(bucket, file_path_1, metadata=object_1_metadata, tagging=tag_1)
obj_head = s3_client.head_object(bucket, file_name) obj_head = s3_client.head_object(bucket, file_name)
assert obj_head.get("Metadata") == object_1_metadata, "Metadata must be the same" assert obj_head.get("Metadata") == object_1_metadata, "Metadata must be the same"
@ -570,7 +571,7 @@ class TestS3GateObject:
assert got_tags, f"Expected tags, got {got_tags}" assert got_tags, f"Expected tags, got {got_tags}"
assert got_tags == [{"Key": tag_key_1, "Value": str(tag_value_1)}], "Tags must be the same" assert got_tags == [{"Key": tag_key_1, "Value": str(tag_value_1)}], "Tags must be the same"
with allure.step("Rewrite file into bucket"): with reporter.step("Rewrite file into bucket"):
file_path_2 = generate_file_with_content(simple_object_size.value, file_path=file_path_1) file_path_2 = generate_file_with_content(simple_object_size.value, file_path=file_path_1)
s3_client.put_object(bucket, file_path_2, metadata=object_2_metadata, tagging=tag_2) s3_client.put_object(bucket, file_path_2, metadata=object_2_metadata, tagging=tag_2)
obj_head = s3_client.head_object(bucket, file_name) obj_head = s3_client.head_object(bucket, file_name)
@ -589,7 +590,7 @@ class TestS3GateObject:
tag_value_3 = uuid.uuid4() tag_value_3 = uuid.uuid4()
tag_3 = f"{tag_key_3}={tag_value_3}" tag_3 = f"{tag_key_3}={tag_value_3}"
with allure.step("Put third object into bucket"): with reporter.step("Put third object into bucket"):
version_id_1 = s3_client.put_object(bucket, file_path_3, metadata=object_3_metadata, tagging=tag_3) version_id_1 = s3_client.put_object(bucket, file_path_3, metadata=object_3_metadata, tagging=tag_3)
obj_head_3 = s3_client.head_object(bucket, file_name_3) obj_head_3 = s3_client.head_object(bucket, file_name_3)
assert obj_head_3.get("Metadata") == object_3_metadata, "Matadata must be the same" assert obj_head_3.get("Metadata") == object_3_metadata, "Matadata must be the same"
@ -597,7 +598,7 @@ class TestS3GateObject:
assert got_tags_3, f"Expected tags, got {got_tags_3}" assert got_tags_3, f"Expected tags, got {got_tags_3}"
assert got_tags_3 == [{"Key": tag_key_3, "Value": str(tag_value_3)}], "Tags must be the same" assert got_tags_3 == [{"Key": tag_key_3, "Value": str(tag_value_3)}], "Tags must be the same"
with allure.step("Put new version of file into bucket"): with reporter.step("Put new version of file into bucket"):
file_path_4 = generate_file_with_content(simple_object_size.value, file_path=file_path_3) file_path_4 = generate_file_with_content(simple_object_size.value, file_path=file_path_3)
version_id_2 = s3_client.put_object(bucket, file_path_4) version_id_2 = s3_client.put_object(bucket, file_path_4)
versions = s3_client.list_objects_versions(bucket) versions = s3_client.list_objects_versions(bucket)
@ -609,13 +610,13 @@ class TestS3GateObject:
got_tags_4 = s3_client.get_object_tagging(bucket, file_name_3) got_tags_4 = s3_client.get_object_tagging(bucket, file_name_3)
assert not got_tags_4, "No tags expected" assert not got_tags_4, "No tags expected"
with allure.step("Get object"): with reporter.step("Get object"):
object_3 = s3_client.get_object(bucket, file_name_3, full_output=True) object_3 = s3_client.get_object(bucket, file_name_3, full_output=True)
assert object_3.get("VersionId") == version_id_2, f"get object with version {version_id_2}" assert object_3.get("VersionId") == version_id_2, f"get object with version {version_id_2}"
object_3 = s3_client.get_object(bucket, file_name_3) object_3 = s3_client.get_object(bucket, file_name_3)
assert get_file_hash(file_path_4) == get_file_hash(object_3), "Hashes must be the same" assert get_file_hash(file_path_4) == get_file_hash(object_3), "Hashes must be the same"
with allure.step("Get first version of object"): with reporter.step("Get first version of object"):
object_4 = s3_client.get_object(bucket, file_name_3, version_id_1, full_output=True) object_4 = s3_client.get_object(bucket, file_name_3, version_id_1, full_output=True)
assert object_4.get("VersionId") == version_id_1, f"get object with version {version_id_1}" assert object_4.get("VersionId") == version_id_1, f"get object with version {version_id_1}"
object_4 = s3_client.get_object(bucket, file_name_3, version_id_1) object_4 = s3_client.get_object(bucket, file_name_3, version_id_1)
@ -645,14 +646,14 @@ class TestS3GateObject:
status = VersioningStatus.SUSPENDED status = VersioningStatus.SUSPENDED
s3_helper.set_bucket_versioning(s3_client, bucket, status) s3_helper.set_bucket_versioning(s3_client, bucket, status)
with allure.step("Put object with acl private"): with reporter.step("Put object with acl private"):
s3_client.put_object(bucket, file_path_1, acl="private") s3_client.put_object(bucket, file_path_1, acl="private")
obj_acl = s3_client.get_object_acl(bucket, file_name) obj_acl = s3_client.get_object_acl(bucket, file_name)
s3_helper.assert_s3_acl(acl_grants=obj_acl, permitted_users="CanonicalUser") s3_helper.assert_s3_acl(acl_grants=obj_acl, permitted_users="CanonicalUser")
object_1 = s3_client.get_object(bucket, file_name) object_1 = s3_client.get_object(bucket, file_name)
assert get_file_hash(file_path_1) == get_file_hash(object_1), "Hashes must be the same" assert get_file_hash(file_path_1) == get_file_hash(object_1), "Hashes must be the same"
with allure.step("Put object with acl public-read"): with reporter.step("Put object with acl public-read"):
file_path_2 = generate_file_with_content(simple_object_size.value, file_path=file_path_1) file_path_2 = generate_file_with_content(simple_object_size.value, file_path=file_path_1)
s3_client.put_object(bucket, file_path_2, acl="public-read") s3_client.put_object(bucket, file_path_2, acl="public-read")
obj_acl = s3_client.get_object_acl(bucket, file_name) obj_acl = s3_client.get_object_acl(bucket, file_name)
@ -660,7 +661,7 @@ class TestS3GateObject:
object_2 = s3_client.get_object(bucket, file_name) object_2 = s3_client.get_object(bucket, file_name)
assert get_file_hash(file_path_2) == get_file_hash(object_2), "Hashes must be the same" assert get_file_hash(file_path_2) == get_file_hash(object_2), "Hashes must be the same"
with allure.step("Put object with acl public-read-write"): with reporter.step("Put object with acl public-read-write"):
file_path_3 = generate_file_with_content(simple_object_size.value, file_path=file_path_1) file_path_3 = generate_file_with_content(simple_object_size.value, file_path=file_path_1)
s3_client.put_object(bucket, file_path_3, acl="public-read-write") s3_client.put_object(bucket, file_path_3, acl="public-read-write")
obj_acl = s3_client.get_object_acl(bucket, file_name) obj_acl = s3_client.get_object_acl(bucket, file_name)
@ -668,7 +669,7 @@ class TestS3GateObject:
object_3 = s3_client.get_object(bucket, file_name) object_3 = s3_client.get_object(bucket, file_name)
assert get_file_hash(file_path_3) == get_file_hash(object_3), "Hashes must be the same" assert get_file_hash(file_path_3) == get_file_hash(object_3), "Hashes must be the same"
with allure.step("Put object with acl authenticated-read"): with reporter.step("Put object with acl authenticated-read"):
file_path_4 = generate_file_with_content(simple_object_size.value, file_path=file_path_1) file_path_4 = generate_file_with_content(simple_object_size.value, file_path=file_path_1)
s3_client.put_object(bucket, file_path_4, acl="authenticated-read") s3_client.put_object(bucket, file_path_4, acl="authenticated-read")
obj_acl = s3_client.get_object_acl(bucket, file_name) obj_acl = s3_client.get_object_acl(bucket, file_name)
@ -679,7 +680,7 @@ class TestS3GateObject:
file_path_5 = generate_file(complex_object_size.value) file_path_5 = generate_file(complex_object_size.value)
file_name_5 = s3_helper.object_key_from_file_path(file_path_5) file_name_5 = s3_helper.object_key_from_file_path(file_path_5)
with allure.step("Put object with --grant-full-control id=mycanonicaluserid"): with reporter.step("Put object with --grant-full-control id=mycanonicaluserid"):
generate_file_with_content(simple_object_size.value, file_path=file_path_5) generate_file_with_content(simple_object_size.value, file_path=file_path_5)
s3_client.put_object( s3_client.put_object(
bucket, bucket,
@ -691,7 +692,7 @@ class TestS3GateObject:
object_5 = s3_client.get_object(bucket, file_name_5) object_5 = s3_client.get_object(bucket, file_name_5)
assert get_file_hash(file_path_5) == get_file_hash(object_5), "Hashes must be the same" assert get_file_hash(file_path_5) == get_file_hash(object_5), "Hashes must be the same"
with allure.step("Put object with --grant-read uri=http://acs.amazonaws.com/groups/global/AllUsers"): with reporter.step("Put object with --grant-read uri=http://acs.amazonaws.com/groups/global/AllUsers"):
generate_file_with_content(simple_object_size.value, file_path=file_path_5) generate_file_with_content(simple_object_size.value, file_path=file_path_5)
s3_client.put_object( s3_client.put_object(
bucket, bucket,
@ -716,7 +717,7 @@ class TestS3GateObject:
bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True) bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=True)
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put object with lock-mode GOVERNANCE lock-retain-until-date +1day, lock-legal-hold-status"): with reporter.step("Put object with lock-mode GOVERNANCE lock-retain-until-date +1day, lock-legal-hold-status"):
date_obj = datetime.utcnow() + timedelta(days=1) date_obj = datetime.utcnow() + timedelta(days=1)
s3_client.put_object( s3_client.put_object(
bucket, bucket,
@ -727,7 +728,7 @@ class TestS3GateObject:
) )
s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "GOVERNANCE", date_obj, "OFF") s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "GOVERNANCE", date_obj, "OFF")
with allure.step( with reporter.step(
"Put new version of object with [--object-lock-mode COMPLIANCE] и [--object-lock-retain-until-date +3days]" "Put new version of object with [--object-lock-mode COMPLIANCE] и [--object-lock-retain-until-date +3days]"
): ):
date_obj = datetime.utcnow() + timedelta(days=2) date_obj = datetime.utcnow() + timedelta(days=2)
@ -740,7 +741,7 @@ class TestS3GateObject:
) )
s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "OFF") s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "OFF")
with allure.step( with reporter.step(
"Put new version of object with [--object-lock-mode COMPLIANCE] и [--object-lock-retain-until-date +2days]" "Put new version of object with [--object-lock-mode COMPLIANCE] и [--object-lock-retain-until-date +2days]"
): ):
date_obj = datetime.utcnow() + timedelta(days=3) date_obj = datetime.utcnow() + timedelta(days=3)
@ -754,7 +755,7 @@ class TestS3GateObject:
) )
s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "ON") s3_helper.assert_object_lock_mode(s3_client, bucket, file_name, "COMPLIANCE", date_obj, "ON")
with allure.step("Put object with lock-mode"): with reporter.step("Put object with lock-mode"):
with pytest.raises( with pytest.raises(
Exception, Exception,
match=r".*must both be supplied*", match=r".*must both be supplied*",
@ -762,7 +763,7 @@ class TestS3GateObject:
# x-amz-object-lock-retain-until-date and x-amz-object-lock-mode must both be supplied # x-amz-object-lock-retain-until-date and x-amz-object-lock-mode must both be supplied
s3_client.put_object(bucket, file_path_1, object_lock_mode="COMPLIANCE") s3_client.put_object(bucket, file_path_1, object_lock_mode="COMPLIANCE")
with allure.step("Put object with lock-mode and past date"): with reporter.step("Put object with lock-mode and past date"):
date_obj = datetime.utcnow() - timedelta(days=3) date_obj = datetime.utcnow() - timedelta(days=3)
with pytest.raises( with pytest.raises(
Exception, Exception,
@ -810,11 +811,11 @@ class TestS3GateObject:
metadata=object_metadata, metadata=object_metadata,
) )
with allure.step("Check objects are synced"): with reporter.step("Check objects are synced"):
objects = s3_client.list_objects(bucket) objects = s3_client.list_objects(bucket)
assert set(key_to_path.keys()) == set(objects), f"Expected all abjects saved. Got {objects}" assert set(key_to_path.keys()) == set(objects), f"Expected all abjects saved. Got {objects}"
with allure.step("Check these are the same objects"): with reporter.step("Check these are the same objects"):
for obj_key in objects: for obj_key in objects:
got_object = s3_client.get_object(bucket, obj_key) got_object = s3_client.get_object(bucket, obj_key)
assert get_file_hash(got_object) == get_file_hash( assert get_file_hash(got_object) == get_file_hash(
@ -841,7 +842,7 @@ class TestS3GateObject:
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
with allure.step("Put object"): with reporter.step("Put object"):
s3_client.put_object(bucket, file_path_1) s3_client.put_object(bucket, file_path_1)
s3_helper.check_objects_in_bucket(s3_client, bucket, [file_name]) s3_helper.check_objects_in_bucket(s3_client, bucket, [file_name])
@ -850,12 +851,12 @@ class TestS3GateObject:
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
objects_list = s3_client.list_objects_versions(bucket) objects_list = s3_client.list_objects_versions(bucket)
with allure.step("Check that bucket is empty"): with reporter.step("Check that bucket is empty"):
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
obj_key = "fake_object_key" obj_key = "fake_object_key"
with allure.step("Delete non-existing object"): with reporter.step("Delete non-existing object"):
delete_obj = s3_client.delete_object(bucket, obj_key) delete_obj = s3_client.delete_object(bucket, obj_key)
# there should be no objects or delete markers in the bucket # there should be no objects or delete markers in the bucket
assert "DeleteMarker" not in delete_obj.keys(), "Delete markers should not be created" assert "DeleteMarker" not in delete_obj.keys(), "Delete markers should not be created"
@ -866,16 +867,16 @@ class TestS3GateObject:
def test_s3_delete_twice(self, s3_client: S3ClientWrapper, bucket: str, simple_object_size: ObjectSize): def test_s3_delete_twice(self, s3_client: S3ClientWrapper, bucket: str, simple_object_size: ObjectSize):
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
with allure.step("Check that bucket is empty"): with reporter.step("Check that bucket is empty"):
assert not objects_list, f"Expected empty bucket, got {objects_list}" assert not objects_list, f"Expected empty bucket, got {objects_list}"
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
with allure.step("Put object into one bucket"): with reporter.step("Put object into one bucket"):
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
with allure.step("Delete the object from the bucket"): with reporter.step("Delete the object from the bucket"):
delete_object = s3_client.delete_object(bucket, file_name) delete_object = s3_client.delete_object(bucket, file_name)
versions = s3_client.list_objects_versions(bucket) versions = s3_client.list_objects_versions(bucket)
@ -883,7 +884,7 @@ class TestS3GateObject:
assert obj_versions, f"Object versions were not found {objects_list}" assert obj_versions, f"Object versions were not found {objects_list}"
assert "DeleteMarker" in delete_object.keys(), "Delete markers not found" assert "DeleteMarker" in delete_object.keys(), "Delete markers not found"
with allure.step("Delete the object from the bucket again"): with reporter.step("Delete the object from the bucket again"):
delete_object_2nd_attempt = s3_client.delete_object(bucket, file_name) delete_object_2nd_attempt = s3_client.delete_object(bucket, file_name)
versions_2nd_attempt = s3_client.list_objects_versions(bucket) versions_2nd_attempt = s3_client.list_objects_versions(bucket)

View file

@ -2,6 +2,7 @@ import os
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.s3 import S3ClientWrapper, VersioningStatus from frostfs_testlib.s3 import S3ClientWrapper, VersioningStatus
from frostfs_testlib.steps.cli.container import search_container_by_name from frostfs_testlib.steps.cli.container import search_container_by_name
from frostfs_testlib.steps.s3 import s3_helper from frostfs_testlib.steps.s3 import s3_helper
@ -22,7 +23,7 @@ class TestS3GatePolicy(ClusterTestBase):
file_path_2 = generate_file(simple_object_size.value) file_path_2 = generate_file(simple_object_size.value)
file_name_2 = s3_helper.object_key_from_file_path(file_path_2) file_name_2 = s3_helper.object_key_from_file_path(file_path_2)
with allure.step("Create two buckets with different bucket configuration"): with reporter.step("Create two buckets with different bucket configuration"):
bucket_1 = s3_client.create_bucket(location_constraint="complex") bucket_1 = s3_client.create_bucket(location_constraint="complex")
s3_helper.set_bucket_versioning(s3_client, bucket_1, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket_1, VersioningStatus.ENABLED)
bucket_2 = s3_client.create_bucket(location_constraint="rep-3") bucket_2 = s3_client.create_bucket(location_constraint="rep-3")
@ -32,24 +33,24 @@ class TestS3GatePolicy(ClusterTestBase):
bucket_1 in list_buckets and bucket_2 in list_buckets bucket_1 in list_buckets and bucket_2 in list_buckets
), f"Expected two buckets {bucket_1, bucket_2}, got {list_buckets}" ), f"Expected two buckets {bucket_1, bucket_2}, got {list_buckets}"
with allure.step("Check head buckets"): with reporter.step("Check head buckets"):
with expect_not_raises(): with expect_not_raises():
s3_client.head_bucket(bucket_1) s3_client.head_bucket(bucket_1)
s3_client.head_bucket(bucket_2) s3_client.head_bucket(bucket_2)
with allure.step("Put objects into buckets"): with reporter.step("Put objects into buckets"):
version_id_1 = s3_client.put_object(bucket_1, file_path_1) version_id_1 = s3_client.put_object(bucket_1, file_path_1)
version_id_2 = s3_client.put_object(bucket_2, file_path_2) version_id_2 = s3_client.put_object(bucket_2, file_path_2)
s3_helper.check_objects_in_bucket(s3_client, bucket_1, [file_name_1]) s3_helper.check_objects_in_bucket(s3_client, bucket_1, [file_name_1])
s3_helper.check_objects_in_bucket(s3_client, bucket_2, [file_name_2]) s3_helper.check_objects_in_bucket(s3_client, bucket_2, [file_name_2])
with allure.step("Check bucket location"): with reporter.step("Check bucket location"):
bucket_loc_1 = s3_client.get_bucket_location(bucket_1) bucket_loc_1 = s3_client.get_bucket_location(bucket_1)
bucket_loc_2 = s3_client.get_bucket_location(bucket_2) bucket_loc_2 = s3_client.get_bucket_location(bucket_2)
assert bucket_loc_1 == "complex" assert bucket_loc_1 == "complex"
assert bucket_loc_2 == "rep-3" assert bucket_loc_2 == "rep-3"
with allure.step("Check object policy"): with reporter.step("Check object policy"):
cid_1 = search_container_by_name( cid_1 = search_container_by_name(
default_wallet, default_wallet,
bucket_1, bucket_1,
@ -81,20 +82,20 @@ class TestS3GatePolicy(ClusterTestBase):
@allure.title("Bucket with unexisting location constraint (s3_client={s3_client})") @allure.title("Bucket with unexisting location constraint (s3_client={s3_client})")
def test_s3_bucket_wrong_location(self, s3_client: S3ClientWrapper): def test_s3_bucket_wrong_location(self, s3_client: S3ClientWrapper):
with allure.step("Create bucket with unenxisting location constraint policy"): with reporter.step("Create bucket with unenxisting location constraint policy"):
with pytest.raises(Exception): with pytest.raises(Exception):
s3_client.create_bucket(location_constraint="UNEXISTING LOCATION CONSTRAINT") s3_client.create_bucket(location_constraint="UNEXISTING LOCATION CONSTRAINT")
@allure.title("Bucket policy (s3_client={s3_client})") @allure.title("Bucket policy (s3_client={s3_client})")
def test_s3_bucket_policy(self, s3_client: S3ClientWrapper): def test_s3_bucket_policy(self, s3_client: S3ClientWrapper):
with allure.step("Create bucket with default policy"): with reporter.step("Create bucket with default policy"):
bucket = s3_client.create_bucket() bucket = s3_client.create_bucket()
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("GetBucketPolicy"): with reporter.step("GetBucketPolicy"):
s3_client.get_bucket_policy(bucket) s3_client.get_bucket_policy(bucket)
with allure.step("Put new policy"): with reporter.step("Put new policy"):
custom_policy = f"file://{os.getcwd()}/pytest_tests/resources/files/bucket_policy.json" custom_policy = f"file://{os.getcwd()}/pytest_tests/resources/files/bucket_policy.json"
custom_policy = { custom_policy = {
"Version": "2008-10-17", "Version": "2008-10-17",
@ -111,20 +112,20 @@ class TestS3GatePolicy(ClusterTestBase):
} }
s3_client.put_bucket_policy(bucket, custom_policy) s3_client.put_bucket_policy(bucket, custom_policy)
with allure.step("GetBucketPolicy"): with reporter.step("GetBucketPolicy"):
policy_1 = s3_client.get_bucket_policy(bucket) policy_1 = s3_client.get_bucket_policy(bucket)
print(policy_1) print(policy_1)
@allure.title("Bucket CORS (s3_client={s3_client})") @allure.title("Bucket CORS (s3_client={s3_client})")
def test_s3_cors(self, s3_client: S3ClientWrapper): def test_s3_cors(self, s3_client: S3ClientWrapper):
with allure.step("Create bucket without cors"): with reporter.step("Create bucket without cors"):
bucket = s3_client.create_bucket() bucket = s3_client.create_bucket()
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with pytest.raises(Exception): with pytest.raises(Exception):
bucket_cors = s3_client.get_bucket_cors(bucket) bucket_cors = s3_client.get_bucket_cors(bucket)
with allure.step("Put bucket cors"): with reporter.step("Put bucket cors"):
cors = { cors = {
"CORSRules": [ "CORSRules": [
{ {
@ -146,7 +147,7 @@ class TestS3GatePolicy(ClusterTestBase):
bucket_cors = s3_client.get_bucket_cors(bucket) bucket_cors = s3_client.get_bucket_cors(bucket)
assert bucket_cors == cors.get("CORSRules"), f"Expected CORSRules must be {cors.get('CORSRules')}" assert bucket_cors == cors.get("CORSRules"), f"Expected CORSRules must be {cors.get('CORSRules')}"
with allure.step("delete bucket cors"): with reporter.step("delete bucket cors"):
s3_client.delete_bucket_cors(bucket) s3_client.delete_bucket_cors(bucket)
with pytest.raises(Exception): with pytest.raises(Exception):

View file

@ -4,6 +4,7 @@ from typing import Tuple
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.s3 import S3ClientWrapper from frostfs_testlib.s3 import S3ClientWrapper
from frostfs_testlib.steps.s3 import s3_helper from frostfs_testlib.steps.s3 import s3_helper
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
@ -27,76 +28,76 @@ class TestS3GateTagging:
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
file_name = s3_helper.object_key_from_file_path(file_path) file_name = s3_helper.object_key_from_file_path(file_path)
with allure.step("Put with 3 tags object into bucket"): with reporter.step("Put with 3 tags object into bucket"):
tag_1 = "Tag1=Value1" tag_1 = "Tag1=Value1"
s3_client.put_object(bucket, file_path, tagging=tag_1) s3_client.put_object(bucket, file_path, tagging=tag_1)
got_tags = s3_client.get_object_tagging(bucket, file_name) got_tags = s3_client.get_object_tagging(bucket, file_name)
assert got_tags, f"Expected tags, got {got_tags}" assert got_tags, f"Expected tags, got {got_tags}"
assert got_tags == [{"Key": "Tag1", "Value": "Value1"}], "Tags must be the same" assert got_tags == [{"Key": "Tag1", "Value": "Value1"}], "Tags must be the same"
with allure.step("Put 10 new tags for object"): with reporter.step("Put 10 new tags for object"):
tags_2 = self.create_tags(10) tags_2 = self.create_tags(10)
s3_client.put_object_tagging(bucket, file_name, tags=tags_2) s3_client.put_object_tagging(bucket, file_name, tags=tags_2)
s3_helper.check_tags_by_object(s3_client, bucket, file_name, tags_2, [("Tag1", "Value1")]) s3_helper.check_tags_by_object(s3_client, bucket, file_name, tags_2, [("Tag1", "Value1")])
with allure.step("Put 10 extra new tags for object"): with reporter.step("Put 10 extra new tags for object"):
tags_3 = self.create_tags(10) tags_3 = self.create_tags(10)
s3_client.put_object_tagging(bucket, file_name, tags=tags_3) s3_client.put_object_tagging(bucket, file_name, tags=tags_3)
s3_helper.check_tags_by_object(s3_client, bucket, file_name, tags_3, tags_2) s3_helper.check_tags_by_object(s3_client, bucket, file_name, tags_3, tags_2)
with allure.step("Copy one object with tag"): with reporter.step("Copy one object with tag"):
copy_obj_path_1 = s3_client.copy_object(bucket, file_name, tagging_directive="COPY") copy_obj_path_1 = s3_client.copy_object(bucket, file_name, tagging_directive="COPY")
s3_helper.check_tags_by_object(s3_client, bucket, copy_obj_path_1, tags_3, tags_2) s3_helper.check_tags_by_object(s3_client, bucket, copy_obj_path_1, tags_3, tags_2)
with allure.step("Put 11 new tags to object and expect an error"): with reporter.step("Put 11 new tags to object and expect an error"):
tags_4 = self.create_tags(11) tags_4 = self.create_tags(11)
with pytest.raises(Exception, match=r".*Object tags cannot be greater than 10*"): with pytest.raises(Exception, match=r".*Object tags cannot be greater than 10*"):
# An error occurred (BadRequest) when calling the PutObjectTagging operation: Object tags cannot be greater than 10 # An error occurred (BadRequest) when calling the PutObjectTagging operation: Object tags cannot be greater than 10
s3_client.put_object_tagging(bucket, file_name, tags=tags_4) s3_client.put_object_tagging(bucket, file_name, tags=tags_4)
with allure.step("Put empty tag"): with reporter.step("Put empty tag"):
tags_5 = [] tags_5 = []
s3_client.put_object_tagging(bucket, file_name, tags=tags_5) s3_client.put_object_tagging(bucket, file_name, tags=tags_5)
s3_helper.check_tags_by_object(s3_client, bucket, file_name, []) s3_helper.check_tags_by_object(s3_client, bucket, file_name, [])
with allure.step("Put 10 object tags"): with reporter.step("Put 10 object tags"):
tags_6 = self.create_tags(10) tags_6 = self.create_tags(10)
s3_client.put_object_tagging(bucket, file_name, tags=tags_6) s3_client.put_object_tagging(bucket, file_name, tags=tags_6)
s3_helper.check_tags_by_object(s3_client, bucket, file_name, tags_6) s3_helper.check_tags_by_object(s3_client, bucket, file_name, tags_6)
with allure.step("Delete tags by delete-object-tagging"): with reporter.step("Delete tags by delete-object-tagging"):
s3_client.delete_object_tagging(bucket, file_name) s3_client.delete_object_tagging(bucket, file_name)
s3_helper.check_tags_by_object(s3_client, bucket, file_name, []) s3_helper.check_tags_by_object(s3_client, bucket, file_name, [])
@allure.title("Bucket tagging (s3_client={s3_client})") @allure.title("Bucket tagging (s3_client={s3_client})")
def test_s3_bucket_tagging(self, s3_client: S3ClientWrapper, bucket: str): def test_s3_bucket_tagging(self, s3_client: S3ClientWrapper, bucket: str):
with allure.step("Put 10 bucket tags"): with reporter.step("Put 10 bucket tags"):
tags_1 = self.create_tags(10) tags_1 = self.create_tags(10)
s3_client.put_bucket_tagging(bucket, tags_1) s3_client.put_bucket_tagging(bucket, tags_1)
s3_helper.check_tags_by_bucket(s3_client, bucket, tags_1) s3_helper.check_tags_by_bucket(s3_client, bucket, tags_1)
with allure.step("Put new 10 bucket tags"): with reporter.step("Put new 10 bucket tags"):
tags_2 = self.create_tags(10) tags_2 = self.create_tags(10)
s3_client.put_bucket_tagging(bucket, tags_2) s3_client.put_bucket_tagging(bucket, tags_2)
s3_helper.check_tags_by_bucket(s3_client, bucket, tags_2, tags_1) s3_helper.check_tags_by_bucket(s3_client, bucket, tags_2, tags_1)
with allure.step("Put 11 new tags to bucket and expect an error"): with reporter.step("Put 11 new tags to bucket and expect an error"):
tags_3 = self.create_tags(11) tags_3 = self.create_tags(11)
with pytest.raises(Exception, match=r".*Object tags cannot be greater than 10.*"): with pytest.raises(Exception, match=r".*Object tags cannot be greater than 10.*"):
# An error occurred (BadRequest) when calling the PutBucketTagging operation (reached max retries: 0): Object tags cannot be greater than 10 # An error occurred (BadRequest) when calling the PutBucketTagging operation (reached max retries: 0): Object tags cannot be greater than 10
s3_client.put_bucket_tagging(bucket, tags_3) s3_client.put_bucket_tagging(bucket, tags_3)
with allure.step("Put empty tag"): with reporter.step("Put empty tag"):
tags_4 = [] tags_4 = []
s3_client.put_bucket_tagging(bucket, tags_4) s3_client.put_bucket_tagging(bucket, tags_4)
s3_helper.check_tags_by_bucket(s3_client, bucket, tags_4) s3_helper.check_tags_by_bucket(s3_client, bucket, tags_4)
with allure.step("Put new 10 bucket tags"): with reporter.step("Put new 10 bucket tags"):
tags_5 = self.create_tags(10) tags_5 = self.create_tags(10)
s3_client.put_bucket_tagging(bucket, tags_5) s3_client.put_bucket_tagging(bucket, tags_5)
s3_helper.check_tags_by_bucket(s3_client, bucket, tags_5, tags_2) s3_helper.check_tags_by_bucket(s3_client, bucket, tags_5, tags_2)
with allure.step("Delete tags by delete-bucket-tagging"): with reporter.step("Delete tags by delete-bucket-tagging"):
s3_client.delete_bucket_tagging(bucket) s3_client.delete_bucket_tagging(bucket)
s3_helper.check_tags_by_bucket(s3_client, bucket, []) s3_helper.check_tags_by_bucket(s3_client, bucket, [])

View file

@ -1,5 +1,6 @@
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.s3 import S3ClientWrapper, VersioningStatus from frostfs_testlib.s3 import S3ClientWrapper, VersioningStatus
from frostfs_testlib.steps.s3 import s3_helper from frostfs_testlib.steps.s3 import s3_helper
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
@ -23,7 +24,7 @@ class TestS3GateVersioning:
bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=False) bucket = s3_client.create_bucket(object_lock_enabled_for_bucket=False)
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.SUSPENDED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.SUSPENDED)
with allure.step("Put object into bucket"): with reporter.step("Put object into bucket"):
s3_client.put_object(bucket, file_path) s3_client.put_object(bucket, file_path)
objects_list = s3_client.list_objects(bucket) objects_list = s3_client.list_objects(bucket)
assert objects_list == bucket_objects, f"Expected list with single objects in bucket, got {objects_list}" assert objects_list == bucket_objects, f"Expected list with single objects in bucket, got {objects_list}"
@ -37,26 +38,26 @@ class TestS3GateVersioning:
s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED) s3_helper.set_bucket_versioning(s3_client, bucket, VersioningStatus.ENABLED)
with allure.step("Put several versions of object into bucket"): with reporter.step("Put several versions of object into bucket"):
version_id_1 = s3_client.put_object(bucket, file_path) version_id_1 = s3_client.put_object(bucket, file_path)
file_name_1 = generate_file_with_content(simple_object_size.value, file_path=file_path) file_name_1 = generate_file_with_content(simple_object_size.value, file_path=file_path)
version_id_2 = s3_client.put_object(bucket, file_name_1) version_id_2 = s3_client.put_object(bucket, file_name_1)
with allure.step("Check bucket shows all versions"): with reporter.step("Check bucket shows all versions"):
versions = s3_client.list_objects_versions(bucket) versions = s3_client.list_objects_versions(bucket)
obj_versions = [version.get("VersionId") for version in versions if version.get("Key") == file_name] obj_versions = [version.get("VersionId") for version in versions if version.get("Key") == file_name]
assert ( assert (
obj_versions.sort() == [version_id_1, version_id_2, "null"].sort() obj_versions.sort() == [version_id_1, version_id_2, "null"].sort()
), f"Expected object has versions: {version_id_1, version_id_2, 'null'}" ), f"Expected object has versions: {version_id_1, version_id_2, 'null'}"
with allure.step("Get object"): with reporter.step("Get object"):
object_1 = s3_client.get_object(bucket, file_name, full_output=True) object_1 = s3_client.get_object(bucket, file_name, full_output=True)
assert object_1.get("VersionId") == version_id_2, f"Get object with version {version_id_2}" assert object_1.get("VersionId") == version_id_2, f"Get object with version {version_id_2}"
with allure.step("Get first version of object"): with reporter.step("Get first version of object"):
object_2 = s3_client.get_object(bucket, file_name, version_id_1, full_output=True) object_2 = s3_client.get_object(bucket, file_name, version_id_1, full_output=True)
assert object_2.get("VersionId") == version_id_1, f"Get object with version {version_id_1}" assert object_2.get("VersionId") == version_id_1, f"Get object with version {version_id_1}"
with allure.step("Get second version of object"): with reporter.step("Get second version of object"):
object_3 = s3_client.get_object(bucket, file_name, version_id_2, full_output=True) object_3 = s3_client.get_object(bucket, file_name, version_id_2, full_output=True)
assert object_3.get("VersionId") == version_id_2, f"Get object with version {version_id_2}" assert object_3.get("VersionId") == version_id_2, f"Get object with version {version_id_2}"

View file

@ -6,6 +6,7 @@ from re import fullmatch, match
import allure import allure
import pytest import pytest
import requests import requests
from frostfs_testlib import reporter
from frostfs_testlib.hosting import Hosting from frostfs_testlib.hosting import Hosting
from frostfs_testlib.resources.common import ASSETS_DIR from frostfs_testlib.resources.common import ASSETS_DIR
from frostfs_testlib.utils.env_utils import read_env_properties, save_env_properties from frostfs_testlib.utils.env_utils import read_env_properties, save_env_properties
@ -21,7 +22,7 @@ def test_binaries_versions(request: FixtureRequest, hosting: Hosting):
""" """
Compare binaries versions from external source (url) and deployed on servers. Compare binaries versions from external source (url) and deployed on servers.
""" """
with allure.step("Get binaries versions from servers"): with reporter.step("Get binaries versions from servers"):
got_versions = get_remote_binaries_versions(hosting) got_versions = get_remote_binaries_versions(hosting)
environment_dir = request.config.getoption("--alluredir") or ASSETS_DIR environment_dir = request.config.getoption("--alluredir") or ASSETS_DIR
@ -52,7 +53,7 @@ def test_binaries_versions(request: FixtureRequest, hosting: Hosting):
raise AssertionError(f"Found binaries with unexpected versions:\n{msg}") raise AssertionError(f"Found binaries with unexpected versions:\n{msg}")
@allure.step("Download versions info from {url}") @reporter.step("Download versions info from {url}")
def download_versions_info(url: str) -> dict: def download_versions_info(url: str) -> dict:
binaries_to_version = {} binaries_to_version = {}

View file

@ -2,6 +2,7 @@ import random
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.common import DEFAULT_WALLET_PASS from frostfs_testlib.resources.common import DEFAULT_WALLET_PASS
from frostfs_testlib.resources.error_patterns import SESSION_NOT_FOUND from frostfs_testlib.resources.error_patterns import SESSION_NOT_FOUND
from frostfs_testlib.steps.cli.container import create_container from frostfs_testlib.steps.cli.container import create_container
@ -31,16 +32,14 @@ class TestDynamicObjectSession(ClusterTestBase):
with a session token with a session token
""" """
with allure.step("Init wallet"): with reporter.step("Init wallet"):
wallet = default_wallet wallet = default_wallet
address = wallet_utils.get_last_address_from_wallet(wallet, "") address = wallet_utils.get_last_address_from_wallet(wallet, "")
with allure.step("Nodes Settlements"): with reporter.step("Nodes Settlements"):
session_token_node, container_node, non_container_node = random.sample( session_token_node, container_node, non_container_node = random.sample(self.cluster.storage_nodes, 3)
self.cluster.storage_nodes, 3
)
with allure.step("Create Session Token"): with reporter.step("Create Session Token"):
session_token = create_session_token( session_token = create_session_token(
shell=self.shell, shell=self.shell,
owner=address, owner=address,
@ -49,7 +48,7 @@ class TestDynamicObjectSession(ClusterTestBase):
rpc_endpoint=session_token_node.get_rpc_endpoint(), rpc_endpoint=session_token_node.get_rpc_endpoint(),
) )
with allure.step("Create Private Container"): with reporter.step("Create Private Container"):
un_locode = container_node.get_un_locode() un_locode = container_node.get_un_locode()
locode = "SPB" if un_locode == "RU LED" else un_locode.split()[1] locode = "SPB" if un_locode == "RU LED" else un_locode.split()[1]
placement_policy = ( placement_policy = (
@ -64,7 +63,7 @@ class TestDynamicObjectSession(ClusterTestBase):
rule=placement_policy, rule=placement_policy,
) )
with allure.step("Put Objects"): with reporter.step("Put Objects"):
file_path = generate_file(object_size.value) file_path = generate_file(object_size.value)
oid = put_object_to_random_node( oid = put_object_to_random_node(
wallet=wallet, wallet=wallet,
@ -81,7 +80,7 @@ class TestDynamicObjectSession(ClusterTestBase):
cluster=self.cluster, cluster=self.cluster,
) )
with allure.step("Node not in container but granted a session token"): with reporter.step("Node not in container but granted a session token"):
put_object( put_object(
wallet=wallet, wallet=wallet,
path=file_path, path=file_path,
@ -99,7 +98,7 @@ class TestDynamicObjectSession(ClusterTestBase):
session=session_token, session=session_token,
) )
with allure.step("Node in container and not granted a session token"): with reporter.step("Node in container and not granted a session token"):
with pytest.raises(Exception, match=SESSION_NOT_FOUND): with pytest.raises(Exception, match=SESSION_NOT_FOUND):
put_object( put_object(
wallet=wallet, wallet=wallet,
@ -119,7 +118,7 @@ class TestDynamicObjectSession(ClusterTestBase):
session=session_token, session=session_token,
) )
with allure.step("Node not in container and not granted a session token"): with reporter.step("Node not in container and not granted a session token"):
with pytest.raises(Exception, match=SESSION_NOT_FOUND): with pytest.raises(Exception, match=SESSION_NOT_FOUND):
put_object( put_object(
wallet=wallet, wallet=wallet,

View file

@ -2,6 +2,7 @@ import logging
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.error_patterns import ( from frostfs_testlib.resources.error_patterns import (
EXPIRED_SESSION_TOKEN, EXPIRED_SESSION_TOKEN,
MALFORMED_REQUEST, MALFORMED_REQUEST,
@ -69,7 +70,7 @@ def storage_objects(
file_path = generate_file(object_size.value) file_path = generate_file(object_size.value)
storage_objects = [] storage_objects = []
with allure.step("Put objects"): with reporter.step("Put objects"):
# upload couple objects # upload couple objects
for _ in range(3): for _ in range(3):
storage_object_id = put_object_to_random_node( storage_object_id = put_object_to_random_node(
@ -92,7 +93,7 @@ def storage_objects(
delete_objects(storage_objects, client_shell, cluster) delete_objects(storage_objects, client_shell, cluster)
@allure.step("Get ranges for test") @reporter.step("Get ranges for test")
def get_ranges(storage_object: StorageObjectInfo, max_object_size: int, shell: Shell, endpoint: str) -> list[str]: def get_ranges(storage_object: StorageObjectInfo, max_object_size: int, shell: Shell, endpoint: str) -> list[str]:
""" """
Returns ranges to test range/hash methods via static session Returns ranges to test range/hash methods via static session
@ -192,7 +193,7 @@ class TestObjectStaticSession(ClusterTestBase):
ranges_to_test = get_ranges(storage_object, max_object_size, self.shell, self.cluster.default_rpc_endpoint) ranges_to_test = get_ranges(storage_object, max_object_size, self.shell, self.cluster.default_rpc_endpoint)
for range_to_test in ranges_to_test: for range_to_test in ranges_to_test:
with allure.step(f"Check range {range_to_test}"): with reporter.step(f"Check range {range_to_test}"):
with expect_not_raises(): with expect_not_raises():
method_under_test( method_under_test(
user_wallet.path, user_wallet.path,
@ -432,7 +433,7 @@ class TestObjectStaticSession(ClusterTestBase):
object_id = storage_objects[0].oid object_id = storage_objects[0].oid
expiration = Lifetime(epoch + 1, epoch, epoch) expiration = Lifetime(epoch + 1, epoch, epoch)
with allure.step("Create session token"): with reporter.step("Create session token"):
token_expire_at_next_epoch = get_object_signed_token( token_expire_at_next_epoch = get_object_signed_token(
owner_wallet, owner_wallet,
user_wallet, user_wallet,
@ -444,7 +445,7 @@ class TestObjectStaticSession(ClusterTestBase):
expiration, expiration,
) )
with allure.step("Object should be available with session token after token creation"): with reporter.step("Object should be available with session token after token creation"):
with expect_not_raises(): with expect_not_raises():
head_object( head_object(
user_wallet.path, user_wallet.path,
@ -455,7 +456,7 @@ class TestObjectStaticSession(ClusterTestBase):
session=token_expire_at_next_epoch, session=token_expire_at_next_epoch,
) )
with allure.step("Object should be available at last epoch before session token expiration"): with reporter.step("Object should be available at last epoch before session token expiration"):
self.tick_epoch() self.tick_epoch()
with expect_not_raises(): with expect_not_raises():
head_object( head_object(
@ -467,7 +468,7 @@ class TestObjectStaticSession(ClusterTestBase):
session=token_expire_at_next_epoch, session=token_expire_at_next_epoch,
) )
with allure.step("Object should NOT be available after session token expiration epoch"): with reporter.step("Object should NOT be available after session token expiration epoch"):
self.tick_epoch() self.tick_epoch()
with pytest.raises(Exception, match=EXPIRED_SESSION_TOKEN): with pytest.raises(Exception, match=EXPIRED_SESSION_TOKEN):
head_object( head_object(
@ -498,7 +499,7 @@ class TestObjectStaticSession(ClusterTestBase):
object_id = storage_objects[0].oid object_id = storage_objects[0].oid
expiration = Lifetime(epoch + 2, epoch + 1, epoch) expiration = Lifetime(epoch + 2, epoch + 1, epoch)
with allure.step("Create session token"): with reporter.step("Create session token"):
token_start_at_next_epoch = get_object_signed_token( token_start_at_next_epoch = get_object_signed_token(
owner_wallet, owner_wallet,
user_wallet, user_wallet,
@ -510,7 +511,7 @@ class TestObjectStaticSession(ClusterTestBase):
expiration, expiration,
) )
with allure.step("Object should NOT be available with session token after token creation"): with reporter.step("Object should NOT be available with session token after token creation"):
with pytest.raises(Exception, match=MALFORMED_REQUEST): with pytest.raises(Exception, match=MALFORMED_REQUEST):
head_object( head_object(
user_wallet.path, user_wallet.path,
@ -521,7 +522,7 @@ class TestObjectStaticSession(ClusterTestBase):
session=token_start_at_next_epoch, session=token_start_at_next_epoch,
) )
with allure.step("Object should be available with session token starting from token nbf epoch"): with reporter.step("Object should be available with session token starting from token nbf epoch"):
self.tick_epoch() self.tick_epoch()
with expect_not_raises(): with expect_not_raises():
head_object( head_object(
@ -533,7 +534,7 @@ class TestObjectStaticSession(ClusterTestBase):
session=token_start_at_next_epoch, session=token_start_at_next_epoch,
) )
with allure.step("Object should be available at last epoch before session token expiration"): with reporter.step("Object should be available at last epoch before session token expiration"):
self.tick_epoch() self.tick_epoch()
with expect_not_raises(): with expect_not_raises():
head_object( head_object(
@ -545,7 +546,7 @@ class TestObjectStaticSession(ClusterTestBase):
session=token_start_at_next_epoch, session=token_start_at_next_epoch,
) )
with allure.step("Object should NOT be available after session token expiration epoch"): with reporter.step("Object should NOT be available after session token expiration epoch"):
self.tick_epoch() self.tick_epoch()
with pytest.raises(Exception, match=EXPIRED_SESSION_TOKEN): with pytest.raises(Exception, match=EXPIRED_SESSION_TOKEN):
head_object( head_object(

View file

@ -1,5 +1,5 @@
import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.shell import Shell from frostfs_testlib.shell import Shell
from frostfs_testlib.steps.acl import create_eacl, set_eacl, wait_for_cache_expired from frostfs_testlib.steps.acl import create_eacl, set_eacl, wait_for_cache_expired
@ -41,7 +41,7 @@ class TestSessionTokenContainer(ClusterTestBase):
""" """
Validate static session with create operation Validate static session with create operation
""" """
with allure.step("Create container with static session token"): with reporter.step("Create container with static session token"):
cid = create_container( cid = create_container(
user_wallet.path, user_wallet.path,
session_token=static_sessions[ContainerVerb.CREATE], session_token=static_sessions[ContainerVerb.CREATE],
@ -68,7 +68,7 @@ class TestSessionTokenContainer(ClusterTestBase):
""" """
Validate static session without create operation Validate static session without create operation
""" """
with allure.step("Try create container with static session token without PUT rule"): with reporter.step("Try create container with static session token without PUT rule"):
for verb in [verb for verb in ContainerVerb if verb != ContainerVerb.CREATE]: for verb in [verb for verb in ContainerVerb if verb != ContainerVerb.CREATE]:
with pytest.raises(Exception): with pytest.raises(Exception):
create_container( create_container(
@ -87,7 +87,7 @@ class TestSessionTokenContainer(ClusterTestBase):
""" """
Validate static session with create operation for other wallet Validate static session with create operation for other wallet
""" """
with allure.step("Try create container with static session token without PUT rule"): with reporter.step("Try create container with static session token without PUT rule"):
with pytest.raises(Exception): with pytest.raises(Exception):
create_container( create_container(
stranger_wallet.path, stranger_wallet.path,
@ -106,14 +106,14 @@ class TestSessionTokenContainer(ClusterTestBase):
""" """
Validate static session with delete operation Validate static session with delete operation
""" """
with allure.step("Create container"): with reporter.step("Create container"):
cid = create_container( cid = create_container(
owner_wallet.path, owner_wallet.path,
shell=self.shell, shell=self.shell,
endpoint=self.cluster.default_rpc_endpoint, endpoint=self.cluster.default_rpc_endpoint,
wait_for_creation=False, wait_for_creation=False,
) )
with allure.step("Delete container with static session token"): with reporter.step("Delete container with static session token"):
delete_container( delete_container(
wallet=user_wallet.path, wallet=user_wallet.path,
cid=cid, cid=cid,
@ -139,7 +139,7 @@ class TestSessionTokenContainer(ClusterTestBase):
""" """
Validate static session with set eacl operation Validate static session with set eacl operation
""" """
with allure.step("Create container"): with reporter.step("Create container"):
cid = create_container( cid = create_container(
owner_wallet.path, owner_wallet.path,
basic_acl=PUBLIC_ACL, basic_acl=PUBLIC_ACL,
@ -149,7 +149,7 @@ class TestSessionTokenContainer(ClusterTestBase):
file_path = generate_file(simple_object_size.value) file_path = generate_file(simple_object_size.value)
assert can_put_object(stranger_wallet.path, cid, file_path, self.shell, self.cluster) assert can_put_object(stranger_wallet.path, cid, file_path, self.shell, self.cluster)
with allure.step("Deny all operations for other via eACL"): with reporter.step("Deny all operations for other via eACL"):
eacl_deny = [EACLRule(access=EACLAccess.DENY, role=EACLRole.OTHERS, operation=op) for op in EACLOperation] eacl_deny = [EACLRule(access=EACLAccess.DENY, role=EACLRole.OTHERS, operation=op) for op in EACLOperation]
set_eacl( set_eacl(
user_wallet.path, user_wallet.path,

View file

@ -4,6 +4,7 @@ from datetime import datetime
import allure import allure
import pytest import pytest
from frostfs_testlib import reporter
from frostfs_testlib.hosting import Host from frostfs_testlib.hosting import Host
from frostfs_testlib.testing.cluster_test_base import Cluster from frostfs_testlib.testing.cluster_test_base import Cluster
from frostfs_testlib.testing.parallel import parallel from frostfs_testlib.testing.parallel import parallel
@ -51,7 +52,7 @@ class TestLogs:
), f"The following hosts contains contain critical errors in system logs: {', '.join(hosts_with_problems)}" ), f"The following hosts contains contain critical errors in system logs: {', '.join(hosts_with_problems)}"
def _collect_logs_on_host(self, host: Host, logs_dir: str, regex: str, since: datetime, until: datetime): def _collect_logs_on_host(self, host: Host, logs_dir: str, regex: str, since: datetime, until: datetime):
with allure.step(f"Get logs from {host.config.address}"): with reporter.step(f"Get logs from {host.config.address}"):
logs = host.get_filtered_logs(regex, since, until) logs = host.get_filtered_logs(regex, since, until)
if not logs: if not logs:
@ -66,4 +67,4 @@ class TestLogs:
# Zip all files and attach to Allure because it is more convenient to download a single # Zip all files and attach to Allure because it is more convenient to download a single
# zip with all logs rather than mess with individual logs files per service or node # zip with all logs rather than mess with individual logs files per service or node
logs_zip_file_path = shutil.make_archive(logs_dir, "zip", logs_dir) logs_zip_file_path = shutil.make_archive(logs_dir, "zip", logs_dir)
allure.attach.file(logs_zip_file_path, name="logs.zip", extension="zip") reporter.attach(logs_zip_file_path, "logs.zip")