forked from TrueCloudLab/frostfs-testcases
Compare commits
10 commits
64bc778116
...
35c3d4657f
Author | SHA1 | Date | |
---|---|---|---|
35c3d4657f | |||
1ee5f73243 | |||
aef9d43979 | |||
0e72717dcd | |||
b33514df3c | |||
09acd6f283 | |||
b7669fc96f | |||
75508cc70c | |||
6b83a89b94 | |||
77126f2706 |
37 changed files with 1502 additions and 232 deletions
0
__init__.py
Normal file
0
__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
import os
|
||||
|
||||
TESTS_BASE_PATH = os.path.dirname(os.path.relpath(__file__))
|
|
@ -6,7 +6,7 @@ from frostfs_testlib.storage.cluster import Cluster
|
|||
from frostfs_testlib.storage.dataclasses import ape
|
||||
from frostfs_testlib.storage.dataclasses.wallet import WalletInfo
|
||||
|
||||
from pytest_tests.helpers.object_access import (
|
||||
from ..helpers.object_access import (
|
||||
can_delete_object,
|
||||
can_get_head_object,
|
||||
can_get_object,
|
||||
|
|
|
@ -2,13 +2,23 @@ from dataclasses import dataclass
|
|||
|
||||
from frostfs_testlib.steps.cli.container import DEFAULT_PLACEMENT_RULE
|
||||
from frostfs_testlib.storage.cluster import Cluster
|
||||
from frostfs_testlib.storage.dataclasses import ape
|
||||
|
||||
APE_PUBLIC_READ_WRITE = [ape.Rule(ape.Verb.ALLOW, ape.ObjectOperations.WILDCARD_ALL)]
|
||||
|
||||
|
||||
@dataclass
|
||||
class ContainerSpec:
|
||||
rule: str = DEFAULT_PLACEMENT_RULE
|
||||
# TODO: Deprecated
|
||||
basic_acl: str = None
|
||||
# TODO: Deprecated
|
||||
allow_owner_via_ape: bool = False
|
||||
ape_rules: list[ape.Rule] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.ape_rules is None:
|
||||
self.ape_rules = []
|
||||
|
||||
def parsed_rule(self, cluster: Cluster):
|
||||
if self.rule is None:
|
||||
|
@ -21,3 +31,18 @@ class ContainerSpec:
|
|||
parsed_rule = parsed_rule.replace(sub, replacement)
|
||||
|
||||
return parsed_rule
|
||||
|
||||
def __repr__(self):
|
||||
spec_info: list[str] = []
|
||||
|
||||
if self.rule:
|
||||
spec_info.append(f"rule='{self.rule}'")
|
||||
if self.ape_rules:
|
||||
ape_rules_list = ", ".join([f"'{rule.as_string()}'" for rule in self.ape_rules])
|
||||
spec_info.append(f"ape_rules=[{ape_rules_list}]")
|
||||
|
||||
return f"ContainerSpec({', '.join(spec_info)})"
|
||||
|
||||
|
||||
class ContainerSpecs:
|
||||
PublicReadWrite = ContainerSpec(ape_rules=APE_PUBLIC_READ_WRITE)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import os
|
||||
|
||||
from .. import TESTS_BASE_PATH
|
||||
|
||||
TEST_CYCLES_COUNT = int(os.getenv("TEST_CYCLES_COUNT", "1"))
|
||||
|
||||
DEVENV_PATH = os.getenv("DEVENV_PATH", os.path.join("..", "frostfs-dev-env"))
|
||||
S3_POLICY_FILE_LOCATION = os.path.join(TESTS_BASE_PATH, "resources/files/policy.json")
|
||||
|
|
|
@ -8,8 +8,7 @@ from frostfs_testlib.storage.cluster import Cluster
|
|||
from frostfs_testlib.storage.dataclasses.wallet import WalletInfo
|
||||
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
||||
|
||||
from pytest_tests.helpers.container_access import assert_full_access_to_container, assert_no_access_to_container, assert_read_only_container
|
||||
|
||||
from ....helpers.container_access import assert_full_access_to_container, assert_no_access_to_container, assert_read_only_container
|
||||
from ....helpers.container_spec import ContainerSpec
|
||||
|
||||
|
||||
|
|
|
@ -12,13 +12,12 @@ from frostfs_testlib.utils import wallet_utils
|
|||
from frostfs_testlib.utils.failover_utils import wait_object_replication
|
||||
from frostfs_testlib.utils.file_utils import TestFile
|
||||
|
||||
from pytest_tests.helpers.container_access import (
|
||||
from ....helpers.container_access import (
|
||||
ALL_OBJECT_OPERATIONS,
|
||||
assert_access_to_container,
|
||||
assert_full_access_to_container,
|
||||
assert_no_access_to_container,
|
||||
)
|
||||
|
||||
from ....helpers.container_spec import ContainerSpec
|
||||
|
||||
|
||||
|
|
|
@ -10,17 +10,16 @@ from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
|||
from frostfs_testlib.testing.test_control import expect_not_raises
|
||||
from frostfs_testlib.utils.file_utils import TestFile
|
||||
|
||||
from pytest_tests.helpers.bearer_token import create_bearer_token
|
||||
from pytest_tests.helpers.container_access import (
|
||||
from ....helpers.bearer_token import create_bearer_token
|
||||
from ....helpers.container_access import (
|
||||
ALL_OBJECT_OPERATIONS,
|
||||
FULL_ACCESS,
|
||||
assert_access_to_container,
|
||||
assert_full_access_to_container,
|
||||
assert_no_access_to_container,
|
||||
)
|
||||
from pytest_tests.helpers.object_access import OBJECT_ACCESS_DENIED
|
||||
|
||||
from ....helpers.container_spec import ContainerSpec
|
||||
from ....helpers.object_access import OBJECT_ACCESS_DENIED
|
||||
|
||||
|
||||
@pytest.mark.nightly
|
||||
|
|
|
@ -7,8 +7,8 @@ from frostfs_testlib.storage.dataclasses.wallet import WalletInfo
|
|||
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
||||
from frostfs_testlib.utils.file_utils import TestFile
|
||||
|
||||
from pytest_tests.helpers.bearer_token import create_bearer_token
|
||||
from pytest_tests.helpers.container_access import (
|
||||
from ....helpers.bearer_token import create_bearer_token
|
||||
from ....helpers.container_access import (
|
||||
ALL_OBJECT_OPERATIONS,
|
||||
assert_access_to_container,
|
||||
assert_full_access_to_container,
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
import json
|
||||
import time
|
||||
|
||||
import allure
|
||||
import pytest
|
||||
from frostfs_testlib import reporter
|
||||
from frostfs_testlib.cli.frostfs_cli.cli import FrostfsCli
|
||||
from frostfs_testlib.resources.common import MORPH_BLOCK_TIME
|
||||
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
|
||||
from frostfs_testlib.shell import Shell
|
||||
from frostfs_testlib.steps.cli.container import create_container, search_nodes_with_container
|
||||
from frostfs_testlib.steps.cli.container import search_nodes_with_container
|
||||
from frostfs_testlib.steps.cli.object import put_object_to_random_node
|
||||
from frostfs_testlib.storage.cluster import Cluster, ClusterNode
|
||||
from frostfs_testlib.storage.dataclasses import ape
|
||||
from frostfs_testlib.storage.dataclasses.wallet import WalletInfo
|
||||
from frostfs_testlib.testing.parallel import parallel
|
||||
from frostfs_testlib.utils import datetime_utils
|
||||
|
||||
from ...helpers.container_spec import ContainerSpec
|
||||
|
||||
OBJECT_COUNT = 5
|
||||
|
||||
|
@ -48,81 +40,6 @@ def test_wallet(default_wallet: WalletInfo, other_wallet: WalletInfo, role: ape.
|
|||
return role_to_wallet_map[role]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def container(
|
||||
default_wallet: WalletInfo,
|
||||
frostfs_cli: FrostfsCli,
|
||||
client_shell: Shell,
|
||||
cluster: Cluster,
|
||||
request: pytest.FixtureRequest,
|
||||
rpc_endpoint: str,
|
||||
) -> str:
|
||||
container_spec = _get_container_spec(request)
|
||||
cid = _create_container_by_spec(default_wallet, client_shell, cluster, rpc_endpoint, container_spec)
|
||||
if container_spec.allow_owner_via_ape:
|
||||
_allow_owner_via_ape(frostfs_cli, cluster, cid)
|
||||
|
||||
return cid
|
||||
|
||||
|
||||
def _create_container_by_spec(
|
||||
default_wallet: WalletInfo, client_shell: Shell, cluster: Cluster, rpc_endpoint: str, container_spec: ContainerSpec
|
||||
) -> str:
|
||||
# TODO: add container spec to step message
|
||||
with reporter.step("Create container"):
|
||||
cid = create_container(
|
||||
default_wallet, client_shell, rpc_endpoint, basic_acl=container_spec.basic_acl, rule=container_spec.parsed_rule(cluster)
|
||||
)
|
||||
|
||||
with reporter.step("Search nodes holding the container"):
|
||||
container_holder_nodes = search_nodes_with_container(default_wallet, cid, client_shell, cluster.default_rpc_endpoint, cluster)
|
||||
report_data = {node.id: node.host_ip for node in container_holder_nodes}
|
||||
|
||||
reporter.attach(json.dumps(report_data, indent=2), "container_nodes.json")
|
||||
|
||||
return cid
|
||||
|
||||
|
||||
def _get_container_spec(request: pytest.FixtureRequest) -> ContainerSpec:
|
||||
container_marker = request.node.get_closest_marker("container")
|
||||
# let default container to be public at the moment
|
||||
container_spec = ContainerSpec(basic_acl=PUBLIC_ACL)
|
||||
|
||||
if container_marker:
|
||||
if len(container_marker.args) != 1:
|
||||
raise RuntimeError(f"Something wrong with container marker: {container_marker}")
|
||||
container_spec = container_marker.args[0]
|
||||
|
||||
if "param" in request.__dict__:
|
||||
container_spec = request.param
|
||||
|
||||
if not container_spec:
|
||||
raise RuntimeError(
|
||||
f"""Container specification is empty.
|
||||
Either add @pytest.mark.container(ContainerSpec(...)) or
|
||||
@pytest.mark.parametrize(\"container\", [ContainerSpec(...)], indirect=True) decorator"""
|
||||
)
|
||||
|
||||
return container_spec
|
||||
|
||||
|
||||
def _allow_owner_via_ape(frostfs_cli: FrostfsCli, cluster: Cluster, container: str):
|
||||
with reporter.step("Create allow APE rule for container owner"):
|
||||
role_condition = ape.Condition.by_role(ape.Role.OWNER)
|
||||
deny_rule = ape.Rule(ape.Verb.ALLOW, ape.ObjectOperations.WILDCARD_ALL, role_condition)
|
||||
|
||||
frostfs_cli.ape_manager.add(
|
||||
cluster.default_rpc_endpoint,
|
||||
deny_rule.chain_id,
|
||||
target_name=container,
|
||||
target_type="container",
|
||||
rule=deny_rule.as_string(),
|
||||
)
|
||||
|
||||
with reporter.step("Wait for one block"):
|
||||
time.sleep(datetime_utils.parse_time(MORPH_BLOCK_TIME))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def objects(container: str, default_wallet: WalletInfo, client_shell: Shell, cluster: Cluster, file_path: str):
|
||||
with reporter.step("Add test objects to container"):
|
||||
|
|
1006
pytest_tests/testsuites/ape/test_ape.py
Normal file
1006
pytest_tests/testsuites/ape/test_ape.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import time
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional
|
||||
|
||||
|
@ -14,15 +14,23 @@ from frostfs_testlib.credentials.interfaces import CredentialsProvider, User
|
|||
from frostfs_testlib.healthcheck.interfaces import Healthcheck
|
||||
from frostfs_testlib.hosting import Hosting
|
||||
from frostfs_testlib.resources import optionals
|
||||
from frostfs_testlib.resources.common import COMPLEX_OBJECT_CHUNKS_COUNT, COMPLEX_OBJECT_TAIL_SIZE, SIMPLE_OBJECT_SIZE
|
||||
from frostfs_testlib.resources.common import COMPLEX_OBJECT_CHUNKS_COUNT, COMPLEX_OBJECT_TAIL_SIZE, MORPH_BLOCK_TIME, SIMPLE_OBJECT_SIZE
|
||||
from frostfs_testlib.s3 import AwsCliClient, Boto3ClientWrapper, S3ClientWrapper, VersioningStatus
|
||||
from frostfs_testlib.s3.interfaces import BucketContainerResolver
|
||||
from frostfs_testlib.shell import LocalShell, Shell
|
||||
from frostfs_testlib.steps.cli.container import DEFAULT_EC_PLACEMENT_RULE, DEFAULT_PLACEMENT_RULE, FROSTFS_CLI_EXEC
|
||||
from frostfs_testlib.steps.cli.container import (
|
||||
DEFAULT_EC_PLACEMENT_RULE,
|
||||
DEFAULT_PLACEMENT_RULE,
|
||||
FROSTFS_CLI_EXEC,
|
||||
create_container,
|
||||
search_nodes_with_container,
|
||||
)
|
||||
from frostfs_testlib.steps.cli.object import get_netmap_netinfo
|
||||
from frostfs_testlib.steps.epoch import ensure_fresh_epoch
|
||||
from frostfs_testlib.steps.s3 import s3_helper
|
||||
from frostfs_testlib.storage.cluster import Cluster, ClusterNode
|
||||
from frostfs_testlib.storage.controllers.cluster_state_controller import ClusterStateController
|
||||
from frostfs_testlib.storage.dataclasses import ape
|
||||
from frostfs_testlib.storage.dataclasses.frostfs_services import StorageNode
|
||||
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
|
||||
from frostfs_testlib.storage.dataclasses.policy import PlacementPolicy
|
||||
|
@ -31,11 +39,12 @@ from frostfs_testlib.storage.grpc_operations.client_wrappers import CliClientWra
|
|||
from frostfs_testlib.storage.grpc_operations.interfaces import GrpcClientWrapper
|
||||
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
||||
from frostfs_testlib.testing.parallel import parallel
|
||||
from frostfs_testlib.testing.test_control import run_optionally, wait_for_success
|
||||
from frostfs_testlib.utils import env_utils, string_utils, version_utils
|
||||
from frostfs_testlib.testing.test_control import cached_fixture, run_optionally, wait_for_success
|
||||
from frostfs_testlib.utils import datetime_utils, env_utils, string_utils, version_utils
|
||||
from frostfs_testlib.utils.file_utils import TestFile, generate_file
|
||||
|
||||
from pytest_tests.resources.common import TEST_CYCLES_COUNT
|
||||
from ..helpers.container_spec import ContainerSpec, ContainerSpecs
|
||||
from ..resources.common import TEST_CYCLES_COUNT
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
|
||||
|
@ -141,15 +150,16 @@ def require_multiple_interfaces(cluster: Cluster):
|
|||
interfaces = cluster.cluster_nodes[0].host.config.interfaces
|
||||
if "internal1" not in interfaces or "data1" not in interfaces:
|
||||
pytest.skip("This test requires multiple internal and data interfaces")
|
||||
yield
|
||||
return
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@cached_fixture(optionals.OPTIONAL_CACHE_FIXTURES)
|
||||
def max_object_size(cluster: Cluster, client_shell: Shell) -> int:
|
||||
storage_node = cluster.storage_nodes[0]
|
||||
wallet = WalletInfo.from_node(storage_node)
|
||||
net_info = get_netmap_netinfo(wallet=wallet, endpoint=storage_node.get_rpc_endpoint(), shell=client_shell)
|
||||
yield net_info["maximum_object_size"]
|
||||
return net_info["maximum_object_size"]
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
|
@ -158,11 +168,6 @@ def simple_object_size(max_object_size: int) -> ObjectSize:
|
|||
return ObjectSize("simple", size)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def file_path(object_size: ObjectSize) -> TestFile:
|
||||
return generate_file(object_size.value)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def complex_object_size(max_object_size: int) -> ObjectSize:
|
||||
size = max_object_size * int(COMPLEX_OBJECT_CHUNKS_COUNT) + int(COMPLEX_OBJECT_TAIL_SIZE)
|
||||
|
@ -181,6 +186,17 @@ def object_size(simple_object_size: ObjectSize, complex_object_size: ObjectSize,
|
|||
return complex_object_size
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def test_file(object_size: ObjectSize) -> TestFile:
|
||||
return generate_file(object_size.value)
|
||||
|
||||
|
||||
# Deprecated. Please migrate all to test_file
|
||||
@pytest.fixture()
|
||||
def file_path(test_file: TestFile) -> TestFile:
|
||||
return test_file
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def rep_placement_policy() -> PlacementPolicy:
|
||||
return PlacementPolicy("rep", DEFAULT_PLACEMENT_RULE)
|
||||
|
@ -418,6 +434,7 @@ def readiness_on_node(cluster_node: ClusterNode):
|
|||
|
||||
@reporter.step("Prepare default user with wallet")
|
||||
@pytest.fixture(scope="session")
|
||||
@cached_fixture(optionals.OPTIONAL_CACHE_FIXTURES)
|
||||
def default_user(credentials_provider: CredentialsProvider, cluster: Cluster) -> User:
|
||||
user = User(string_utils.unique_name("user-"))
|
||||
node = cluster.cluster_nodes[0]
|
||||
|
@ -469,3 +486,142 @@ def bucket_container_resolver(node_under_test: ClusterNode) -> BucketContainerRe
|
|||
resolver_cls = plugins.load_plugin("frostfs.testlib.bucket_cid_resolver", node_under_test.host.config.product)
|
||||
resolver: BucketContainerResolver = resolver_cls()
|
||||
return resolver
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def container(
|
||||
default_wallet: WalletInfo,
|
||||
frostfs_cli: FrostfsCli,
|
||||
client_shell: Shell,
|
||||
cluster: Cluster,
|
||||
request: pytest.FixtureRequest,
|
||||
rpc_endpoint: str,
|
||||
) -> str:
|
||||
with reporter.step("Get container specification for test"):
|
||||
container_spec = _get_container_spec(request)
|
||||
|
||||
with reporter.step("Create container"):
|
||||
cid = _create_container_by_spec(default_wallet, client_shell, cluster, rpc_endpoint, container_spec)
|
||||
# TODO: deprecate this. Use generic ContainerSpec.ape_rule param
|
||||
if container_spec.allow_owner_via_ape:
|
||||
with reporter.step("Allow owner via APE on container"):
|
||||
_allow_owner_via_ape(frostfs_cli, cluster, cid)
|
||||
|
||||
with reporter.step("Apply APE rules for container"):
|
||||
if container_spec.ape_rules:
|
||||
_apply_ape_rules(frostfs_cli, cluster, cid, container_spec.ape_rules)
|
||||
|
||||
# Add marker if we want to run all tests with container
|
||||
request.node.add_marker("requires_container")
|
||||
|
||||
return cid
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def container_module_scope(
|
||||
default_wallet: WalletInfo,
|
||||
frostfs_cli: FrostfsCli,
|
||||
client_shell: Shell,
|
||||
cluster: Cluster,
|
||||
request: pytest.FixtureRequest,
|
||||
rpc_endpoint: str,
|
||||
) -> str:
|
||||
with reporter.step("Get container specification for test"):
|
||||
container_spec = _get_container_spec(request)
|
||||
|
||||
with reporter.step("Create container"):
|
||||
cid = _create_container_by_spec(default_wallet, client_shell, cluster, rpc_endpoint, container_spec)
|
||||
# TODO: deprecate this. Use generic ContainerSpec.ape_rule param
|
||||
if container_spec.allow_owner_via_ape:
|
||||
with reporter.step("Allow owner via APE on container"):
|
||||
_allow_owner_via_ape(frostfs_cli, cluster, cid)
|
||||
|
||||
with reporter.step("Apply APE rules for container"):
|
||||
if container_spec.ape_rules:
|
||||
_apply_ape_rules(frostfs_cli, cluster, cid, container_spec.ape_rules)
|
||||
|
||||
# Add marker if we want to run all tests with container
|
||||
request.node.add_marker("requires_container")
|
||||
|
||||
return cid
|
||||
|
||||
|
||||
def _apply_ape_rules(frostfs_cli: FrostfsCli, cluster: Cluster, container: str, ape_rules: list[ape.Rule]):
|
||||
for ape_rule in ape_rules:
|
||||
rule_str = ape_rule.as_string()
|
||||
with reporter.step(f"Apply APE rule '{rule_str}' for container {container}"):
|
||||
frostfs_cli.ape_manager.add(
|
||||
cluster.default_rpc_endpoint,
|
||||
ape_rule.chain_id,
|
||||
target_name=container,
|
||||
target_type="container",
|
||||
rule=rule_str,
|
||||
)
|
||||
|
||||
with reporter.step("Wait for one block"):
|
||||
time.sleep(datetime_utils.parse_time(MORPH_BLOCK_TIME))
|
||||
|
||||
|
||||
def _create_container_by_spec(
|
||||
default_wallet: WalletInfo, client_shell: Shell, cluster: Cluster, rpc_endpoint: str, container_spec: ContainerSpec
|
||||
) -> str:
|
||||
with reporter.step(f"Create container by spec {container_spec}"):
|
||||
cid = create_container(default_wallet, client_shell, rpc_endpoint, container_spec.parsed_rule(cluster))
|
||||
|
||||
with reporter.step("Search nodes holding the container"):
|
||||
container_holder_nodes = search_nodes_with_container(default_wallet, cid, client_shell, cluster.default_rpc_endpoint, cluster)
|
||||
report_data = {node.id: node.host_ip for node in container_holder_nodes}
|
||||
|
||||
reporter.attach(json.dumps(report_data, indent=2), "container_nodes.json")
|
||||
|
||||
return cid
|
||||
|
||||
|
||||
def _get_container_spec(request: pytest.FixtureRequest) -> ContainerSpec:
|
||||
container_marker = request.node.get_closest_marker("container")
|
||||
# let default container to be public at the moment
|
||||
container_spec = ContainerSpecs.PublicReadWrite
|
||||
|
||||
if container_marker:
|
||||
if len(container_marker.args) != 1:
|
||||
raise RuntimeError(f"Something wrong with container marker: {container_marker}")
|
||||
container_spec = container_marker.args[0]
|
||||
|
||||
if "param" in request.__dict__:
|
||||
container_spec = request.param
|
||||
|
||||
if not container_spec:
|
||||
raise RuntimeError(
|
||||
f"""Container specification is empty.
|
||||
Either add @pytest.mark.container(ContainerSpec(...)) or
|
||||
@pytest.mark.parametrize(\"container\", [ContainerSpec(...)], indirect=True) decorator"""
|
||||
)
|
||||
|
||||
return container_spec
|
||||
|
||||
|
||||
def _allow_owner_via_ape(frostfs_cli: FrostfsCli, cluster: Cluster, container: str):
|
||||
with reporter.step("Create allow APE rule for container owner"):
|
||||
role_condition = ape.Condition.by_role(ape.Role.OWNER)
|
||||
ape_rule = ape.Rule(ape.Verb.ALLOW, ape.ObjectOperations.WILDCARD_ALL, role_condition)
|
||||
|
||||
frostfs_cli.ape_manager.add(
|
||||
cluster.default_rpc_endpoint,
|
||||
ape_rule.chain_id,
|
||||
target_name=container,
|
||||
target_type="container",
|
||||
rule=ape_rule.as_string(),
|
||||
)
|
||||
|
||||
with reporter.step("Wait for one block"):
|
||||
time.sleep(datetime_utils.parse_time(MORPH_BLOCK_TIME))
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def new_epoch(client_shell: Shell, cluster: Cluster) -> int:
|
||||
return ensure_fresh_epoch(client_shell, cluster)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def new_epoch_module_scope(client_shell: Shell, cluster: Cluster) -> int:
|
||||
return ensure_fresh_epoch(client_shell, cluster)
|
||||
|
|
0
pytest_tests/testsuites/container/__init__.py
Normal file
0
pytest_tests/testsuites/container/__init__.py
Normal file
|
@ -13,7 +13,7 @@ from frostfs_testlib.steps.cli.container import (
|
|||
from frostfs_testlib.storage.dataclasses.wallet import WalletInfo
|
||||
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
||||
|
||||
from pytest_tests.helpers.utility import placement_policy_from_container
|
||||
from ...helpers.utility import placement_policy_from_container
|
||||
|
||||
|
||||
@pytest.mark.nightly
|
||||
|
|
|
@ -16,8 +16,8 @@ from frostfs_testlib.testing.test_control import wait_for_success
|
|||
from frostfs_testlib.utils.cli_utils import parse_netmap_output
|
||||
from frostfs_testlib.utils.file_utils import generate_file
|
||||
|
||||
from pytest_tests.helpers.utility import placement_policy_from_container
|
||||
from pytest_tests.resources.policy_error_patterns import NOT_ENOUGH_TO_SELECT, NOT_FOUND_FILTER, NOT_FOUND_SELECTOR, NOT_PARSE_POLICY
|
||||
from ...helpers.utility import placement_policy_from_container
|
||||
from ...resources.policy_error_patterns import NOT_ENOUGH_TO_SELECT, NOT_FOUND_FILTER, NOT_FOUND_SELECTOR, NOT_PARSE_POLICY
|
||||
|
||||
|
||||
@pytest.mark.nightly
|
||||
|
|
0
pytest_tests/testsuites/failovers/__init__.py
Normal file
0
pytest_tests/testsuites/failovers/__init__.py
Normal file
|
@ -34,6 +34,8 @@ from frostfs_testlib.utils.failover_utils import wait_object_replication
|
|||
from frostfs_testlib.utils.file_keeper import FileKeeper
|
||||
from frostfs_testlib.utils.file_utils import generate_file, get_file_hash
|
||||
|
||||
from ...resources.common import S3_POLICY_FILE_LOCATION
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
stopped_nodes: list[StorageNode] = []
|
||||
|
||||
|
@ -95,9 +97,7 @@ class TestFailoverStorage(ClusterTestBase):
|
|||
)
|
||||
|
||||
with reporter.step("Check object data is not corrupted"):
|
||||
got_file_path = get_object(
|
||||
wallet, cid, oid, endpoint=replicated_nodes[0].get_rpc_endpoint(), shell=self.shell
|
||||
)
|
||||
got_file_path = get_object(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)
|
||||
|
||||
with reporter.step("Return all hosts"):
|
||||
|
@ -105,12 +105,10 @@ class TestFailoverStorage(ClusterTestBase):
|
|||
|
||||
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)
|
||||
got_file_path = get_object(
|
||||
wallet, cid, oid, shell=self.shell, endpoint=replicated_nodes[0].get_rpc_endpoint()
|
||||
)
|
||||
got_file_path = get_object(wallet, cid, oid, shell=self.shell, endpoint=replicated_nodes[0].get_rpc_endpoint())
|
||||
assert get_file_hash(source_file_path) == get_file_hash(got_file_path)
|
||||
|
||||
@pytest.mark.parametrize("s3_policy", ["pytest_tests/resources/files/policy.json"], indirect=True)
|
||||
@pytest.mark.parametrize("s3_policy", [S3_POLICY_FILE_LOCATION], indirect=True)
|
||||
@allure.title("Do not ignore unhealthy tree endpoints (s3_client={s3_client})")
|
||||
def test_unhealthy_tree(
|
||||
self,
|
||||
|
@ -151,7 +149,7 @@ class TestFailoverStorage(ClusterTestBase):
|
|||
wallet=default_wallet,
|
||||
shell=self.shell,
|
||||
endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(),
|
||||
bucket_container_resolver=bucket_container_resolver
|
||||
bucket_container_resolver=bucket_container_resolver,
|
||||
)[0]
|
||||
|
||||
with reporter.step("Turn off all storage nodes except bucket node"):
|
||||
|
@ -282,9 +280,7 @@ class TestEmptyMap(ClusterTestBase):
|
|||
cluster_state_controller.stop_services_of_type(StorageNode)
|
||||
|
||||
with reporter.step("Remove all nodes from network map"):
|
||||
remove_nodes_from_map_morph(
|
||||
shell=self.shell, cluster=self.cluster, remove_nodes=self.cluster.services(StorageNode)
|
||||
)
|
||||
remove_nodes_from_map_morph(shell=self.shell, cluster=self.cluster, remove_nodes=self.cluster.services(StorageNode))
|
||||
|
||||
with reporter.step("Return all storage nodes to network map"):
|
||||
self.return_nodes_after_stop_with_check_empty_map(cluster_state_controller)
|
||||
|
@ -465,9 +461,7 @@ class TestStorageDataLoss(ClusterTestBase):
|
|||
s3_client.put_object(bucket, complex_object_path)
|
||||
|
||||
with reporter.step("Check objects are in bucket"):
|
||||
s3_helper.check_objects_in_bucket(
|
||||
s3_client, bucket, expected_objects=[simple_object_key, complex_object_key]
|
||||
)
|
||||
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[simple_object_key, complex_object_key])
|
||||
|
||||
with reporter.step("Stop storage services on all nodes"):
|
||||
cluster_state_controller.stop_services_of_type(StorageNode)
|
||||
|
@ -581,17 +575,13 @@ class TestStorageDataLoss(ClusterTestBase):
|
|||
exception_messages.append(f"Shard {shard} changed status to {status}")
|
||||
|
||||
with reporter.step("No related errors should be in log"):
|
||||
if node_under_test.host.is_message_in_logs(
|
||||
message_regex=r"\Wno such file or directory\W", since=test_start_time
|
||||
):
|
||||
if node_under_test.host.is_message_in_logs(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")
|
||||
|
||||
with reporter.step("Pass test if no errors found"):
|
||||
assert not exception_messages, "\n".join(exception_messages)
|
||||
|
||||
@allure.title(
|
||||
"Loss of one node should trigger use of tree and storage service in another node (s3_client={s3_client})"
|
||||
)
|
||||
@allure.title("Loss of one node should trigger use of tree and storage service in another node (s3_client={s3_client})")
|
||||
def test_s3_one_endpoint_loss(
|
||||
self,
|
||||
bucket,
|
||||
|
@ -613,7 +603,7 @@ class TestStorageDataLoss(ClusterTestBase):
|
|||
put_object = s3_client.put_object(bucket, file_path)
|
||||
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
|
||||
|
||||
@pytest.mark.parametrize("s3_policy", ["pytest_tests/resources/files/policy.json"], indirect=True)
|
||||
@pytest.mark.parametrize("s3_policy", [S3_POLICY_FILE_LOCATION], indirect=True)
|
||||
@allure.title("After Pilorama.db loss on one node object is retrievable (s3_client={s3_client})")
|
||||
def test_s3_one_pilorama_loss(
|
||||
self,
|
||||
|
|
0
pytest_tests/testsuites/management/__init__.py
Normal file
0
pytest_tests/testsuites/management/__init__.py
Normal file
|
@ -44,7 +44,7 @@ from frostfs_testlib.utils import string_utils
|
|||
from frostfs_testlib.utils.failover_utils import wait_object_replication
|
||||
from frostfs_testlib.utils.file_utils import generate_file
|
||||
|
||||
from pytest_tests.helpers.utility import wait_for_gc_pass_on_storage_nodes
|
||||
from ...helpers.utility import wait_for_gc_pass_on_storage_nodes
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
check_nodes: list[StorageNode] = []
|
||||
|
|
|
@ -4,11 +4,11 @@ import allure
|
|||
from frostfs_testlib.testing.parallel import parallel
|
||||
import pytest
|
||||
from frostfs_testlib import reporter
|
||||
from frostfs_testlib.steps.cli.container import create_container, search_nodes_with_container
|
||||
from frostfs_testlib.steps.cli.container import create_container, delete_container, search_nodes_with_container, wait_for_container_deletion
|
||||
from frostfs_testlib.steps.cli.object import delete_object, head_object, put_object_to_random_node
|
||||
from frostfs_testlib.steps.metrics import check_metrics_counter, get_metrics_value
|
||||
from frostfs_testlib.steps.metrics import calc_metrics_count_from_stdout, check_metrics_counter, get_metrics_value
|
||||
from frostfs_testlib.steps.storage_policy import get_nodes_with_object
|
||||
from frostfs_testlib.storage.cluster import Cluster
|
||||
from frostfs_testlib.storage.cluster import Cluster, ClusterNode
|
||||
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
|
||||
from frostfs_testlib.storage.dataclasses.wallet import WalletInfo
|
||||
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
||||
|
@ -25,6 +25,14 @@ class TestContainerMetrics(ClusterTestBase):
|
|||
oid = put_object_to_random_node(wallet, file_path, cid, self.shell, self.cluster)
|
||||
return oid
|
||||
|
||||
@reporter.step("Get metrics value from node")
|
||||
def get_metrics_search_by_greps_parallel(self, node: ClusterNode, **greps):
|
||||
try:
|
||||
content_stdout = node.metrics.storage.get_metrics_search_by_greps(greps)
|
||||
return calc_metrics_count_from_stdout(content_stdout)
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
@allure.title("Container metrics (obj_size={object_size},policy={policy})")
|
||||
@pytest.mark.parametrize("placement_policy, policy", [("REP 2 IN X CBF 2 SELECT 2 FROM * AS X", "REP"), ("EC 1.1 CBF 1", "EC")])
|
||||
def test_container_metrics(
|
||||
|
@ -164,3 +172,43 @@ class TestContainerMetrics(ClusterTestBase):
|
|||
for act_metric in metrics_value_nodes:
|
||||
assert act_metric >= 0, "Metrics value is negative"
|
||||
assert sum(metrics_value_nodes) // len(self.cluster.cluster_nodes) == tombstones_size, "tomstone size of objects not correct"
|
||||
|
||||
|
||||
@allure.title("Container metrics (policy={policy})")
|
||||
@pytest.mark.parametrize("placement_policy, policy", [("REP 2 IN X CBF 2 SELECT 2 FROM * AS X", "REP"), ("EC 1.1 CBF 1", "EC")])
|
||||
def test_container_metrics_delete_complex_objects(
|
||||
self,
|
||||
complex_object_size: ObjectSize,
|
||||
default_wallet: WalletInfo,
|
||||
cluster: Cluster,
|
||||
placement_policy: str,
|
||||
policy: str
|
||||
):
|
||||
copies = 2 if policy == "REP" else 1
|
||||
objects_count = 2
|
||||
metric_name = "frostfs_node_engine_container_objects_total"
|
||||
with reporter.step(f"Create container"):
|
||||
cid = create_container(default_wallet, self.shell, cluster.default_rpc_endpoint, rule=placement_policy)
|
||||
|
||||
with reporter.step(f"Put {objects_count} objects"):
|
||||
files_path = [generate_file(complex_object_size.value) for _ in range(objects_count)]
|
||||
futures = parallel(self.put_object_parallel, files_path, wallet=default_wallet, cid=cid)
|
||||
oids = [future.result() for future in futures]
|
||||
|
||||
with reporter.step(f"Check metrics value in each nodes, should be {objects_count} for 'user'"):
|
||||
check_metrics_counter(cluster.cluster_nodes, counter_exp=objects_count * copies, command=metric_name, cid=cid, type="user")
|
||||
|
||||
with reporter.step("Delete objects and container"):
|
||||
for oid in oids:
|
||||
delete_object(default_wallet, cid, oid, self.shell, cluster.default_rpc_endpoint)
|
||||
|
||||
delete_container(default_wallet, cid, self.shell, cluster.default_rpc_endpoint)
|
||||
|
||||
with reporter.step("Tick epoch and check container was deleted"):
|
||||
self.tick_epoch()
|
||||
wait_for_container_deletion(default_wallet, cid, shell=self.shell, endpoint=cluster.default_rpc_endpoint)
|
||||
|
||||
with reporter.step(f"Check metrics value in each nodes, should not be show any result"):
|
||||
futures = parallel(self.get_metrics_search_by_greps_parallel, cluster.cluster_nodes, command=metric_name, cid=cid)
|
||||
metrics_results = [future.result() for future in futures if future.result() is not None]
|
||||
assert len(metrics_results) == 0, f"Metrics value is not empty in Prometheus, actual value in nodes: {metrics_results}"
|
||||
|
|
0
pytest_tests/testsuites/object/__init__.py
Normal file
0
pytest_tests/testsuites/object/__init__.py
Normal file
|
@ -21,8 +21,8 @@ from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
|||
from frostfs_testlib.testing.test_control import expect_not_raises
|
||||
from pytest import FixtureRequest
|
||||
|
||||
from pytest_tests.helpers.bearer_token import create_bearer_token
|
||||
from pytest_tests.helpers.container_access import assert_full_access_to_container
|
||||
from ...helpers.bearer_token import create_bearer_token
|
||||
from ...helpers.container_access import assert_full_access_to_container
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
|
|
|
@ -4,15 +4,16 @@ import allure
|
|||
import pytest
|
||||
from frostfs_testlib import reporter
|
||||
from frostfs_testlib.resources.error_patterns import OBJECT_NOT_FOUND
|
||||
from frostfs_testlib.steps.cli.container import create_container
|
||||
from frostfs_testlib.steps.cli.object import get_object_from_random_node, head_object, put_object_to_random_node
|
||||
from frostfs_testlib.steps.epoch import get_epoch
|
||||
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
|
||||
from frostfs_testlib.storage.dataclasses.wallet import WalletInfo
|
||||
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
||||
from frostfs_testlib.utils.file_utils import generate_file, get_file_hash
|
||||
from frostfs_testlib.testing.test_control import expect_not_raises
|
||||
from frostfs_testlib.utils.file_utils import TestFile
|
||||
|
||||
from pytest_tests.helpers.utility import wait_for_gc_pass_on_storage_nodes
|
||||
from ...helpers.container_spec import ContainerSpecs
|
||||
from ...helpers.utility import wait_for_gc_pass_on_storage_nodes
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
|
||||
|
@ -21,36 +22,32 @@ logger = logging.getLogger("NeoLogger")
|
|||
@pytest.mark.sanity
|
||||
@pytest.mark.grpc_api
|
||||
class TestObjectApiLifetime(ClusterTestBase):
|
||||
@pytest.mark.container(ContainerSpecs.PublicReadWrite)
|
||||
@allure.title("Object is removed when lifetime expired (obj_size={object_size})")
|
||||
def test_object_api_lifetime(self, default_wallet: WalletInfo, object_size: ObjectSize):
|
||||
def test_object_api_lifetime(self, container: str, test_file: TestFile, default_wallet: WalletInfo, object_size: ObjectSize):
|
||||
"""
|
||||
Test object deleted after expiration epoch.
|
||||
"""
|
||||
|
||||
wallet = default_wallet
|
||||
endpoint = self.cluster.default_rpc_endpoint
|
||||
cid = create_container(wallet, self.shell, endpoint)
|
||||
|
||||
file_path = generate_file(object_size.value)
|
||||
file_hash = get_file_hash(file_path)
|
||||
epoch = get_epoch(self.shell, self.cluster)
|
||||
|
||||
oid = put_object_to_random_node(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)
|
||||
assert get_file_hash(got_file) == file_hash
|
||||
oid = put_object_to_random_node(wallet, test_file.path, container, self.shell, self.cluster, expire_at=epoch + 1)
|
||||
with expect_not_raises():
|
||||
head_object(wallet, container, oid, self.shell, self.cluster.default_rpc_endpoint)
|
||||
|
||||
with reporter.step("Tick two epochs"):
|
||||
for _ in range(2):
|
||||
self.tick_epoch()
|
||||
self.tick_epochs(2)
|
||||
|
||||
# Wait for GC, because object with expiration is counted as alive until GC removes it
|
||||
wait_for_gc_pass_on_storage_nodes()
|
||||
|
||||
with reporter.step("Check object deleted because it expires on epoch"):
|
||||
with pytest.raises(Exception, match=OBJECT_NOT_FOUND):
|
||||
head_object(wallet, cid, oid, self.shell, self.cluster.default_rpc_endpoint)
|
||||
head_object(wallet, container, oid, self.shell, self.cluster.default_rpc_endpoint)
|
||||
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, container, oid, self.shell, self.cluster)
|
||||
|
||||
with reporter.step("Tick additional epoch"):
|
||||
self.tick_epoch()
|
||||
|
@ -59,6 +56,6 @@ class TestObjectApiLifetime(ClusterTestBase):
|
|||
|
||||
with reporter.step("Check object deleted because it expires on previous epoch"):
|
||||
with pytest.raises(Exception, match=OBJECT_NOT_FOUND):
|
||||
head_object(wallet, cid, oid, self.shell, self.cluster.default_rpc_endpoint)
|
||||
head_object(wallet, container, oid, self.shell, self.cluster.default_rpc_endpoint)
|
||||
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, container, oid, self.shell, self.cluster)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import logging
|
||||
import re
|
||||
|
||||
import allure
|
||||
import pytest
|
||||
|
@ -11,15 +10,14 @@ from frostfs_testlib.resources.error_patterns import (
|
|||
LOCK_NON_REGULAR_OBJECT,
|
||||
LOCK_OBJECT_EXPIRATION,
|
||||
LOCK_OBJECT_REMOVAL,
|
||||
OBJECT_ALREADY_REMOVED,
|
||||
OBJECT_IS_LOCKED,
|
||||
OBJECT_NOT_FOUND,
|
||||
)
|
||||
from frostfs_testlib.shell import Shell
|
||||
from frostfs_testlib.steps.cli.container import StorageContainer, StorageContainerInfo, create_container
|
||||
from frostfs_testlib.steps.cli.container import StorageContainer, StorageContainerInfo
|
||||
from frostfs_testlib.steps.cli.object import delete_object, head_object, lock_object
|
||||
from frostfs_testlib.steps.complex_object_actions import get_link_object, get_storage_object_chunks
|
||||
from frostfs_testlib.steps.epoch import ensure_fresh_epoch, get_epoch, tick_epoch
|
||||
from frostfs_testlib.steps.epoch import ensure_fresh_epoch
|
||||
from frostfs_testlib.steps.node_management import drop_object
|
||||
from frostfs_testlib.steps.storage_object import delete_objects
|
||||
from frostfs_testlib.steps.storage_policy import get_nodes_with_object
|
||||
|
@ -31,29 +29,32 @@ from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
|||
from frostfs_testlib.testing.test_control import expect_not_raises, wait_for_success
|
||||
from frostfs_testlib.utils import datetime_utils, string_utils
|
||||
|
||||
from pytest_tests.helpers.utility import wait_for_gc_pass_on_storage_nodes
|
||||
from ...helpers.container_spec import ContainerSpecs
|
||||
from ...helpers.utility import wait_for_gc_pass_on_storage_nodes
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
|
||||
FIXTURE_LOCK_LIFETIME = 5
|
||||
FIXTURE_OBJECT_LIFETIME = 10
|
||||
|
||||
pytestmark = pytest.mark.container(ContainerSpecs.PublicReadWrite)
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def user_wallet(credentials_provider: CredentialsProvider, cluster: Cluster) -> WalletInfo:
|
||||
with reporter.step("Create user wallet with container"):
|
||||
user = User(string_utils.unique_name("user-"))
|
||||
return credentials_provider.GRPC.provide(user, cluster.cluster_nodes[0])
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def user_container(user_wallet: WalletInfo, client_shell: Shell, cluster: Cluster):
|
||||
container_id = create_container(user_wallet, shell=client_shell, endpoint=cluster.default_rpc_endpoint)
|
||||
return StorageContainer(StorageContainerInfo(container_id, user_wallet), client_shell, cluster)
|
||||
@pytest.fixture(scope="class")
|
||||
def user_container(container_module_scope: str, user_wallet: WalletInfo, client_shell: Shell, cluster: Cluster):
|
||||
return StorageContainer(StorageContainerInfo(container_module_scope, user_wallet), client_shell, cluster)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
@pytest.fixture(scope="class")
|
||||
def locked_storage_object(
|
||||
new_epoch_module_scope: int,
|
||||
user_container: StorageContainer,
|
||||
client_shell: Shell,
|
||||
cluster: Cluster,
|
||||
|
@ -63,7 +64,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
|
||||
"""
|
||||
with reporter.step("Creating locked object"):
|
||||
current_epoch = ensure_fresh_epoch(client_shell, cluster)
|
||||
current_epoch = new_epoch_module_scope
|
||||
expiration_epoch = current_epoch + FIXTURE_LOCK_LIFETIME
|
||||
|
||||
storage_object = user_container.generate_object(object_size.value, expire_at=current_epoch + FIXTURE_OBJECT_LIFETIME)
|
||||
|
@ -77,30 +78,7 @@ def locked_storage_object(
|
|||
)
|
||||
storage_object.locks = [LockObjectInfo(storage_object.cid, lock_object_id, FIXTURE_LOCK_LIFETIME, expiration_epoch)]
|
||||
|
||||
yield storage_object
|
||||
|
||||
with reporter.step("Delete created locked object"):
|
||||
current_epoch = get_epoch(client_shell, cluster)
|
||||
epoch_diff = expiration_epoch - current_epoch + 1
|
||||
|
||||
if epoch_diff > 0:
|
||||
with reporter.step(f"Tick {epoch_diff} epochs"):
|
||||
for _ in range(epoch_diff):
|
||||
tick_epoch(client_shell, cluster)
|
||||
try:
|
||||
delete_object(
|
||||
storage_object.wallet,
|
||||
storage_object.cid,
|
||||
storage_object.oid,
|
||||
client_shell,
|
||||
cluster.default_rpc_endpoint,
|
||||
)
|
||||
except Exception as ex:
|
||||
ex_message = str(ex)
|
||||
# It's okay if object already removed
|
||||
if not re.search(OBJECT_NOT_FOUND, ex_message) and not re.search(OBJECT_ALREADY_REMOVED, ex_message):
|
||||
raise ex
|
||||
logger.debug(ex_message)
|
||||
return storage_object
|
||||
|
||||
|
||||
@wait_for_success(datetime_utils.parse_time(STORAGE_GC_TIME))
|
||||
|
|
63
pytest_tests/testsuites/object/test_object_tombstone.py
Normal file
63
pytest_tests/testsuites/object/test_object_tombstone.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
import allure
|
||||
import pytest
|
||||
from frostfs_testlib import reporter
|
||||
from frostfs_testlib.resources.common import EXPIRATION_EPOCH_ATTRIBUTE
|
||||
from frostfs_testlib.resources.error_patterns import OBJECT_NOT_FOUND
|
||||
from frostfs_testlib.storage.controllers.cluster_state_controller import ClusterStateController
|
||||
from frostfs_testlib.storage.controllers.state_managers.config_state_manager import ConfigStateManager
|
||||
from frostfs_testlib.storage.dataclasses.frostfs_services import StorageNode
|
||||
from frostfs_testlib.storage.grpc_operations.interfaces import GrpcClientWrapper
|
||||
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
||||
from frostfs_testlib.utils.file_utils import TestFile
|
||||
|
||||
from ....pytest_tests.helpers.container_spec import APE_PUBLIC_READ_WRITE, ContainerSpec
|
||||
|
||||
|
||||
class TestObjectTombstone(ClusterTestBase):
|
||||
@pytest.fixture()
|
||||
@allure.title("Change tombstone lifetime")
|
||||
def tombstone_lifetime(self, cluster_state_controller: ClusterStateController, request: pytest.FixtureRequest):
|
||||
config_manager = cluster_state_controller.manager(ConfigStateManager)
|
||||
config_manager.set_on_all_nodes(StorageNode, {"object:delete:tombstone_lifetime": request.param}, True)
|
||||
|
||||
yield f"Tombstone lifetime was changed to {request.param}"
|
||||
|
||||
config_manager.revert_all(True)
|
||||
|
||||
@pytest.mark.container(ContainerSpec(ape_rules=APE_PUBLIC_READ_WRITE))
|
||||
@pytest.mark.parametrize("object_size, tombstone_lifetime", [("simple", 2)], indirect=True)
|
||||
@allure.title("Tombstone object should be removed after expiration")
|
||||
def test_tombstone_lifetime(
|
||||
self,
|
||||
new_epoch: int,
|
||||
container: str,
|
||||
grpc_client: GrpcClientWrapper,
|
||||
test_file: TestFile,
|
||||
rpc_endpoint: str,
|
||||
tombstone_lifetime: str,
|
||||
):
|
||||
allure.dynamic.description(tombstone_lifetime)
|
||||
|
||||
with reporter.step("Put object"):
|
||||
oid = grpc_client.object.put(test_file.path, container, rpc_endpoint)
|
||||
|
||||
with reporter.step("Remove object"):
|
||||
tombstone_oid = grpc_client.object.delete(container, oid, rpc_endpoint)
|
||||
|
||||
with reporter.step("Get tombstone object lifetime"):
|
||||
tombstone_info = grpc_client.object.head(container, tombstone_oid, rpc_endpoint)
|
||||
tombstone_expiration_epoch = tombstone_info["header"]["attributes"][EXPIRATION_EPOCH_ATTRIBUTE]
|
||||
|
||||
with reporter.step("Tombstone lifetime should be <= 3"):
|
||||
epochs_to_skip = int(tombstone_expiration_epoch) - new_epoch + 1
|
||||
assert epochs_to_skip <= 3
|
||||
|
||||
with reporter.step("Wait for tombstone expiration"):
|
||||
self.tick_epochs(epochs_to_skip)
|
||||
|
||||
with reporter.step("Tombstone should be removed after expiration"):
|
||||
with pytest.raises(RuntimeError, match=OBJECT_NOT_FOUND):
|
||||
grpc_client.object.head(container, tombstone_oid, rpc_endpoint)
|
||||
|
||||
with pytest.raises(RuntimeError, match=OBJECT_NOT_FOUND):
|
||||
grpc_client.object.get(container, tombstone_oid, rpc_endpoint)
|
0
pytest_tests/testsuites/replication/__init__.py
Normal file
0
pytest_tests/testsuites/replication/__init__.py
Normal file
|
@ -22,6 +22,8 @@ from frostfs_testlib.testing.test_control import wait_for_success
|
|||
from frostfs_testlib.utils import datetime_utils
|
||||
from frostfs_testlib.utils.file_utils import generate_file, get_file_hash
|
||||
|
||||
from ...resources.common import S3_POLICY_FILE_LOCATION
|
||||
|
||||
|
||||
def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
|
||||
if "ec_policy" not in metafunc.fixturenames:
|
||||
|
@ -39,7 +41,8 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
|
|||
100: ["EC 12.4", "EC 8.4", "EC 5.3", "EC 4.4"],
|
||||
}
|
||||
|
||||
metafunc.parametrize("ec_policy, node_count", ((ec_policy, node_count) for ec_policy in ec_map[node_count]))
|
||||
nearest_node_count = ([4] + (list(filter(lambda x: x <= node_count, ec_map.keys()))))[-1]
|
||||
metafunc.parametrize("ec_policy, node_count", ((ec_policy, node_count) for ec_policy in ec_map[nearest_node_count]))
|
||||
|
||||
|
||||
@allure.title("Initialized remote FrostfsAdm")
|
||||
|
@ -677,7 +680,7 @@ class TestECReplication(ClusterTestBase):
|
|||
)
|
||||
|
||||
@allure.title("Create bucket with EC policy (s3_client={s3_client})")
|
||||
@pytest.mark.parametrize("s3_policy, s3_client", [("pytest_tests/resources/files/policy.json", AwsCliClient)], indirect=True)
|
||||
@pytest.mark.parametrize("s3_policy, s3_client", [(S3_POLICY_FILE_LOCATION, AwsCliClient)], indirect=True)
|
||||
def test_create_bucket_with_ec_location(
|
||||
self, s3_client: S3ClientWrapper, bucket_container_resolver: BucketContainerResolver, grpc_client: GrpcClientWrapper
|
||||
) -> None:
|
||||
|
@ -692,7 +695,7 @@ class TestECReplication(ClusterTestBase):
|
|||
assert container
|
||||
|
||||
@allure.title("Bucket object count chunks (s3_client={s3_client}, size={object_size})")
|
||||
@pytest.mark.parametrize("s3_policy, s3_client", [("pytest_tests/resources/files/policy.json", AwsCliClient)], indirect=True)
|
||||
@pytest.mark.parametrize("s3_policy, s3_client", [(S3_POLICY_FILE_LOCATION, AwsCliClient)], indirect=True)
|
||||
def test_count_chunks_bucket_with_ec_location(
|
||||
self,
|
||||
s3_client: S3ClientWrapper,
|
||||
|
|
0
pytest_tests/testsuites/services/__init__.py
Normal file
0
pytest_tests/testsuites/services/__init__.py
Normal file
0
pytest_tests/testsuites/services/http_gate/__init__.py
Normal file
0
pytest_tests/testsuites/services/http_gate/__init__.py
Normal file
|
@ -15,7 +15,7 @@ from frostfs_testlib.storage.dataclasses.wallet import WalletInfo
|
|||
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
||||
from frostfs_testlib.utils.file_utils import generate_file
|
||||
|
||||
from pytest_tests.helpers.bearer_token import create_bearer_token
|
||||
from ....helpers.bearer_token import create_bearer_token
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
|
|||
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
||||
from frostfs_testlib.utils.file_utils import generate_file, get_file_hash
|
||||
|
||||
from pytest_tests.helpers.utility import wait_for_gc_pass_on_storage_nodes
|
||||
from ....helpers.utility import wait_for_gc_pass_on_storage_nodes
|
||||
|
||||
OBJECT_NOT_FOUND_ERROR = "not found"
|
||||
|
||||
|
|
|
@ -23,7 +23,10 @@ from frostfs_testlib.utils.file_utils import generate_file
|
|||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
EXPIRATION_TIMESTAMP_HEADER = "__SYSTEM__EXPIRATION_TIMESTAMP"
|
||||
|
||||
# TODO: Depreacated. Use EXPIRATION_EPOCH_ATTRIBUTE from testlib
|
||||
EXPIRATION_EPOCH_HEADER = "__SYSTEM__EXPIRATION_EPOCH"
|
||||
|
||||
EXPIRATION_DURATION_HEADER = "__SYSTEM__EXPIRATION_DURATION"
|
||||
EXPIRATION_EXPIRATION_RFC = "__SYSTEM__EXPIRATION_RFC3339"
|
||||
SYSTEM_EXPIRATION_EPOCH = "System-Expiration-Epoch"
|
||||
|
@ -151,9 +154,7 @@ class Test_http_system_header(ClusterTestBase):
|
|||
def test_unable_put_negative_duration(self, user_container: str, simple_object_size: ObjectSize):
|
||||
headers = attr_into_str_header_curl({"System-Expiration-Duration": "-1h"})
|
||||
file_path = generate_file(simple_object_size.value)
|
||||
with reporter.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(
|
||||
cid=user_container,
|
||||
filepath=file_path,
|
||||
|
@ -166,9 +167,7 @@ class Test_http_system_header(ClusterTestBase):
|
|||
def test_unable_put_expired_timestamp(self, user_container: str, simple_object_size: ObjectSize):
|
||||
headers = attr_into_str_header_curl({"System-Expiration-Timestamp": "1635075727"})
|
||||
file_path = generate_file(simple_object_size.value)
|
||||
with reporter.step(
|
||||
"Put object using HTTP with attribute System-Expiration-Timestamp where duration is in the past"
|
||||
):
|
||||
with reporter.step("Put object using HTTP with attribute System-Expiration-Timestamp where duration is in the past"):
|
||||
upload_via_http_gate_curl(
|
||||
cid=user_container,
|
||||
filepath=file_path,
|
||||
|
@ -177,9 +176,7 @@ class Test_http_system_header(ClusterTestBase):
|
|||
error_pattern=f"{EXPIRATION_TIMESTAMP_HEADER} must be in the future",
|
||||
)
|
||||
|
||||
@allure.title(
|
||||
"[NEGATIVE] Put object using HTTP with attribute System-Expiration-RFC3339 where duration is in the past"
|
||||
)
|
||||
@allure.title("[NEGATIVE] Put object using HTTP with attribute System-Expiration-RFC3339 where duration is in the past")
|
||||
def test_unable_put_expired_rfc(self, user_container: str, simple_object_size: ObjectSize):
|
||||
headers = attr_into_str_header_curl({"System-Expiration-RFC3339": "2021-11-22T09:55:49Z"})
|
||||
file_path = generate_file(simple_object_size.value)
|
||||
|
@ -204,9 +201,7 @@ class Test_http_system_header(ClusterTestBase):
|
|||
with reporter.step(
|
||||
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(
|
||||
file_path=file_path, attributes=attributes, user_container=user_container
|
||||
)
|
||||
oid, head_info = self.oid_header_info_for_object(file_path=file_path, attributes=attributes, user_container=user_container)
|
||||
self.validation_for_http_header_attr(head_info=head_info, expected_epoch=expected_epoch)
|
||||
with reporter.step("Check that object becomes unavailable when epoch is expired"):
|
||||
for _ in range(0, epoch_count + 1):
|
||||
|
@ -243,9 +238,7 @@ class Test_http_system_header(ClusterTestBase):
|
|||
with reporter.step(
|
||||
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(
|
||||
file_path=file_path, attributes=attributes, user_container=user_container
|
||||
)
|
||||
oid, head_info = self.oid_header_info_for_object(file_path=file_path, attributes=attributes, user_container=user_container)
|
||||
self.validation_for_http_header_attr(head_info=head_info, expected_epoch=expected_epoch)
|
||||
with reporter.step("Check that object becomes unavailable when epoch is expired"):
|
||||
for _ in range(0, epoch_count + 1):
|
||||
|
@ -276,17 +269,13 @@ class Test_http_system_header(ClusterTestBase):
|
|||
)
|
||||
attributes = {
|
||||
SYSTEM_EXPIRATION_TIMESTAMP: self.epoch_count_into_timestamp(epoch_duration=epoch_duration, epoch=2),
|
||||
SYSTEM_EXPIRATION_RFC3339: self.epoch_count_into_timestamp(
|
||||
epoch_duration=epoch_duration, epoch=1, rfc3339=True
|
||||
),
|
||||
SYSTEM_EXPIRATION_RFC3339: self.epoch_count_into_timestamp(epoch_duration=epoch_duration, epoch=1, rfc3339=True),
|
||||
}
|
||||
file_path = generate_file(object_size.value)
|
||||
with reporter.step(
|
||||
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(
|
||||
file_path=file_path, attributes=attributes, user_container=user_container
|
||||
)
|
||||
oid, head_info = self.oid_header_info_for_object(file_path=file_path, attributes=attributes, user_container=user_container)
|
||||
self.validation_for_http_header_attr(head_info=head_info, expected_epoch=expected_epoch)
|
||||
with reporter.step("Check that object becomes unavailable when epoch is expired"):
|
||||
for _ in range(0, epoch_count + 1):
|
||||
|
@ -314,20 +303,14 @@ class Test_http_system_header(ClusterTestBase):
|
|||
["simple"],
|
||||
indirect=True,
|
||||
)
|
||||
def test_http_rfc_object_unavailable_after_expir(
|
||||
self, user_container: str, object_size: ObjectSize, epoch_duration: int
|
||||
):
|
||||
def test_http_rfc_object_unavailable_after_expir(self, user_container: str, object_size: ObjectSize, epoch_duration: int):
|
||||
self.tick_epoch()
|
||||
epoch_count = 2
|
||||
expected_epoch = get_epoch(self.shell, self.cluster) + epoch_count
|
||||
logger.info(
|
||||
f"epoch duration={epoch_duration}, current_epoch= {get_epoch(self.shell, self.cluster)} expected_epoch {expected_epoch}"
|
||||
)
|
||||
attributes = {
|
||||
SYSTEM_EXPIRATION_RFC3339: self.epoch_count_into_timestamp(
|
||||
epoch_duration=epoch_duration, epoch=2, rfc3339=True
|
||||
)
|
||||
}
|
||||
attributes = {SYSTEM_EXPIRATION_RFC3339: self.epoch_count_into_timestamp(epoch_duration=epoch_duration, epoch=2, rfc3339=True)}
|
||||
file_path = generate_file(object_size.value)
|
||||
with reporter.step(
|
||||
f"Put objects using HTTP with attributes and head command should display {EXPIRATION_EPOCH_HEADER}: {expected_epoch} attr"
|
||||
|
|
0
pytest_tests/testsuites/services/s3_gate/__init__.py
Normal file
0
pytest_tests/testsuites/services/s3_gate/__init__.py
Normal file
|
@ -1,3 +1,4 @@
|
|||
import string
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import allure
|
||||
|
@ -6,8 +7,15 @@ from frostfs_testlib import reporter
|
|||
from frostfs_testlib.s3 import S3ClientWrapper, VersioningStatus
|
||||
from frostfs_testlib.steps.s3 import s3_helper
|
||||
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
|
||||
from frostfs_testlib.utils import string_utils
|
||||
from frostfs_testlib.utils.file_utils import generate_file
|
||||
|
||||
VALID_SYMBOLS_WITHOUT_DOT = string.ascii_lowercase + string.digits + "-"
|
||||
VALID_AND_INVALID_SYMBOLS = string.ascii_letters + string.punctuation
|
||||
|
||||
# TODO: The dot symbol is temporarily not supported.
|
||||
VALID_SYMBOLS_WITH_DOT = VALID_SYMBOLS_WITHOUT_DOT + "."
|
||||
|
||||
|
||||
@pytest.mark.nightly
|
||||
@pytest.mark.s3_gate
|
||||
|
@ -140,3 +148,87 @@ class TestS3GateBucket:
|
|||
s3_client.delete_bucket(bucket)
|
||||
with pytest.raises(Exception, match=r".*Not Found.*"):
|
||||
s3_client.head_bucket(bucket)
|
||||
|
||||
@allure.title("Create bucket with valid name length (s3_client={s3_client}, length={length})")
|
||||
@pytest.mark.parametrize("length", [3, 4, 32, 62, 63])
|
||||
def test_s3_create_bucket_with_valid_length(self, s3_client: S3ClientWrapper, length: int):
|
||||
bucket_name = string_utils.random_string(length, VALID_SYMBOLS_WITHOUT_DOT)
|
||||
while not (bucket_name[0].isalnum() and bucket_name[-1].isalnum()):
|
||||
bucket_name = string_utils.random_string(length, VALID_SYMBOLS_WITHOUT_DOT)
|
||||
|
||||
with reporter.step("Create bucket with valid name length"):
|
||||
s3_client.create_bucket(bucket_name)
|
||||
|
||||
with reporter.step("Check bucket name in buckets"):
|
||||
assert bucket_name in s3_client.list_buckets()
|
||||
|
||||
@allure.title("[NEGATIVE] Bucket with invalid name length should not be created (s3_client={s3_client}, length={length})")
|
||||
@pytest.mark.parametrize("length", [2, 64, 254, 255, 256])
|
||||
def test_s3_create_bucket_with_invalid_length(self, s3_client: S3ClientWrapper, length: int):
|
||||
bucket_name = string_utils.random_string(length, VALID_SYMBOLS_WITHOUT_DOT)
|
||||
while not (bucket_name[0].isalnum() and bucket_name[-1].isalnum()):
|
||||
bucket_name = string_utils.random_string(length, VALID_SYMBOLS_WITHOUT_DOT)
|
||||
|
||||
with reporter.step("Create bucket with invalid name length and catch exception"):
|
||||
with pytest.raises(Exception, match=".*(?:InvalidBucketName|Invalid bucket name).*"):
|
||||
s3_client.create_bucket(bucket_name)
|
||||
|
||||
@allure.title("[NEGATIVE] Bucket with invalid name should not be created (s3_client={s3_client}, bucket_name={bucket_name})")
|
||||
@pytest.mark.parametrize(
|
||||
"bucket_name",
|
||||
[
|
||||
"BUCKET-1",
|
||||
"buckeT-2",
|
||||
# The following case for AWS CLI is not handled correctly
|
||||
# "-bucket-3",
|
||||
"bucket-4-",
|
||||
".bucket-5",
|
||||
"bucket-6.",
|
||||
"bucket..7",
|
||||
"bucket+8",
|
||||
"bucket_9",
|
||||
"bucket 10",
|
||||
"127.10.5.11",
|
||||
"xn--bucket-12",
|
||||
"bucket-13-s3alias",
|
||||
# The following names can be used in FrostFS but are prohibited by the AWS specification.
|
||||
# "sthree-bucket-14"
|
||||
# "sthree-configurator-bucket-15"
|
||||
# "amzn-s3-demo-bucket-16"
|
||||
# "sthree-bucket-17"
|
||||
# "bucket-18--ol-s3"
|
||||
# "bucket-19--x-s3"
|
||||
# "bucket-20.mrap"
|
||||
],
|
||||
)
|
||||
def test_s3_create_bucket_with_invalid_name(self, s3_client: S3ClientWrapper, bucket_name: str):
|
||||
with reporter.step("Create bucket with invalid name and catch exception"):
|
||||
with pytest.raises(Exception, match=".*(?:InvalidBucketName|Invalid bucket name).*"):
|
||||
s3_client.create_bucket(bucket_name)
|
||||
|
||||
@allure.title("[NEGATIVE] Delete non-empty bucket (s3_client={s3_client})")
|
||||
def test_s3_check_availability_non_empty_bucket_after_deleting(
|
||||
self,
|
||||
bucket: str,
|
||||
simple_object_size: ObjectSize,
|
||||
s3_client: S3ClientWrapper,
|
||||
):
|
||||
object_path = generate_file(simple_object_size.value)
|
||||
object_name = s3_helper.object_key_from_file_path(object_path)
|
||||
|
||||
with reporter.step("Put object into bucket"):
|
||||
s3_client.put_object(bucket, object_path)
|
||||
|
||||
with reporter.step("Check that object appears in bucket"):
|
||||
objects = s3_client.list_objects(bucket)
|
||||
assert objects, f"Expected bucket with object, got empty {objects}"
|
||||
assert object_name in objects, f"Object {object_name} not found in bucket object list {objects}"
|
||||
|
||||
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.*"):
|
||||
s3_client.delete_bucket(bucket)
|
||||
|
||||
with reporter.step("Check bucket availability"):
|
||||
objects = s3_client.list_objects(bucket)
|
||||
assert objects, f"Expected bucket with object, got empty {objects}"
|
||||
assert object_name in objects, f"Object {object_name} not found in bucket object list {objects}"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import json
|
||||
import os
|
||||
|
||||
import allure
|
||||
import pytest
|
||||
|
@ -15,10 +14,12 @@ from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
|
|||
from frostfs_testlib.testing.test_control import expect_not_raises
|
||||
from frostfs_testlib.utils.file_utils import generate_file
|
||||
|
||||
from ....resources.common import S3_POLICY_FILE_LOCATION
|
||||
|
||||
|
||||
@pytest.mark.nightly
|
||||
@pytest.mark.s3_gate
|
||||
@pytest.mark.parametrize("s3_policy", ["pytest_tests/resources/files/policy.json"], indirect=True)
|
||||
@pytest.mark.parametrize("s3_policy", [S3_POLICY_FILE_LOCATION], indirect=True)
|
||||
class TestS3GatePolicy(ClusterTestBase):
|
||||
@allure.title("Bucket creation with retention policy applied (s3_client={s3_client})")
|
||||
def test_s3_bucket_location(
|
||||
|
@ -100,7 +101,6 @@ class TestS3GatePolicy(ClusterTestBase):
|
|||
s3_client.get_bucket_policy(bucket)
|
||||
|
||||
with reporter.step("Put new policy"):
|
||||
custom_policy = f"file://{os.getcwd()}/pytest_tests/resources/files/bucket_policy.json"
|
||||
custom_policy = {
|
||||
"Version": "2012-10-17",
|
||||
"Id": "aaaa-bbbb-cccc-dddd",
|
||||
|
|
|
@ -31,7 +31,17 @@ class TestLogs:
|
|||
if not os.path.exists(logs_dir):
|
||||
os.makedirs(logs_dir)
|
||||
|
||||
issues_regex = r"\bpanic\b|\boom\b|too many|insufficient funds|insufficient amount of gas|cannot assign requested address|\bunable to process\b"
|
||||
regexes = [
|
||||
r"\bpanic\b",
|
||||
r"\boom\b",
|
||||
r"too many",
|
||||
r"insufficient funds",
|
||||
r"insufficient amount of gas",
|
||||
r"cannot assign requested address",
|
||||
r"\bunable to process\b",
|
||||
r"\bmaximum number of subscriptions is reached\b",
|
||||
]
|
||||
issues_regex = "|".join(regexes)
|
||||
exclude_filter = r"too many requests"
|
||||
log_level_priority = "3" # will include 0-3 priority logs (0: emergency 1: alerts 2: critical 3: errors)
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
allure-pytest==2.13.2
|
||||
allure-python-commons==2.13.2
|
||||
base58==2.1.0
|
||||
boto3==1.16.33
|
||||
botocore==1.19.33
|
||||
boto3==1.35.30
|
||||
configobj==5.0.6
|
||||
neo-mamba==1.0.0
|
||||
pexpect==4.8.0
|
||||
|
|
Loading…
Reference in a new issue