Compare commits

...
Sign in to create a new pull request.

6 commits

Author SHA1 Message Date
bf9c7aac61 [#327] add chain error
Some checks failed
DCO action / DCO (pull_request) Has been cancelled
2024-11-27 20:06:06 +03:00
2cbe1c1f1f [#321] update morph ape
Some checks failed
DCO action / DCO (pull_request) Has been cancelled
2024-11-18 12:43:00 +03:00
3219dd6389 [#310] Update test marking
Some checks failed
DCO action / DCO (pull_request) Has been cancelled
Signed-off-by: a.berezin <a.berezin@yadro.com>
2024-11-06 19:16:40 +03:00
fdc4e7a78b [#302] Autoadd marks for frostfs
Signed-off-by: a.berezin <a.berezin@yadro.com>
2024-11-06 19:16:32 +03:00
59a0a7f883 [#299] Add fuse to prevent similar names generation
Signed-off-by: a.berezin <a.berezin@yadro.com>
2024-11-06 19:16:20 +03:00
2cedb30f82 [#308] Upgrade boto3 client version
Some checks failed
DCO action / DCO (pull_request) Has been cancelled
2024-10-23 17:07:13 +03:00
11 changed files with 66 additions and 47 deletions

View file

@ -27,8 +27,8 @@ dependencies = [
"testrail-api>=1.12.0", "testrail-api>=1.12.0",
"pytest==7.1.2", "pytest==7.1.2",
"tenacity==8.0.1", "tenacity==8.0.1",
"boto3==1.16.33", "boto3==1.35.30",
"boto3-stubs[essential]==1.16.33", "boto3-stubs[essential]==1.35.30",
] ]
requires-python = ">=3.10" requires-python = ">=3.10"

View file

@ -8,8 +8,8 @@ docstring_parser==0.15
testrail-api==1.12.0 testrail-api==1.12.0
tenacity==8.0.1 tenacity==8.0.1
pytest==7.1.2 pytest==7.1.2
boto3==1.16.33 boto3==1.35.30
boto3-stubs[essential]==1.16.33 boto3-stubs[essential]==1.35.30
# Dev dependencies # Dev dependencies
black==22.8.0 black==22.8.0

View file

@ -1,3 +1,4 @@
__version__ = "2.0.1" __version__ = "2.0.1"
from .fixtures import configure_testlib, hosting, temp_directory from .fixtures import configure_testlib, hosting, temp_directory
from .hooks import pytest_collection_modifyitems

View file

@ -69,9 +69,7 @@ class FrostfsAdmMorph(CliCommand):
**{param: param_value for param, param_value in locals().items() if param not in ["self"]}, **{param: param_value for param, param_value in locals().items() if param not in ["self"]},
) )
def set_config( def set_config(self, set_key_value: str, rpc_endpoint: Optional[str] = None, alphabet_wallets: Optional[str] = None) -> CommandResult:
self, set_key_value: str, rpc_endpoint: Optional[str] = None, alphabet_wallets: Optional[str] = None
) -> CommandResult:
"""Add/update global config value in the FrostFS network. """Add/update global config value in the FrostFS network.
Args: Args:
@ -124,9 +122,7 @@ class FrostfsAdmMorph(CliCommand):
**{param: param_value for param, param_value in locals().items() if param not in ["self"]}, **{param: param_value for param, param_value in locals().items() if param not in ["self"]},
) )
def force_new_epoch( def force_new_epoch(self, rpc_endpoint: Optional[str] = None, alphabet_wallets: Optional[str] = None) -> CommandResult:
self, rpc_endpoint: Optional[str] = None, alphabet_wallets: Optional[str] = None
) -> CommandResult:
"""Create new FrostFS epoch event in the side chain. """Create new FrostFS epoch event in the side chain.
Args: Args:
@ -344,20 +340,16 @@ class FrostfsAdmMorph(CliCommand):
return self._execute( return self._execute(
f"morph remove-nodes {' '.join(node_netmap_keys)}", f"morph remove-nodes {' '.join(node_netmap_keys)}",
**{ **{param: param_value for param, param_value in locals().items() if param not in ["self", "node_netmap_keys"]},
param: param_value
for param, param_value in locals().items()
if param not in ["self", "node_netmap_keys"]
},
) )
def add_rule( def add_rule(
self, self,
endpoint: str,
chain_id: str, chain_id: str,
target_name: str, target_name: str,
target_type: str, target_type: str,
rule: Optional[list[str]] = None, rule: Optional[list[str]] = None,
chain_name: Optional[str] = None,
path: Optional[str] = None, path: Optional[str] = None,
chain_id_hex: Optional[bool] = None, chain_id_hex: Optional[bool] = None,
wallet: Optional[str] = None, wallet: Optional[str] = None,
@ -367,10 +359,8 @@ class FrostfsAdmMorph(CliCommand):
"""Drop objects from the node's local storage """Drop objects from the node's local storage
Args: Args:
address: Address of wallet account
chain-id: Assign ID to the parsed chain chain-id: Assign ID to the parsed chain
chain-id-hex: Flag to parse chain ID as hex chain-id-hex: Flag to parse chain ID as hex
endpoint: Remote node control address (as 'multiaddr' or '<host>:<port>')
path: Path to encoded chain in JSON or binary format path: Path to encoded chain in JSON or binary format
rule: Rule statement rule: Rule statement
target-name: Resource name in APE resource name format target-name: Resource name in APE resource name format
@ -382,17 +372,17 @@ class FrostfsAdmMorph(CliCommand):
Command`s result. Command`s result.
""" """
return self._execute( return self._execute(
"control add-rule", "morph ape add-rule-chain",
**{param: value for param, value in locals().items() if param not in ["self"]}, **{param: value for param, value in locals().items() if param not in ["self"]},
) )
def get_rule( def get_rule(
self, self,
endpoint: str,
chain_id: str, chain_id: str,
target_name: str, target_name: str,
target_type: str, target_type: str,
chain_id_hex: Optional[bool] = None, chain_id_hex: Optional[bool] = None,
chain_name: Optional[str] = None,
wallet: Optional[str] = None, wallet: Optional[str] = None,
address: Optional[str] = None, address: Optional[str] = None,
timeout: Optional[str] = None, timeout: Optional[str] = None,
@ -400,10 +390,8 @@ class FrostfsAdmMorph(CliCommand):
"""Drop objects from the node's local storage """Drop objects from the node's local storage
Args: Args:
address string Address of wallet account
chain-id string Chain id chain-id string Chain id
chain-id-hex Flag to parse chain ID as hex chain-id-hex Flag to parse chain ID as hex
endpoint string Remote node control address (as 'multiaddr' or '<host>:<port>')
target-name string Resource name in APE resource name format target-name string Resource name in APE resource name format
target-type string Resource type(container/namespace) target-type string Resource type(container/namespace)
timeout duration Timeout for an operation (default 15s) timeout duration Timeout for an operation (default 15s)
@ -413,7 +401,7 @@ class FrostfsAdmMorph(CliCommand):
Command`s result. Command`s result.
""" """
return self._execute( return self._execute(
"control get-rule", "morph ape get-rule-chain",
**{param: value for param, value in locals().items() if param not in ["self"]}, **{param: value for param, value in locals().items() if param not in ["self"]},
) )
@ -429,8 +417,6 @@ class FrostfsAdmMorph(CliCommand):
"""Drop objects from the node's local storage """Drop objects from the node's local storage
Args: Args:
address: Address of wallet account
endpoint: Remote node control address (as 'multiaddr' or '<host>:<port>')
target-name: Resource name in APE resource name format target-name: Resource name in APE resource name format
target-type: Resource type(container/namespace) target-type: Resource type(container/namespace)
timeout: Timeout for an operation (default 15s) timeout: Timeout for an operation (default 15s)
@ -446,12 +432,12 @@ class FrostfsAdmMorph(CliCommand):
def remove_rule( def remove_rule(
self, self,
endpoint: str,
chain_id: str, chain_id: str,
target_name: str, target_name: str,
target_type: str, target_type: str,
all: Optional[bool] = None, all: Optional[bool] = None,
chain_id_hex: Optional[bool] = None, chain_id_hex: Optional[bool] = None,
chain_name: Optional[str] = None,
wallet: Optional[str] = None, wallet: Optional[str] = None,
address: Optional[str] = None, address: Optional[str] = None,
timeout: Optional[str] = None, timeout: Optional[str] = None,
@ -459,11 +445,9 @@ class FrostfsAdmMorph(CliCommand):
"""Drop objects from the node's local storage """Drop objects from the node's local storage
Args: Args:
address: Address of wallet account
all: Remove all chains all: Remove all chains
chain-id: Assign ID to the parsed chain chain-id: Assign ID to the parsed chain
chain-id-hex: Flag to parse chain ID as hex chain-id-hex: Flag to parse chain ID as hex
endpoint: Remote node control address (as 'multiaddr' or '<host>:<port>')
target-name: Resource name in APE resource name format target-name: Resource name in APE resource name format
target-type: Resource type(container/namespace) target-type: Resource type(container/namespace)
timeout: Timeout for an operation (default 15s) timeout: Timeout for an operation (default 15s)
@ -473,6 +457,6 @@ class FrostfsAdmMorph(CliCommand):
Command`s result. Command`s result.
""" """
return self._execute( return self._execute(
"control remove-rule", "morph ape rm-rule-chain",
**{param: value for param, value in locals().items() if param not in ["self"]}, **{param: value for param, value in locals().items() if param not in ["self"]},
) )

View file

@ -0,0 +1,13 @@
import pytest
@pytest.hookimpl
def pytest_collection_modifyitems(items: list[pytest.Item]):
# All tests which reside in frostfs nodeid are granted with frostfs marker, excluding
# nodeid = full path of the test
# 1. plugins
# 2. testlib itself
for item in items:
location = item.location[0]
if "frostfs" in location and "plugin" not in location and "testlib" not in location:
item.add_marker("frostfs")

View file

@ -9,6 +9,7 @@ OBJECT_ALREADY_REMOVED = "code = 2052.*message = object already removed"
SESSION_NOT_FOUND = "code = 4096.*message = session token not found" SESSION_NOT_FOUND = "code = 4096.*message = session token not found"
OUT_OF_RANGE = "code = 2053.*message = out of range" OUT_OF_RANGE = "code = 2053.*message = out of range"
EXPIRED_SESSION_TOKEN = "code = 4097.*message = expired session token" EXPIRED_SESSION_TOKEN = "code = 4097.*message = expired session token"
ADD_CHAIN_ERROR = "code = 5120 message = apemanager access denied"
# TODO: Change to codes with message # TODO: Change to codes with message
# OBJECT_IS_LOCKED = "code = 2050.*message = object is locked" # OBJECT_IS_LOCKED = "code = 2050.*message = object is locked"
# LOCK_NON_REGULAR_OBJECT = "code = 2051.*message = ..." will be available once 2092 is fixed # LOCK_NON_REGULAR_OBJECT = "code = 2051.*message = ..." will be available once 2092 is fixed

View file

@ -94,6 +94,7 @@ class Boto3ClientWrapper(S3ClientWrapper):
service_name="iam", service_name="iam",
aws_access_key_id=self.access_key_id, aws_access_key_id=self.access_key_id,
aws_secret_access_key=self.secret_access_key, aws_secret_access_key=self.secret_access_key,
region_name=self.region,
endpoint_url=iam_endpoint, endpoint_url=iam_endpoint,
verify=False, verify=False,
) )

View file

@ -58,6 +58,10 @@ class S3ClientWrapper(HumanReadableABC):
def set_endpoint(self, s3gate_endpoint: str): def set_endpoint(self, s3gate_endpoint: str):
"""Set endpoint""" """Set endpoint"""
@abstractmethod
def set_iam_endpoint(self, iam_endpoint: str):
"""Set iam endpoint"""
@abstractmethod @abstractmethod
def create_bucket( def create_bucket(
self, self,

View file

@ -26,6 +26,20 @@ class ObjectOperations(HumanReadableEnum):
return [op for op in ObjectOperations if op != ObjectOperations.WILDCARD_ALL] return [op for op in ObjectOperations if op != ObjectOperations.WILDCARD_ALL]
class Operations(HumanReadableEnum):
GET_CONTAINER = "GetContainer"
PUT_CONTAINER = "PutContainer"
DELETE_CONTAINER = "DeleteContainer"
LIST_CONTAINER = "ListContainers"
GET_OBJECT = "GetObject"
DELETE_OBJECT = "DeleteObject"
HASH_OBJECT = "HashObject"
RANGE_OBJECT = "RangeObject"
SEARCH_OBJECT = "SearchObject"
HEAD_OBJECT = "HeadObject"
PUT_OBJECT = "PutObject"
class Verb(HumanReadableEnum): class Verb(HumanReadableEnum):
ALLOW = "allow" ALLOW = "allow"
DENY = "deny" DENY = "deny"

View file

@ -181,7 +181,6 @@ class ContainerOperations(interfaces.ContainerInterface):
force: bool = False, force: bool = False,
trace: bool = False, trace: bool = False,
): ):
try:
return self.cli.container.delete( return self.cli.container.delete(
rpc_endpoint=endpoint, rpc_endpoint=endpoint,
cid=cid, cid=cid,
@ -193,8 +192,6 @@ class ContainerOperations(interfaces.ContainerInterface):
force=force, force=force,
trace=trace, trace=trace,
).stdout ).stdout
except RuntimeError as e:
print(f"Error request:\n{e}")
@reporter.step("Get container") @reporter.step("Get container")
def get( def get(

View file

@ -1,3 +1,4 @@
import itertools
import random import random
import re import re
import string import string
@ -7,6 +8,9 @@ ONLY_ASCII_LETTERS = string.ascii_letters
DIGITS_AND_ASCII_LETTERS = string.ascii_letters + string.digits DIGITS_AND_ASCII_LETTERS = string.ascii_letters + string.digits
NON_DIGITS_AND_LETTERS = string.punctuation NON_DIGITS_AND_LETTERS = string.punctuation
# if unique_name is called multiple times within the same microsecond, append 0-4 to the name so it surely unique
FUSE = itertools.cycle(range(5))
def unique_name(prefix: str = "", postfix: str = ""): def unique_name(prefix: str = "", postfix: str = ""):
""" """
@ -18,7 +22,7 @@ def unique_name(prefix: str = "", postfix: str = ""):
Returns: Returns:
unique name string unique name string
""" """
return f"{prefix}{hex(int(datetime.now().timestamp() * 1000000))}{postfix}" return f"{prefix}{hex(int(datetime.now().timestamp() * 1000000))}{next(FUSE)}{postfix}"
def random_string(length: int = 5, source: str = ONLY_ASCII_LETTERS): def random_string(length: int = 5, source: str = ONLY_ASCII_LETTERS):