forked from TrueCloudLab/frostfs-testlib
Compare commits
5 commits
Author | SHA1 | Date | |
---|---|---|---|
7d2c92ebc0 | |||
0c4e601840 | |||
f1073d214c | |||
b00d080982 | |||
97b9b5498a |
10 changed files with 70 additions and 48 deletions
|
@ -1,4 +1,4 @@
|
|||
__version__ = "2.0.1"
|
||||
|
||||
from .fixtures import configure_testlib, hosting, temp_directory
|
||||
from .fixtures import configure_testlib, hosting, session_start_time, temp_directory
|
||||
from .hooks import pytest_add_frostfs_marker, pytest_collection_modifyitems
|
||||
|
|
|
@ -24,9 +24,7 @@ class CliCommand:
|
|||
def __init__(self, shell: Shell, cli_exec_path: str, **base_params):
|
||||
self.shell = shell
|
||||
self.cli_exec_path = cli_exec_path
|
||||
self.__base_params = " ".join(
|
||||
[f"--{param} {value}" for param, value in base_params.items() if value]
|
||||
)
|
||||
self.__base_params = " ".join([f"--{param} {value}" for param, value in base_params.items() if value])
|
||||
|
||||
def _format_command(self, command: str, **params) -> str:
|
||||
param_str = []
|
||||
|
@ -48,9 +46,7 @@ class CliCommand:
|
|||
val_str = str(value_item).replace("'", "\\'")
|
||||
param_str.append(f"--{param} '{val_str}'")
|
||||
elif isinstance(value, dict):
|
||||
param_str.append(
|
||||
f'--{param} \'{",".join(f"{key}={val}" for key, val in value.items())}\''
|
||||
)
|
||||
param_str.append(f'--{param} \'{",".join(f"{key}={val}" for key, val in value.items())}\'')
|
||||
else:
|
||||
if "'" in str(value):
|
||||
value_str = str(value).replace('"', '\\"')
|
||||
|
@ -63,12 +59,18 @@ class CliCommand:
|
|||
return f"{self.cli_exec_path} {self.__base_params} {command or ''} {param_str}"
|
||||
|
||||
def _execute(self, command: Optional[str], **params) -> CommandResult:
|
||||
return self.shell.exec(self._format_command(command, **params))
|
||||
|
||||
def _execute_with_password(self, command: Optional[str], password, **params) -> CommandResult:
|
||||
timeout = int(params["timeout"].rstrip("s")) if params.get("timeout") else None
|
||||
return self.shell.exec(
|
||||
self._format_command(command, **params),
|
||||
options=CommandOptions(
|
||||
interactive_inputs=[InteractiveInput(prompt_pattern="assword", input=password)]
|
||||
CommandOptions(timeout=timeout),
|
||||
)
|
||||
|
||||
def _execute_with_password(self, command: Optional[str], password, **params) -> CommandResult:
|
||||
timeout = int(params["timeout"].rstrip("s")) if params.get("timeout") else None
|
||||
return self.shell.exec(
|
||||
self._format_command(command, **params),
|
||||
CommandOptions(
|
||||
interactive_inputs=[InteractiveInput(prompt_pattern="assword", input=password)],
|
||||
timeout=timeout,
|
||||
),
|
||||
)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from frostfs_testlib.clients.http.http_client import HttpClient
|
|
@ -1 +1,3 @@
|
|||
from frostfs_testlib.clients.s3.interfaces import BucketContainerResolver, S3ClientWrapper, VersioningStatus
|
||||
from frostfs_testlib.clients.s3.aws_cli_client import AwsCliClient
|
||||
from frostfs_testlib.clients.s3.boto3_client import Boto3ClientWrapper
|
||||
from frostfs_testlib.clients.s3.interfaces import ACL, BucketContainerResolver, S3ClientWrapper, VersioningStatus
|
||||
|
|
|
@ -33,12 +33,14 @@ class AwsCliClient(S3ClientWrapper):
|
|||
self, access_key_id: str, secret_access_key: str, s3gate_endpoint: str, profile: str = "default", region: str = "us-east-1"
|
||||
) -> None:
|
||||
self.s3gate_endpoint = s3gate_endpoint
|
||||
self.iam_endpoint = None
|
||||
|
||||
self.access_key_id: str = access_key_id
|
||||
self.secret_access_key: str = secret_access_key
|
||||
self.profile = profile
|
||||
self.local_shell = LocalShell()
|
||||
self.region = region
|
||||
self.iam_endpoint = None
|
||||
|
||||
self.local_shell = LocalShell()
|
||||
try:
|
||||
_configure_aws_cli(f"aws configure --profile {profile}", access_key_id, secret_access_key, region)
|
||||
self.local_shell.exec(f"aws configure set max_attempts {MAX_REQUEST_ATTEMPTS} --profile {profile}")
|
||||
|
@ -977,7 +979,7 @@ class AwsCliClient(S3ClientWrapper):
|
|||
cmd += f" --profile {self.profile}"
|
||||
output = self.local_shell.exec(cmd).stdout
|
||||
response = self._to_json(output)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
|
||||
return response
|
||||
|
||||
|
@ -988,7 +990,7 @@ class AwsCliClient(S3ClientWrapper):
|
|||
cmd += f" --profile {self.profile}"
|
||||
output = self.local_shell.exec(cmd).stdout
|
||||
response = self._to_json(output)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
|
||||
return response
|
||||
|
||||
|
@ -1120,7 +1122,7 @@ class AwsCliClient(S3ClientWrapper):
|
|||
cmd += f" --profile {self.profile}"
|
||||
output = self.local_shell.exec(cmd).stdout
|
||||
response = self._to_json(output)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
|
||||
return response
|
||||
|
||||
|
@ -1131,7 +1133,7 @@ class AwsCliClient(S3ClientWrapper):
|
|||
cmd += f" --profile {self.profile}"
|
||||
output = self.local_shell.exec(cmd).stdout
|
||||
response = self._to_json(output)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
|
||||
return response
|
||||
|
||||
|
@ -1350,7 +1352,7 @@ class AwsCliClient(S3ClientWrapper):
|
|||
cmd += f" --profile {self.profile}"
|
||||
output = self.local_shell.exec(cmd).stdout
|
||||
response = self._to_json(output)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
|
||||
return response
|
||||
|
||||
|
@ -1365,7 +1367,7 @@ class AwsCliClient(S3ClientWrapper):
|
|||
|
||||
output = self.local_shell.exec(cmd).stdout
|
||||
response = self._to_json(output)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
|
||||
return response
|
||||
|
||||
|
|
|
@ -35,26 +35,20 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
def __init__(
|
||||
self, access_key_id: str, secret_access_key: str, s3gate_endpoint: str, profile: str = "default", region: str = "us-east-1"
|
||||
) -> None:
|
||||
self.boto3_client: S3Client = None
|
||||
self.s3gate_endpoint: str = ""
|
||||
self.boto3_client: S3Client = None
|
||||
|
||||
self.boto3_iam_client: S3Client = None
|
||||
self.iam_endpoint: str = ""
|
||||
|
||||
self.boto3_iam_client: S3Client = None
|
||||
self.boto3_sts_client: S3Client = None
|
||||
|
||||
self.access_key_id: str = access_key_id
|
||||
self.secret_access_key: str = secret_access_key
|
||||
self.access_key_id = access_key_id
|
||||
self.secret_access_key = secret_access_key
|
||||
self.profile = profile
|
||||
self.region = region
|
||||
|
||||
self.session = boto3.Session()
|
||||
self.config = Config(
|
||||
retries={
|
||||
"max_attempts": MAX_REQUEST_ATTEMPTS,
|
||||
"mode": RETRY_MODE,
|
||||
}
|
||||
)
|
||||
self.config = Config(retries={"max_attempts": MAX_REQUEST_ATTEMPTS, "mode": RETRY_MODE})
|
||||
|
||||
self.set_endpoint(s3gate_endpoint)
|
||||
|
||||
|
@ -90,7 +84,7 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
endpoint_url=self.iam_endpoint,
|
||||
verify=False,
|
||||
)
|
||||
# since the STS does not have an enpoint, IAM is used
|
||||
# since the STS does not have an endpoint, IAM is used
|
||||
self.boto3_sts_client = self.session.client(
|
||||
service_name="sts",
|
||||
aws_access_key_id=self.access_key_id,
|
||||
|
@ -145,6 +139,7 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
params = {"Bucket": bucket}
|
||||
if object_lock_enabled_for_bucket is not None:
|
||||
params.update({"ObjectLockEnabledForBucket": object_lock_enabled_for_bucket})
|
||||
|
||||
if acl is not None:
|
||||
params.update({"ACL": acl})
|
||||
elif grant_write or grant_read or grant_full_control:
|
||||
|
@ -154,6 +149,7 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
params.update({"GrantRead": grant_read})
|
||||
elif grant_full_control:
|
||||
params.update({"GrantFullControl": grant_full_control})
|
||||
|
||||
if location_constraint:
|
||||
params.update({"CreateBucketConfiguration": {"LocationConstraint": location_constraint}})
|
||||
|
||||
|
@ -840,7 +836,7 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
endpoint=self.iam_endpoint,
|
||||
profile=self.profile,
|
||||
)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
return response
|
||||
|
||||
@reporter.step("Attaches the specified managed policy to the specified user")
|
||||
|
@ -852,7 +848,7 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
endpoint=self.iam_endpoint,
|
||||
profile=self.profile,
|
||||
)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
return response
|
||||
|
||||
@reporter.step("Creates a new AWS secret access key and access key ID for the specified user")
|
||||
|
@ -983,7 +979,7 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
endpoint=self.iam_endpoint,
|
||||
profile=self.profile,
|
||||
)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
return response
|
||||
|
||||
@reporter.step("Removes the specified managed policy from the specified user")
|
||||
|
@ -995,7 +991,7 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
endpoint=self.iam_endpoint,
|
||||
profile=self.profile,
|
||||
)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
return response
|
||||
|
||||
@reporter.step("Returns a list of IAM users that are in the specified IAM group")
|
||||
|
@ -1205,7 +1201,7 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
endpoint=self.iam_endpoint,
|
||||
profile=self.profile,
|
||||
)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
return response
|
||||
|
||||
@reporter.step("Adds or updates an inline policy document that is embedded in the specified IAM user")
|
||||
|
@ -1220,7 +1216,7 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
endpoint=self.iam_endpoint,
|
||||
profile=self.profile,
|
||||
)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
sleep(S3_SYNC_WAIT_TIME * 14)
|
||||
return response
|
||||
|
||||
@reporter.step("Removes the specified user from the specified group")
|
||||
|
|
|
@ -22,15 +22,15 @@ class VersioningStatus(HumanReadableEnum):
|
|||
SUSPENDED = "Suspended"
|
||||
|
||||
|
||||
ACL_COPY = [
|
||||
"private",
|
||||
"public-read",
|
||||
"public-read-write",
|
||||
"authenticated-read",
|
||||
"aws-exec-read",
|
||||
"bucket-owner-read",
|
||||
"bucket-owner-full-control",
|
||||
]
|
||||
class ACL:
|
||||
PRIVATE = "private"
|
||||
PUBLIC_READ = "public-read"
|
||||
PUBLIC_READ_WRITE = "public-read-write"
|
||||
AUTHENTICATED_READ = "authenticated-read"
|
||||
AWS_EXEC_READ = "aws-exec-read"
|
||||
BUCKET_OWNER_READ = "bucket-owner-read"
|
||||
BUCKET_OWNER_FULL_CONTROL = "bucket-owner-full-control"
|
||||
LOG_DELIVERY_WRITE = "log-delivery-write"
|
||||
|
||||
|
||||
class BucketContainerResolver(ABC):
|
||||
|
@ -50,6 +50,14 @@ class BucketContainerResolver(ABC):
|
|||
|
||||
|
||||
class S3ClientWrapper(HumanReadableABC):
|
||||
access_key_id: str
|
||||
secret_access_key: str
|
||||
profile: str
|
||||
region: str
|
||||
|
||||
s3gate_endpoint: str
|
||||
iam_endpoint: str
|
||||
|
||||
@abstractmethod
|
||||
def __init__(self, access_key_id: str, secret_access_key: str, s3gate_endpoint: str, profile: str, region: str) -> None:
|
||||
pass
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import logging
|
||||
import os
|
||||
from datetime import datetime
|
||||
from importlib.metadata import entry_points
|
||||
|
||||
import pytest
|
||||
|
@ -11,6 +12,12 @@ from frostfs_testlib.resources.common import ASSETS_DIR, HOSTING_CONFIG_FILE
|
|||
from frostfs_testlib.storage import get_service_registry
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def session_start_time():
|
||||
start_time = datetime.utcnow()
|
||||
return start_time
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def configure_testlib():
|
||||
reporter.get_reporter().register_handler(reporter.AllureHandler())
|
||||
|
|
|
@ -29,6 +29,9 @@ class Host(ABC):
|
|||
self._service_config_by_name = {service_config.name: service_config for service_config in config.services}
|
||||
self._cli_config_by_name = {cli_config.name: cli_config for cli_config in config.clis}
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.config.address
|
||||
|
||||
@property
|
||||
def config(self) -> HostConfig:
|
||||
"""Returns config of the host.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Regex patterns of status codes of Container service
|
||||
CONTAINER_NOT_FOUND = "code = 3072.*message = container not found"
|
||||
SUBJECT_NOT_FOUND = "code = 1024.*message = frostfs error: chain/client.*subject not found.*"
|
||||
|
||||
# Regex patterns of status codes of Object service
|
||||
MALFORMED_REQUEST = "code = 1024.*message = malformed request"
|
||||
|
|
Loading…
Add table
Reference in a new issue