forked from TrueCloudLab/frostfs-testlib
Compare commits
7 commits
2976e30b75
...
b2bf6677f1
Author | SHA1 | Date | |
---|---|---|---|
b2bf6677f1 | |||
3f3be83d90 | |||
5fa58a55c0 | |||
738cfacbb7 | |||
cf48f474eb | |||
2a41f2b0f6 | |||
a04eba8aec |
15 changed files with 247 additions and 67 deletions
|
@ -27,8 +27,8 @@ dependencies = [
|
|||
"testrail-api>=1.12.0",
|
||||
"pytest==7.1.2",
|
||||
"tenacity==8.0.1",
|
||||
"boto3==1.16.33",
|
||||
"boto3-stubs[essential]==1.16.33",
|
||||
"boto3==1.35.30",
|
||||
"boto3-stubs[essential]==1.35.30",
|
||||
]
|
||||
requires-python = ">=3.10"
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ docstring_parser==0.15
|
|||
testrail-api==1.12.0
|
||||
tenacity==8.0.1
|
||||
pytest==7.1.2
|
||||
boto3==1.16.33
|
||||
boto3-stubs[essential]==1.16.33
|
||||
boto3==1.35.30
|
||||
boto3-stubs[essential]==1.35.30
|
||||
|
||||
# Dev dependencies
|
||||
black==22.8.0
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
__version__ = "2.0.1"
|
||||
|
||||
from .fixtures import configure_testlib, hosting, temp_directory
|
||||
from .hooks import pytest_collection_modifyitems
|
||||
|
|
|
@ -69,9 +69,7 @@ class FrostfsAdmMorph(CliCommand):
|
|||
**{param: param_value for param, param_value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def set_config(
|
||||
self, set_key_value: str, rpc_endpoint: Optional[str] = None, alphabet_wallets: Optional[str] = None
|
||||
) -> CommandResult:
|
||||
def set_config(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.
|
||||
|
||||
Args:
|
||||
|
@ -125,7 +123,7 @@ class FrostfsAdmMorph(CliCommand):
|
|||
)
|
||||
|
||||
def force_new_epoch(
|
||||
self, rpc_endpoint: Optional[str] = None, alphabet_wallets: Optional[str] = None
|
||||
self, rpc_endpoint: Optional[str] = None, alphabet_wallets: Optional[str] = None, delta: Optional[int] = None
|
||||
) -> CommandResult:
|
||||
"""Create new FrostFS epoch event in the side chain.
|
||||
|
||||
|
@ -344,11 +342,7 @@ class FrostfsAdmMorph(CliCommand):
|
|||
|
||||
return self._execute(
|
||||
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(
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import re
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from frostfs_testlib import reporter
|
||||
|
@ -10,6 +9,7 @@ from frostfs_testlib.shell import LocalShell
|
|||
from frostfs_testlib.steps.cli.container import list_containers
|
||||
from frostfs_testlib.storage.cluster import ClusterNode
|
||||
from frostfs_testlib.storage.dataclasses.frostfs_services import S3Gate
|
||||
from frostfs_testlib.utils import string_utils
|
||||
|
||||
|
||||
class AuthmateS3CredentialsProvider(S3CredentialsProvider):
|
||||
|
@ -22,7 +22,7 @@ class AuthmateS3CredentialsProvider(S3CredentialsProvider):
|
|||
|
||||
gate_public_keys = [node.service(S3Gate).get_wallet_public_key() for node in cluster_nodes]
|
||||
# unique short bucket name
|
||||
bucket = f"bucket-{hex(int(datetime.now().timestamp()*1000000))}"
|
||||
bucket = string_utils.unique_name("bucket-")
|
||||
|
||||
frostfs_authmate: FrostfsAuthmate = FrostfsAuthmate(shell, FROSTFS_AUTHMATE_EXEC)
|
||||
issue_secret_output = frostfs_authmate.secret.issue(
|
||||
|
|
13
src/frostfs_testlib/hooks.py
Normal file
13
src/frostfs_testlib/hooks.py
Normal 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")
|
|
@ -1,5 +1,6 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
from frostfs_testlib.load.interfaces.loader import Loader
|
||||
from frostfs_testlib.load.k6 import K6
|
||||
from frostfs_testlib.load.load_config import LoadParams
|
||||
from frostfs_testlib.storage.cluster import ClusterNode
|
||||
|
@ -48,3 +49,7 @@ class ScenarioRunner(ABC):
|
|||
@abstractmethod
|
||||
def get_results(self) -> dict:
|
||||
"""Get results from K6 run"""
|
||||
|
||||
@abstractmethod
|
||||
def get_loaders(self) -> list[Loader]:
|
||||
"""Return loaders"""
|
||||
|
|
|
@ -30,6 +30,7 @@ from frostfs_testlib.utils.file_keeper import FileKeeper
|
|||
|
||||
class RunnerBase(ScenarioRunner):
|
||||
k6_instances: list[K6]
|
||||
loaders: list[Loader]
|
||||
|
||||
@reporter.step("Run preset on loaders")
|
||||
def preset(self):
|
||||
|
@ -49,9 +50,11 @@ class RunnerBase(ScenarioRunner):
|
|||
def get_k6_instances(self):
|
||||
return self.k6_instances
|
||||
|
||||
def get_loaders(self) -> list[Loader]:
|
||||
return self.loaders
|
||||
|
||||
|
||||
class DefaultRunner(RunnerBase):
|
||||
loaders: list[Loader]
|
||||
user: User
|
||||
|
||||
def __init__(
|
||||
|
@ -228,7 +231,6 @@ class DefaultRunner(RunnerBase):
|
|||
|
||||
|
||||
class LocalRunner(RunnerBase):
|
||||
loaders: list[Loader]
|
||||
cluster_state_controller: ClusterStateController
|
||||
file_keeper: FileKeeper
|
||||
user: User
|
||||
|
|
|
@ -754,6 +754,36 @@ class AwsCliClient(S3ClientWrapper):
|
|||
response = self._to_json(output)
|
||||
return response.get("ObjectLockConfiguration")
|
||||
|
||||
@reporter.step("Put bucket lifecycle configuration")
|
||||
def put_bucket_lifecycle_configuration(self, bucket: str, lifecycle_configuration: dict, dumped_configuration: str) -> dict:
|
||||
cmd = (
|
||||
f"aws {self.common_flags} s3api put-bucket-lifecycle-configuration --bucket {bucket} "
|
||||
f"--endpoint-url {self.s3gate_endpoint} --lifecycle-configuration file://{dumped_configuration} --profile {self.profile}"
|
||||
)
|
||||
output = self.local_shell.exec(cmd).stdout
|
||||
response = self._to_json(output)
|
||||
return response
|
||||
|
||||
@reporter.step("Get bucket lifecycle configuration")
|
||||
def get_bucket_lifecycle_configuration(self, bucket: str) -> dict:
|
||||
cmd = (
|
||||
f"aws {self.common_flags} s3api get-bucket-lifecycle-configuration --bucket {bucket} "
|
||||
f"--endpoint-url {self.s3gate_endpoint} --profile {self.profile}"
|
||||
)
|
||||
output = self.local_shell.exec(cmd).stdout
|
||||
response = self._to_json(output)
|
||||
return response
|
||||
|
||||
@reporter.step("Delete bucket lifecycle configuration")
|
||||
def delete_bucket_lifecycle(self, bucket: str) -> dict:
|
||||
cmd = (
|
||||
f"aws {self.common_flags} s3api delete-bucket-lifecycle --bucket {bucket} "
|
||||
f"--endpoint-url {self.s3gate_endpoint} --profile {self.profile}"
|
||||
)
|
||||
output = self.local_shell.exec(cmd).stdout
|
||||
response = self._to_json(output)
|
||||
return response
|
||||
|
||||
@staticmethod
|
||||
def _to_json(output: str) -> dict:
|
||||
json_output = {}
|
||||
|
|
|
@ -68,6 +68,7 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
self.access_key_id: str = access_key_id
|
||||
self.secret_access_key: str = secret_access_key
|
||||
self.s3gate_endpoint: str = ""
|
||||
self.iam_endpoint: str = ""
|
||||
self.boto3_iam_client: S3Client = None
|
||||
self.set_endpoint(s3gate_endpoint)
|
||||
|
||||
|
@ -90,11 +91,16 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
|
||||
@reporter.step("Set endpoint IAM to {iam_endpoint}")
|
||||
def set_iam_endpoint(self, iam_endpoint: str):
|
||||
if self.iam_endpoint == iam_endpoint:
|
||||
return
|
||||
|
||||
self.iam_endpoint = iam_endpoint
|
||||
|
||||
self.boto3_iam_client = self.session.client(
|
||||
service_name="iam",
|
||||
aws_access_key_id=self.access_key_id,
|
||||
aws_secret_access_key=self.secret_access_key,
|
||||
endpoint_url=iam_endpoint,
|
||||
endpoint_url=self.iam_endpoint,
|
||||
verify=False,
|
||||
)
|
||||
|
||||
|
@ -296,6 +302,27 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
response = self.boto3_client.delete_bucket_cors(Bucket=bucket)
|
||||
log_command_execution(self.s3gate_endpoint, "S3 delete_bucket_cors result", response, {"Bucket": bucket})
|
||||
|
||||
@reporter.step("Put bucket lifecycle configuration")
|
||||
@report_error
|
||||
def put_bucket_lifecycle_configuration(self, bucket: str, lifecycle_configuration: dict, dumped_configuration: str) -> dict:
|
||||
response = self.boto3_client.put_bucket_lifecycle_configuration(Bucket=bucket, LifecycleConfiguration=lifecycle_configuration)
|
||||
log_command_execution(self.s3gate_endpoint, "S3 put_bucket_lifecycle_configuration result", response, {"Bucket": bucket})
|
||||
return response
|
||||
|
||||
@reporter.step("Get bucket lifecycle configuration")
|
||||
@report_error
|
||||
def get_bucket_lifecycle_configuration(self, bucket: str) -> dict:
|
||||
response = self.boto3_client.get_bucket_lifecycle_configuration(Bucket=bucket)
|
||||
log_command_execution(self.s3gate_endpoint, "S3 get_bucket_lifecycle_configuration result", response, {"Bucket": bucket})
|
||||
return {"Rules": response.get("Rules")}
|
||||
|
||||
@reporter.step("Delete bucket lifecycle configuration")
|
||||
@report_error
|
||||
def delete_bucket_lifecycle(self, bucket: str) -> dict:
|
||||
response = self.boto3_client.delete_bucket_lifecycle(Bucket=bucket)
|
||||
log_command_execution(self.s3gate_endpoint, "S3 delete_bucket_lifecycle result", response, {"Bucket": bucket})
|
||||
return response
|
||||
|
||||
# END OF BUCKET METHODS #
|
||||
# OBJECT METHODS #
|
||||
|
||||
|
@ -666,25 +693,36 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
# Some methods don't have checks because boto3 is silent in some cases (delete, attach, etc.)
|
||||
|
||||
@reporter.step("Adds the specified user to the specified group")
|
||||
@report_error
|
||||
def iam_add_user_to_group(self, user_name: str, group_name: str) -> dict:
|
||||
response = self.boto3_iam_client.add_user_to_group(UserName=user_name, GroupName=group_name)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.add_user_to_group(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Add User to Group", response, params)
|
||||
return response
|
||||
|
||||
@reporter.step("Attaches the specified managed policy to the specified IAM group")
|
||||
@report_error
|
||||
def iam_attach_group_policy(self, group_name: str, policy_arn: str) -> dict:
|
||||
response = self.boto3_iam_client.attach_group_policy(GroupName=group_name, PolicyArn=policy_arn)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.attach_group_policy(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Attach Group Policy", response, params)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
return response
|
||||
|
||||
@reporter.step("Attaches the specified managed policy to the specified user")
|
||||
@report_error
|
||||
def iam_attach_user_policy(self, user_name: str, policy_arn: str) -> dict:
|
||||
response = self.boto3_iam_client.attach_user_policy(UserName=user_name, PolicyArn=policy_arn)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.attach_user_policy(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Attach User Policy", response, params)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
return response
|
||||
|
||||
@reporter.step("Creates a new AWS secret access key and access key ID for the specified user")
|
||||
@report_error
|
||||
def iam_create_access_key(self, user_name: str) -> dict:
|
||||
response = self.boto3_iam_client.create_access_key(UserName=user_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM Create Access Key", response, {"UserName": user_name})
|
||||
|
||||
access_key_id = response["AccessKey"].get("AccessKeyId")
|
||||
secret_access_key = response["AccessKey"].get("SecretAccessKey")
|
||||
|
@ -694,138 +732,190 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
return access_key_id, secret_access_key
|
||||
|
||||
@reporter.step("Creates a new group")
|
||||
@report_error
|
||||
def iam_create_group(self, group_name: str) -> dict:
|
||||
response = self.boto3_iam_client.create_group(GroupName=group_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM Create Group", response, {"GroupName": group_name})
|
||||
|
||||
assert response.get("Group"), f"Expected Group in response:\n{response}"
|
||||
assert response["Group"].get("GroupName") == group_name, f"GroupName should be equal to {group_name}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Creates a new managed policy for your AWS account")
|
||||
@report_error
|
||||
def iam_create_policy(self, policy_name: str, policy_document: dict) -> dict:
|
||||
response = self.boto3_iam_client.create_policy(PolicyName=policy_name, PolicyDocument=json.dumps(policy_document))
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
params["PolicyDocument"] = json.dumps(policy_document)
|
||||
response = self.boto3_iam_client.create_policy(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Create Policy", response, params)
|
||||
|
||||
assert response.get("Policy"), f"Expected Policy in response:\n{response}"
|
||||
assert response["Policy"].get("PolicyName") == policy_name, f"PolicyName should be equal to {policy_name}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Creates a new IAM user for your AWS account")
|
||||
@report_error
|
||||
def iam_create_user(self, user_name: str) -> dict:
|
||||
response = self.boto3_iam_client.create_user(UserName=user_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM Create User", response, {"UserName": user_name})
|
||||
|
||||
assert response.get("User"), f"Expected User in response:\n{response}"
|
||||
assert response["User"].get("UserName") == user_name, f"UserName should be equal to {user_name}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Deletes the access key pair associated with the specified IAM user")
|
||||
@report_error
|
||||
def iam_delete_access_key(self, access_key_id: str, user_name: str) -> dict:
|
||||
response = self.boto3_iam_client.delete_access_key(AccessKeyId=access_key_id, UserName=user_name)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.delete_access_key(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Delete Access Key", response, params)
|
||||
return response
|
||||
|
||||
@reporter.step("Deletes the specified IAM group")
|
||||
@report_error
|
||||
def iam_delete_group(self, group_name: str) -> dict:
|
||||
response = self.boto3_iam_client.delete_group(GroupName=group_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM Delete Group", response, {"GroupName": group_name})
|
||||
return response
|
||||
|
||||
@reporter.step("Deletes the specified inline policy that is embedded in the specified IAM group")
|
||||
@report_error
|
||||
def iam_delete_group_policy(self, group_name: str, policy_name: str) -> dict:
|
||||
response = self.boto3_iam_client.delete_group_policy(GroupName=group_name, PolicyName=policy_name)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.delete_group_policy(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Delete Group Policy", response, params)
|
||||
return response
|
||||
|
||||
@reporter.step("Deletes the specified managed policy")
|
||||
@report_error
|
||||
def iam_delete_policy(self, policy_arn: str) -> dict:
|
||||
response = self.boto3_iam_client.delete_policy(PolicyArn=policy_arn)
|
||||
log_command_execution(self.iam_endpoint, "IAM Delete Policy", response, {"PolicyArn": policy_arn})
|
||||
return response
|
||||
|
||||
@reporter.step("Deletes the specified IAM user")
|
||||
@report_error
|
||||
def iam_delete_user(self, user_name: str) -> dict:
|
||||
response = self.boto3_iam_client.delete_user(UserName=user_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM Delete User", response, {"UserName": user_name})
|
||||
return response
|
||||
|
||||
@reporter.step("Deletes the specified inline policy that is embedded in the specified IAM user")
|
||||
@report_error
|
||||
def iam_delete_user_policy(self, user_name: str, policy_name: str) -> dict:
|
||||
response = self.boto3_iam_client.delete_user_policy(UserName=user_name, PolicyName=policy_name)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.delete_user_policy(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Delete User Policy", response, params)
|
||||
return response
|
||||
|
||||
@reporter.step("Removes the specified managed policy from the specified IAM group")
|
||||
@report_error
|
||||
def iam_detach_group_policy(self, group_name: str, policy_arn: str) -> dict:
|
||||
response = self.boto3_iam_client.detach_group_policy(GroupName=group_name, PolicyArn=policy_arn)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.detach_group_policy(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Detach Group Policy", response, params)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
return response
|
||||
|
||||
@reporter.step("Removes the specified managed policy from the specified user")
|
||||
@report_error
|
||||
def iam_detach_user_policy(self, user_name: str, policy_arn: str) -> dict:
|
||||
response = self.boto3_iam_client.detach_user_policy(UserName=user_name, PolicyArn=policy_arn)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.detach_user_policy(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Detach User Policy", response, params)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
return response
|
||||
|
||||
@reporter.step("Returns a list of IAM users that are in the specified IAM group")
|
||||
@report_error
|
||||
def iam_get_group(self, group_name: str) -> dict:
|
||||
response = self.boto3_iam_client.get_group(GroupName=group_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM Get Group", response, {"GroupName": group_name})
|
||||
assert response.get("Group").get("GroupName") == group_name, f"GroupName should be equal to {group_name}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Retrieves the specified inline policy document that is embedded in the specified IAM group")
|
||||
@report_error
|
||||
def iam_get_group_policy(self, group_name: str, policy_name: str) -> dict:
|
||||
response = self.boto3_iam_client.get_group_policy(GroupName=group_name, PolicyName=policy_name)
|
||||
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.get_group_policy(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Get Group Policy", response, params)
|
||||
return response
|
||||
|
||||
@reporter.step("Retrieves information about the specified managed policy")
|
||||
@report_error
|
||||
def iam_get_policy(self, policy_arn: str) -> dict:
|
||||
response = self.boto3_iam_client.get_policy(PolicyArn=policy_arn)
|
||||
log_command_execution(self.iam_endpoint, "IAM Get Policy", response, {"PolicyArn": policy_arn})
|
||||
|
||||
assert response.get("Policy"), f"Expected Policy in response:\n{response}"
|
||||
assert response["Policy"].get("Arn") == policy_arn, f"PolicyArn should be equal to {policy_arn}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Retrieves information about the specified version of the specified managed policy")
|
||||
@report_error
|
||||
def iam_get_policy_version(self, policy_arn: str, version_id: str) -> dict:
|
||||
response = self.boto3_iam_client.get_policy_version(PolicyArn=policy_arn, VersionId=version_id)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.get_policy_version(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Get Policy Version", response, params)
|
||||
|
||||
assert response.get("PolicyVersion"), f"Expected PolicyVersion in response:\n{response}"
|
||||
assert response["PolicyVersion"].get("VersionId") == version_id, f"VersionId should be equal to {version_id}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Retrieves information about the specified IAM user")
|
||||
@report_error
|
||||
def iam_get_user(self, user_name: str) -> dict:
|
||||
response = self.boto3_iam_client.get_user(UserName=user_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM Get User", response, {"UserName": user_name})
|
||||
|
||||
assert response.get("User"), f"Expected User in response:\n{response}"
|
||||
assert response["User"].get("UserName") == user_name, f"UserName should be equal to {user_name}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Retrieves the specified inline policy document that is embedded in the specified IAM user")
|
||||
@report_error
|
||||
def iam_get_user_policy(self, user_name: str, policy_name: str) -> dict:
|
||||
response = self.boto3_iam_client.get_user_policy(UserName=user_name, PolicyName=policy_name)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.get_user_policy(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Get User Policy", response, params)
|
||||
assert response.get("UserName"), f"Expected UserName in response:\n{response}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Returns information about the access key IDs associated with the specified IAM user")
|
||||
@report_error
|
||||
def iam_list_access_keys(self, user_name: str) -> dict:
|
||||
response = self.boto3_iam_client.list_access_keys(UserName=user_name)
|
||||
|
||||
log_command_execution(self.iam_endpoint, "IAM List Access Keys", response, {"UserName": user_name})
|
||||
return response
|
||||
|
||||
@reporter.step("Lists all managed policies that are attached to the specified IAM group")
|
||||
@report_error
|
||||
def iam_list_attached_group_policies(self, group_name: str) -> dict:
|
||||
response = self.boto3_iam_client.list_attached_group_policies(GroupName=group_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM List Attached Group Policies", response, {"GroupName": group_name})
|
||||
assert response.get("AttachedPolicies"), f"Expected AttachedPolicies in response:\n{response}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Lists all managed policies that are attached to the specified IAM user")
|
||||
@report_error
|
||||
def iam_list_attached_user_policies(self, user_name: str) -> dict:
|
||||
response = self.boto3_iam_client.list_attached_user_policies(UserName=user_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM List Attached User Policies", response, {"UserName": user_name})
|
||||
assert response.get("AttachedPolicies"), f"Expected AttachedPolicies in response:\n{response}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Lists all IAM users, groups, and roles that the specified managed policy is attached to")
|
||||
@report_error
|
||||
def iam_list_entities_for_policy(self, policy_arn: str) -> dict:
|
||||
response = self.boto3_iam_client.list_entities_for_policy(PolicyArn=policy_arn)
|
||||
log_command_execution(self.iam_endpoint, "IAM List Entities For Policy", response, {"PolicyArn": policy_arn})
|
||||
|
||||
assert response.get("PolicyGroups"), f"Expected PolicyGroups in response:\n{response}"
|
||||
assert response.get("PolicyUsers"), f"Expected PolicyUsers in response:\n{response}"
|
||||
|
@ -833,98 +923,125 @@ class Boto3ClientWrapper(S3ClientWrapper):
|
|||
return response
|
||||
|
||||
@reporter.step("Lists the names of the inline policies that are embedded in the specified IAM group")
|
||||
@report_error
|
||||
def iam_list_group_policies(self, group_name: str) -> dict:
|
||||
response = self.boto3_iam_client.list_group_policies(GroupName=group_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM List Group Policies", response, {"GroupName": group_name})
|
||||
assert response.get("PolicyNames"), f"Expected PolicyNames in response:\n{response}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Lists the IAM groups")
|
||||
@report_error
|
||||
def iam_list_groups(self) -> dict:
|
||||
response = self.boto3_iam_client.list_groups()
|
||||
log_command_execution(self.iam_endpoint, "IAM List Groups", response)
|
||||
assert response.get("Groups"), f"Expected Groups in response:\n{response}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Lists the IAM groups that the specified IAM user belongs to")
|
||||
@report_error
|
||||
def iam_list_groups_for_user(self, user_name: str) -> dict:
|
||||
response = self.boto3_iam_client.list_groups_for_user(UserName=user_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM List Groups For User", response, {"UserName": user_name})
|
||||
assert response.get("Groups"), f"Expected Groups in response:\n{response}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Lists all the managed policies that are available in your AWS account")
|
||||
@report_error
|
||||
def iam_list_policies(self) -> dict:
|
||||
response = self.boto3_iam_client.list_policies()
|
||||
log_command_execution(self.iam_endpoint, "IAM List Policies", response)
|
||||
assert response.get("Policies"), f"Expected Policies in response:\n{response}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Lists information about the versions of the specified managed policy")
|
||||
@report_error
|
||||
def iam_list_policy_versions(self, policy_arn: str) -> dict:
|
||||
response = self.boto3_iam_client.list_policy_versions(PolicyArn=policy_arn)
|
||||
log_command_execution(self.iam_endpoint, "IAM List Policy Versions", response, {"PolicyArn": policy_arn})
|
||||
assert response.get("Versions"), f"Expected Versions in response:\n{response}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Lists the names of the inline policies embedded in the specified IAM user")
|
||||
@report_error
|
||||
def iam_list_user_policies(self, user_name: str) -> dict:
|
||||
response = self.boto3_iam_client.list_user_policies(UserName=user_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM List User Policies", response, {"UserName": user_name})
|
||||
assert response.get("PolicyNames"), f"Expected PolicyNames in response:\n{response}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Lists the IAM users")
|
||||
@report_error
|
||||
def iam_list_users(self) -> dict:
|
||||
response = self.boto3_iam_client.list_users()
|
||||
log_command_execution(self.iam_endpoint, "IAM List Users", response)
|
||||
assert response.get("Users"), f"Expected Users in response:\n{response}"
|
||||
|
||||
return response
|
||||
|
||||
@reporter.step("Adds or updates an inline policy document that is embedded in the specified IAM group")
|
||||
@report_error
|
||||
def iam_put_group_policy(self, group_name: str, policy_name: str, policy_document: dict) -> dict:
|
||||
response = self.boto3_iam_client.put_group_policy(
|
||||
GroupName=group_name, PolicyName=policy_name, PolicyDocument=json.dumps(policy_document)
|
||||
)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
params["PolicyDocument"] = json.dumps(policy_document)
|
||||
response = self.boto3_iam_client.put_group_policy(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Put Group Policy", response, params)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
return response
|
||||
|
||||
@reporter.step("Adds or updates an inline policy document that is embedded in the specified IAM user")
|
||||
@report_error
|
||||
def iam_put_user_policy(self, user_name: str, policy_name: str, policy_document: dict) -> dict:
|
||||
response = self.boto3_iam_client.put_user_policy(
|
||||
UserName=user_name, PolicyName=policy_name, PolicyDocument=json.dumps(policy_document)
|
||||
)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
params["PolicyDocument"] = json.dumps(policy_document)
|
||||
response = self.boto3_iam_client.put_user_policy(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Put User Policy", response, params)
|
||||
sleep(S3_SYNC_WAIT_TIME * 10)
|
||||
return response
|
||||
|
||||
@reporter.step("Removes the specified user from the specified group")
|
||||
@report_error
|
||||
def iam_remove_user_from_group(self, group_name: str, user_name: str) -> dict:
|
||||
response = self.boto3_iam_client.remove_user_from_group(GroupName=group_name, UserName=user_name)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.remove_user_from_group(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Remove User From Group", response, params)
|
||||
return response
|
||||
|
||||
@reporter.step("Updates the name and/or the path of the specified IAM group")
|
||||
@report_error
|
||||
def iam_update_group(self, group_name: str, new_name: str, new_path: Optional[str] = None) -> dict:
|
||||
response = self.boto3_iam_client.update_group(GroupName=group_name, NewGroupName=new_name, NewPath="/")
|
||||
|
||||
params = {"GroupName": group_name, "NewGroupName": new_name, "NewPath": "/"}
|
||||
response = self.boto3_iam_client.update_group(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Update Group", response, params)
|
||||
return response
|
||||
|
||||
@reporter.step("Updates the name and/or the path of the specified IAM user")
|
||||
@report_error
|
||||
def iam_update_user(self, user_name: str, new_name: str, new_path: Optional[str] = None) -> dict:
|
||||
response = self.boto3_iam_client.update_user(UserName=user_name, NewUserName=new_name, NewPath="/")
|
||||
params = {"UserName": user_name, "NewUserName": new_name, "NewPath": "/"}
|
||||
response = self.boto3_iam_client.update_user(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Update User", response, params)
|
||||
return response
|
||||
|
||||
@reporter.step("Adds one or more tags to an IAM user")
|
||||
@report_error
|
||||
def iam_tag_user(self, user_name: str, tags: list) -> dict:
|
||||
tags_json = [{"Key": tag_key, "Value": tag_value} for tag_key, tag_value in tags]
|
||||
response = self.boto3_iam_client.tag_user(UserName=user_name, Tags=tags_json)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
params["Tags"] = [{"Key": tag_key, "Value": tag_value} for tag_key, tag_value in tags]
|
||||
response = self.boto3_iam_client.tag_user(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Tag User", response, params)
|
||||
return response
|
||||
|
||||
@reporter.step("List tags of IAM user")
|
||||
@report_error
|
||||
def iam_list_user_tags(self, user_name: str) -> dict:
|
||||
response = self.boto3_iam_client.list_user_tags(UserName=user_name)
|
||||
log_command_execution(self.iam_endpoint, "IAM List User Tags", response, {"UserName": user_name})
|
||||
return response
|
||||
|
||||
@reporter.step("Removes the specified tags from the user")
|
||||
@report_error
|
||||
def iam_untag_user(self, user_name: str, tag_keys: list) -> dict:
|
||||
response = self.boto3_iam_client.untag_user(UserName=user_name, TagKeys=tag_keys)
|
||||
params = self._convert_to_s3_params(locals().items())
|
||||
response = self.boto3_iam_client.untag_user(**params)
|
||||
log_command_execution(self.iam_endpoint, "IAM Untag User", response, params)
|
||||
return response
|
||||
|
|
|
@ -58,6 +58,10 @@ class S3ClientWrapper(HumanReadableABC):
|
|||
def set_endpoint(self, s3gate_endpoint: str):
|
||||
"""Set endpoint"""
|
||||
|
||||
@abstractmethod
|
||||
def set_iam_endpoint(self, iam_endpoint: str):
|
||||
"""Set iam endpoint"""
|
||||
|
||||
@abstractmethod
|
||||
def create_bucket(
|
||||
self,
|
||||
|
@ -366,6 +370,18 @@ class S3ClientWrapper(HumanReadableABC):
|
|||
def delete_object_tagging(self, bucket: str, key: str) -> None:
|
||||
"""Removes the entire tag set from the specified object."""
|
||||
|
||||
@abstractmethod
|
||||
def put_bucket_lifecycle_configuration(self, bucket: str, lifecycle_configuration: dict, dumped_configuration: str) -> dict:
|
||||
"""Adds or updates bucket lifecycle configuration"""
|
||||
|
||||
@abstractmethod
|
||||
def get_bucket_lifecycle_configuration(self, bucket: str) -> dict:
|
||||
"""Gets bucket lifecycle configuration"""
|
||||
|
||||
@abstractmethod
|
||||
def delete_bucket_lifecycle(self, bucket: str) -> dict:
|
||||
"""Deletes bucket lifecycle"""
|
||||
|
||||
@abstractmethod
|
||||
def get_object_attributes(
|
||||
self,
|
||||
|
|
|
@ -69,7 +69,7 @@ def get_epoch(shell: Shell, cluster: Cluster, alive_node: Optional[StorageNode]
|
|||
|
||||
|
||||
@reporter.step("Tick Epoch")
|
||||
def tick_epoch(shell: Shell, cluster: Cluster, alive_node: Optional[StorageNode] = None):
|
||||
def tick_epoch(shell: Shell, cluster: Cluster, alive_node: Optional[StorageNode] = None, delta: Optional[int] = None):
|
||||
"""
|
||||
Tick epoch using frostfs-adm or NeoGo if frostfs-adm is not available (DevEnv)
|
||||
Args:
|
||||
|
@ -88,12 +88,17 @@ def tick_epoch(shell: Shell, cluster: Cluster, alive_node: Optional[StorageNode]
|
|||
frostfs_adm_exec_path=FROSTFS_ADM_EXEC,
|
||||
config_file=FROSTFS_ADM_CONFIG_PATH,
|
||||
)
|
||||
frostfs_adm.morph.force_new_epoch()
|
||||
frostfs_adm.morph.force_new_epoch(delta=delta)
|
||||
return
|
||||
|
||||
# Otherwise we tick epoch using transaction
|
||||
cur_epoch = get_epoch(shell, cluster)
|
||||
|
||||
if delta:
|
||||
next_epoch = cur_epoch + delta
|
||||
else:
|
||||
next_epoch = cur_epoch + 1
|
||||
|
||||
# Use first node by default
|
||||
ir_node = cluster.services(InnerRing)[0]
|
||||
# In case if no local_wallet_path is provided, we use wallet_path
|
||||
|
@ -110,7 +115,7 @@ def tick_epoch(shell: Shell, cluster: Cluster, alive_node: Optional[StorageNode]
|
|||
wallet_password=ir_wallet_pass,
|
||||
scripthash=get_contract_hash(morph_chain, "netmap.frostfs", shell=shell),
|
||||
method="newEpoch",
|
||||
arguments=f"int:{cur_epoch + 1}",
|
||||
arguments=f"int:{next_epoch}",
|
||||
multisig_hash=f"{ir_address}:Global",
|
||||
address=ir_address,
|
||||
rpc_endpoint=morph_endpoint,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import re
|
||||
|
||||
from frostfs_testlib import reporter
|
||||
from frostfs_testlib.testing.test_control import wait_for_success
|
||||
from frostfs_testlib.storage.cluster import ClusterNode
|
||||
from frostfs_testlib.testing.test_control import wait_for_success
|
||||
|
||||
|
||||
@reporter.step("Check metrics result")
|
||||
|
@ -19,7 +19,7 @@ def check_metrics_counter(
|
|||
counter_act += get_metrics_value(cluster_node, parse_from_command, **metrics_greps)
|
||||
assert eval(
|
||||
f"{counter_act} {operator} {counter_exp}"
|
||||
), f"Expected: {counter_exp} {operator} Actual: {counter_act} in node: {cluster_node}"
|
||||
), f"Expected: {counter_exp} {operator} Actual: {counter_act} in nodes: {cluster_nodes}"
|
||||
|
||||
|
||||
@reporter.step("Get metrics value from node: {node}")
|
||||
|
|
|
@ -25,12 +25,8 @@ class ClusterTestBase:
|
|||
for _ in range(epochs_to_tick):
|
||||
self.tick_epoch(alive_node, wait_block)
|
||||
|
||||
def tick_epoch(
|
||||
self,
|
||||
alive_node: Optional[StorageNode] = None,
|
||||
wait_block: int = None,
|
||||
):
|
||||
epoch.tick_epoch(self.shell, self.cluster, alive_node=alive_node)
|
||||
def tick_epoch(self, alive_node: Optional[StorageNode] = None, wait_block: int = None, delta: Optional[int] = None):
|
||||
epoch.tick_epoch(self.shell, self.cluster, alive_node=alive_node, delta=delta)
|
||||
if wait_block:
|
||||
self.wait_for_blocks(wait_block)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ ONLY_ASCII_LETTERS = string.ascii_letters
|
|||
DIGITS_AND_ASCII_LETTERS = string.ascii_letters + string.digits
|
||||
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))
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue