Compare commits
6 commits
master
...
support/v0
Author | SHA1 | Date | |
---|---|---|---|
db0db9ff1a | |||
8944eee057 | |||
57d60068bc | |||
d82c2d86c9 | |||
4be14ed159 | |||
6869800d87 |
6 changed files with 68 additions and 47 deletions
|
@ -7,7 +7,7 @@ import allure
|
||||||
import pytest
|
import pytest
|
||||||
from frostfs_testlib import reporter
|
from frostfs_testlib import reporter
|
||||||
from frostfs_testlib.healthcheck.interfaces import Healthcheck
|
from frostfs_testlib.healthcheck.interfaces import Healthcheck
|
||||||
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
|
from frostfs_testlib.resources.wellknown_acl import EACL_PUBLIC_READ_WRITE, PUBLIC_ACL
|
||||||
from frostfs_testlib.steps.cli.container import create_container
|
from frostfs_testlib.steps.cli.container import create_container
|
||||||
from frostfs_testlib.steps.cli.object import (
|
from frostfs_testlib.steps.cli.object import (
|
||||||
get_object,
|
get_object,
|
||||||
|
@ -88,6 +88,7 @@ class TestFailoverNetwork(ClusterTestBase):
|
||||||
endpoint=self.cluster.default_rpc_endpoint,
|
endpoint=self.cluster.default_rpc_endpoint,
|
||||||
rule=placement_rule,
|
rule=placement_rule,
|
||||||
await_mode=True,
|
await_mode=True,
|
||||||
|
basic_acl=EACL_PUBLIC_READ_WRITE,
|
||||||
)
|
)
|
||||||
|
|
||||||
storage_objects = []
|
storage_objects = []
|
||||||
|
@ -210,11 +211,9 @@ class TestFailoverNetwork(ClusterTestBase):
|
||||||
with reporter.step("Search nodes with object"):
|
with reporter.step("Search nodes with object"):
|
||||||
nodes_with_object = get_object_nodes(
|
nodes_with_object = get_object_nodes(
|
||||||
cluster=self.cluster,
|
cluster=self.cluster,
|
||||||
wallet=default_wallet,
|
|
||||||
cid=storage_object.cid,
|
cid=storage_object.cid,
|
||||||
oid=storage_object.oid,
|
oid=storage_object.oid,
|
||||||
shell=self.shell,
|
alive_node=self.cluster.cluster_nodes[0],
|
||||||
endpoint=self.cluster.default_rpc_endpoint,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with reporter.step("Get data interface to node"):
|
with reporter.step("Get data interface to node"):
|
||||||
|
@ -270,11 +269,9 @@ class TestFailoverNetwork(ClusterTestBase):
|
||||||
with reporter.step("Search nodes with object"):
|
with reporter.step("Search nodes with object"):
|
||||||
nodes_with_object = get_object_nodes(
|
nodes_with_object = get_object_nodes(
|
||||||
cluster=self.cluster,
|
cluster=self.cluster,
|
||||||
wallet=default_wallet,
|
|
||||||
cid=storage_object.cid,
|
cid=storage_object.cid,
|
||||||
oid=storage_object.oid,
|
oid=storage_object.oid,
|
||||||
shell=self.shell,
|
alive_node=self.cluster.cluster_nodes[0],
|
||||||
endpoint=self.cluster.default_rpc_endpoint,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with reporter.step("Get internal interface to node"):
|
with reporter.step("Get internal interface to node"):
|
||||||
|
|
|
@ -156,12 +156,7 @@ class TestFailoverServer(ClusterTestBase):
|
||||||
) -> tuple[StorageObjectInfo, list[ClusterNode]]:
|
) -> tuple[StorageObjectInfo, list[ClusterNode]]:
|
||||||
object_info = container.generate_object(simple_object_size.value)
|
object_info = container.generate_object(simple_object_size.value)
|
||||||
object_nodes = get_object_nodes(
|
object_nodes = get_object_nodes(
|
||||||
cluster=self.cluster,
|
cluster=self.cluster, cid=object_info.cid, oid=object_info.oid, alive_node=self.cluster.cluster_nodes[0]
|
||||||
wallet=default_wallet,
|
|
||||||
cid=object_info.cid,
|
|
||||||
oid=object_info.oid,
|
|
||||||
shell=self.shell,
|
|
||||||
endpoint=self.cluster.default_rpc_endpoint,
|
|
||||||
)
|
)
|
||||||
return object_info, object_nodes
|
return object_info, object_nodes
|
||||||
|
|
||||||
|
@ -313,12 +308,7 @@ class TestFailoverServer(ClusterTestBase):
|
||||||
)
|
)
|
||||||
with reporter.step("Search nodes with object"):
|
with reporter.step("Search nodes with object"):
|
||||||
object_nodes = get_object_nodes(
|
object_nodes = get_object_nodes(
|
||||||
cluster=self.cluster,
|
cluster=self.cluster, cid=cid_1, oid=oid, alive_node=self.cluster.cluster_nodes[0]
|
||||||
wallet=default_wallet,
|
|
||||||
cid=cid_1,
|
|
||||||
oid=oid,
|
|
||||||
shell=self.shell,
|
|
||||||
endpoint=self.cluster.default_rpc_endpoint,
|
|
||||||
)
|
)
|
||||||
with reporter.step("Turn off random node with object"):
|
with reporter.step("Turn off random node with object"):
|
||||||
cluster_state_controller.stop_node_host(node=random.choice(object_nodes[1:]), mode="hard")
|
cluster_state_controller.stop_node_host(node=random.choice(object_nodes[1:]), mode="hard")
|
||||||
|
|
|
@ -20,6 +20,7 @@ from frostfs_testlib.steps.node_management import (
|
||||||
wait_for_node_to_be_ready,
|
wait_for_node_to_be_ready,
|
||||||
)
|
)
|
||||||
from frostfs_testlib.steps.s3 import s3_helper
|
from frostfs_testlib.steps.s3 import s3_helper
|
||||||
|
from frostfs_testlib.steps.s3.s3_helper import search_nodes_with_bucket
|
||||||
from frostfs_testlib.storage.cluster import Cluster, ClusterNode, S3Gate, StorageNode
|
from frostfs_testlib.storage.cluster import Cluster, ClusterNode, S3Gate, StorageNode
|
||||||
from frostfs_testlib.storage.controllers import ClusterStateController, ShardsWatcher
|
from frostfs_testlib.storage.controllers import ClusterStateController, ShardsWatcher
|
||||||
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
|
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
|
||||||
|
@ -126,6 +127,7 @@ class TestFailoverStorage(ClusterTestBase):
|
||||||
def test_unhealthy_tree(
|
def test_unhealthy_tree(
|
||||||
self,
|
self,
|
||||||
s3_client: S3ClientWrapper,
|
s3_client: S3ClientWrapper,
|
||||||
|
default_wallet: str,
|
||||||
simple_object_size: ObjectSize,
|
simple_object_size: ObjectSize,
|
||||||
cluster_state_controller: ClusterStateController,
|
cluster_state_controller: ClusterStateController,
|
||||||
after_run_return_all_stopped_services,
|
after_run_return_all_stopped_services,
|
||||||
|
@ -155,11 +157,22 @@ class TestFailoverStorage(ClusterTestBase):
|
||||||
put_object = s3_client.put_object(bucket, file_path)
|
put_object = s3_client.put_object(bucket, file_path)
|
||||||
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
|
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
|
||||||
|
|
||||||
with reporter.step("Turn off all storage nodes except default"):
|
node_bucket = search_nodes_with_bucket(
|
||||||
for node in self.cluster.cluster_nodes[1:]:
|
cluster=self.cluster,
|
||||||
|
bucket_name=bucket,
|
||||||
|
wallet=default_wallet,
|
||||||
|
shell=self.shell,
|
||||||
|
endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(),
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
with reporter.step("Turn off all storage nodes except bucket node"):
|
||||||
|
for node in [node_to_stop for node_to_stop in self.cluster.cluster_nodes if node_to_stop != node_bucket]:
|
||||||
with reporter.step(f"Stop storage service on node: {node}"):
|
with reporter.step(f"Stop storage service on node: {node}"):
|
||||||
cluster_state_controller.stop_service_of_type(node, StorageNode)
|
cluster_state_controller.stop_service_of_type(node, StorageNode)
|
||||||
|
|
||||||
|
with reporter.step(f"Change s3 endpoint to bucket node"):
|
||||||
|
s3_client.set_endpoint(node_bucket.s3_gate.get_endpoint())
|
||||||
|
|
||||||
with reporter.step("Check that object is available"):
|
with reporter.step("Check that object is available"):
|
||||||
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
|
s3_helper.check_objects_in_bucket(s3_client, bucket, expected_objects=[file_name])
|
||||||
|
|
||||||
|
|
|
@ -16,15 +16,23 @@ PART_SIZE = 5 * 1024 * 1024
|
||||||
class TestS3GateMultipart(ClusterTestBase):
|
class TestS3GateMultipart(ClusterTestBase):
|
||||||
NO_SUCH_UPLOAD = "The upload ID may be invalid, or the upload may have been aborted or completed."
|
NO_SUCH_UPLOAD = "The upload ID may be invalid, or the upload may have been aborted or completed."
|
||||||
|
|
||||||
@allure.title("Object Multipart API (s3_client={s3_client})")
|
@allure.title("Object Multipart API (s3_client={s3_client}, bucket versioning = {versioning_status})")
|
||||||
@pytest.mark.parametrize("versioning_status", [VersioningStatus.ENABLED], indirect=True)
|
@pytest.mark.parametrize("versioning_status", [VersioningStatus.ENABLED, VersioningStatus.UNDEFINED], indirect=True)
|
||||||
def test_s3_object_multipart(self, s3_client: S3ClientWrapper, bucket: str):
|
def test_s3_object_multipart(
|
||||||
|
self, s3_client: S3ClientWrapper, bucket: str, default_wallet: str, versioning_status: str
|
||||||
|
):
|
||||||
parts_count = 5
|
parts_count = 5
|
||||||
file_name_large = generate_file(PART_SIZE * parts_count) # 5Mb - min part
|
file_name_large = generate_file(PART_SIZE * parts_count) # 5Mb - min part
|
||||||
object_key = s3_helper.object_key_from_file_path(file_name_large)
|
object_key = s3_helper.object_key_from_file_path(file_name_large)
|
||||||
part_files = split_file(file_name_large, parts_count)
|
part_files = split_file(file_name_large, parts_count)
|
||||||
parts = []
|
parts = []
|
||||||
|
|
||||||
|
with reporter.step(f"Get related container_id for bucket"):
|
||||||
|
for cluster_node in self.cluster.cluster_nodes:
|
||||||
|
container_id = search_container_by_name(bucket, cluster_node)
|
||||||
|
if container_id:
|
||||||
|
break
|
||||||
|
|
||||||
with reporter.step("Upload first part"):
|
with reporter.step("Upload first part"):
|
||||||
upload_id = s3_client.create_multipart_upload(bucket, object_key)
|
upload_id = s3_client.create_multipart_upload(bucket, object_key)
|
||||||
uploads = s3_client.list_multipart_uploads(bucket)
|
uploads = s3_client.list_multipart_uploads(bucket)
|
||||||
|
@ -38,7 +46,11 @@ class TestS3GateMultipart(ClusterTestBase):
|
||||||
etag = s3_client.upload_part(bucket, object_key, upload_id, part_id, file_path)
|
etag = s3_client.upload_part(bucket, object_key, upload_id, part_id, file_path)
|
||||||
parts.append((part_id, etag))
|
parts.append((part_id, etag))
|
||||||
got_parts = s3_client.list_parts(bucket, object_key, upload_id)
|
got_parts = s3_client.list_parts(bucket, object_key, upload_id)
|
||||||
s3_client.complete_multipart_upload(bucket, object_key, upload_id, parts)
|
response = s3_client.complete_multipart_upload(bucket, object_key, upload_id, parts)
|
||||||
|
|
||||||
|
version_id = None
|
||||||
|
if versioning_status == VersioningStatus.ENABLED:
|
||||||
|
version_id = response["VersionId"]
|
||||||
assert len(got_parts) == len(part_files), f"Expected {parts_count} parts, got\n{got_parts}"
|
assert len(got_parts) == len(part_files), f"Expected {parts_count} parts, got\n{got_parts}"
|
||||||
|
|
||||||
with reporter.step("Check upload list is empty"):
|
with reporter.step("Check upload list is empty"):
|
||||||
|
@ -49,6 +61,21 @@ class TestS3GateMultipart(ClusterTestBase):
|
||||||
got_object = s3_client.get_object(bucket, object_key)
|
got_object = s3_client.get_object(bucket, object_key)
|
||||||
assert get_file_hash(got_object) == get_file_hash(file_name_large)
|
assert get_file_hash(got_object) == get_file_hash(file_name_large)
|
||||||
|
|
||||||
|
if version_id:
|
||||||
|
with reporter.step("Delete the object version"):
|
||||||
|
s3_client.delete_object(bucket, object_key, version_id)
|
||||||
|
else:
|
||||||
|
with reporter.step("Delete the object"):
|
||||||
|
s3_client.delete_object(bucket, object_key)
|
||||||
|
|
||||||
|
with reporter.step("List objects in the bucket, expect to be empty"):
|
||||||
|
objects_list = s3_client.list_objects(bucket)
|
||||||
|
assert not objects_list, f"Expected empty bucket, got {objects_list}"
|
||||||
|
|
||||||
|
with reporter.step("List objects in the container via rpc, expect to be empty"):
|
||||||
|
objects = list_objects(default_wallet, self.shell, container_id, self.cluster.default_rpc_endpoint)
|
||||||
|
assert len(objects) == 0, f"Expected no objects in container, got\n{objects}"
|
||||||
|
|
||||||
@allure.title("Abort Multipart Upload (s3_client={s3_client})")
|
@allure.title("Abort Multipart Upload (s3_client={s3_client})")
|
||||||
@pytest.mark.parametrize("versioning_status", [VersioningStatus.ENABLED], indirect=True)
|
@pytest.mark.parametrize("versioning_status", [VersioningStatus.ENABLED], indirect=True)
|
||||||
def test_s3_abort_multipart(
|
def test_s3_abort_multipart(
|
||||||
|
|
|
@ -51,12 +51,10 @@ class TestS3GatePolicy(ClusterTestBase):
|
||||||
assert bucket_loc_2 == "rep-3"
|
assert bucket_loc_2 == "rep-3"
|
||||||
|
|
||||||
with reporter.step("Check object policy"):
|
with reporter.step("Check object policy"):
|
||||||
cid_1 = search_container_by_name(
|
for cluster_node in self.cluster.cluster_nodes:
|
||||||
default_wallet,
|
cid_1 = search_container_by_name(name=bucket_1, node=cluster_node)
|
||||||
bucket_1,
|
if cid_1:
|
||||||
shell=self.shell,
|
break
|
||||||
endpoint=self.cluster.default_rpc_endpoint,
|
|
||||||
)
|
|
||||||
copies_1 = get_simple_object_copies(
|
copies_1 = get_simple_object_copies(
|
||||||
wallet=default_wallet,
|
wallet=default_wallet,
|
||||||
cid=cid_1,
|
cid=cid_1,
|
||||||
|
@ -65,12 +63,10 @@ class TestS3GatePolicy(ClusterTestBase):
|
||||||
nodes=self.cluster.storage_nodes,
|
nodes=self.cluster.storage_nodes,
|
||||||
)
|
)
|
||||||
assert copies_1 == 1
|
assert copies_1 == 1
|
||||||
cid_2 = search_container_by_name(
|
for cluster_node in self.cluster.cluster_nodes:
|
||||||
default_wallet,
|
cid_2 = search_container_by_name(name=bucket_1, node=cluster_node)
|
||||||
bucket_2,
|
if cid_2:
|
||||||
shell=self.shell,
|
break
|
||||||
endpoint=self.cluster.default_rpc_endpoint,
|
|
||||||
)
|
|
||||||
copies_2 = get_simple_object_copies(
|
copies_2 = get_simple_object_copies(
|
||||||
wallet=default_wallet,
|
wallet=default_wallet,
|
||||||
cid=cid_2,
|
cid=cid_2,
|
||||||
|
|
|
@ -7,6 +7,7 @@ from frostfs_testlib import reporter
|
||||||
from frostfs_testlib.cli import FrostfsCli
|
from frostfs_testlib.cli import FrostfsCli
|
||||||
from frostfs_testlib.resources.cli import CLI_DEFAULT_TIMEOUT
|
from frostfs_testlib.resources.cli import CLI_DEFAULT_TIMEOUT
|
||||||
from frostfs_testlib.resources.common import DEFAULT_WALLET_CONFIG
|
from frostfs_testlib.resources.common import DEFAULT_WALLET_CONFIG
|
||||||
|
from frostfs_testlib.resources.wellknown_acl import EACL_PUBLIC_READ_WRITE
|
||||||
from frostfs_testlib.steps.cli.container import create_container, delete_container
|
from frostfs_testlib.steps.cli.container import create_container, delete_container
|
||||||
from frostfs_testlib.steps.cli.object import delete_object, get_object, get_object_nodes, put_object
|
from frostfs_testlib.steps.cli.object import delete_object, get_object, get_object_nodes, put_object
|
||||||
from frostfs_testlib.storage.cluster import Cluster, ClusterNode, StorageNode
|
from frostfs_testlib.storage.cluster import Cluster, ClusterNode, StorageNode
|
||||||
|
@ -52,21 +53,18 @@ class TestControlShard(ClusterTestBase):
|
||||||
def oid_cid_node(self, default_wallet: str) -> tuple[str, str, ClusterNode]:
|
def oid_cid_node(self, default_wallet: str) -> tuple[str, str, ClusterNode]:
|
||||||
with reporter.step("Create container, and put object"):
|
with reporter.step("Create container, and put object"):
|
||||||
cid = create_container(
|
cid = create_container(
|
||||||
wallet=default_wallet, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint, rule="REP 1 CBF 1"
|
wallet=default_wallet,
|
||||||
|
shell=self.shell,
|
||||||
|
endpoint=self.cluster.default_rpc_endpoint,
|
||||||
|
rule="REP 1 CBF 1",
|
||||||
|
basic_acl=EACL_PUBLIC_READ_WRITE,
|
||||||
)
|
)
|
||||||
file = generate_file(5242880)
|
file = generate_file(5242880)
|
||||||
oid = put_object(
|
oid = put_object(
|
||||||
wallet=default_wallet, path=file, cid=cid, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint
|
wallet=default_wallet, path=file, cid=cid, shell=self.shell, endpoint=self.cluster.default_rpc_endpoint
|
||||||
)
|
)
|
||||||
with reporter.step("Search node with object"):
|
with reporter.step("Search node with object"):
|
||||||
nodes = get_object_nodes(
|
nodes = get_object_nodes(cluster=self.cluster, cid=cid, oid=oid, alive_node=self.cluster.cluster_nodes[0])
|
||||||
cluster=self.cluster,
|
|
||||||
wallet=default_wallet,
|
|
||||||
cid=cid,
|
|
||||||
oid=oid,
|
|
||||||
shell=self.shell,
|
|
||||||
endpoint=self.cluster.default_rpc_endpoint,
|
|
||||||
)
|
|
||||||
|
|
||||||
yield oid, cid, nodes[0]
|
yield oid, cid, nodes[0]
|
||||||
|
|
||||||
|
@ -136,7 +134,7 @@ class TestControlShard(ClusterTestBase):
|
||||||
revert_all_shards_mode: None,
|
revert_all_shards_mode: None,
|
||||||
):
|
):
|
||||||
oid, cid, node = oid_cid_node
|
oid, cid, node = oid_cid_node
|
||||||
object_path, object_name = self.get_object_path_and_name_file(oid, cid, node)
|
object_path, object_name = self.get_object_path_and_name_file(*oid_cid_node)
|
||||||
with reporter.step("Block read file"):
|
with reporter.step("Block read file"):
|
||||||
node.host.get_shell().exec(f"chmod a-r {object_path}/{object_name}")
|
node.host.get_shell().exec(f"chmod a-r {object_path}/{object_name}")
|
||||||
with reporter.step("Get object, expect 6 errors"):
|
with reporter.step("Get object, expect 6 errors"):
|
||||||
|
|
Loading…
Reference in a new issue