Skip to content

Commit 3f27cca

Browse files
authored
fix(obs): method added (#622)
fix(obs): cleanup Reviewed-by: Anton Sidelnikov
1 parent 1abb61a commit 3f27cca

File tree

5 files changed

+183
-0
lines changed

5 files changed

+183
-0
lines changed

otcextensions/sdk/obs/v1/_proxy.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
from otcextensions.sdk import sdk_proxy
2424
from otcextensions.sdk.obs.v1 import container as _container
2525
from otcextensions.sdk.obs.v1 import obj as _obj
26+
from openstack import utils as os_utils
27+
2628

2729
DEFAULT_OBJECT_SEGMENT_SIZE = 1073741824 # 1GB
2830
DEFAULT_MAX_FILE_SIZE = int((5 * 1024 * 1024 * 1024 + 2) / 2)
@@ -772,3 +774,126 @@ def _abort_multipart_upload(
772774
"""
773775
url = f'{endpoint}?uploadId={upload_id}'
774776
return self.delete(url, requests_auth=self._get_req_auth(endpoint))
777+
778+
def wait_for_delete_object(self, obj, container=None,
779+
interval=2, wait=300):
780+
"""Waits for an object to be deleted.
781+
782+
:param obj: The value can be the name of an object or a
783+
:class:`~otcextensions.sdk.obs.v1.obj.Object` instance.
784+
:param container: The value can be the name of a container or a
785+
:class:`~otcextensions.sdk.obs.v1.container.Container`.
786+
:param int interval: The time in seconds to wait between checks.
787+
:param int wait: The maximum time in seconds to wait for the object
788+
to be deleted.
789+
790+
:returns: The last head response from the server.
791+
:raises: :class:`~openstack.exceptions.ResourceTimeout` if the
792+
object is not deleted in the specified time.
793+
"""
794+
obj = self._get_resource(_obj.Object, obj)
795+
container = self._get_container_name(obj, container)
796+
for _ in os_utils.iterate_timeout(
797+
timeout=wait,
798+
message=f"Timeout waiting for {obj.name} "
799+
f"in {container} to delete",
800+
wait=interval,
801+
):
802+
try:
803+
obj.fetch(self, container=container, skip_cache=True)
804+
except exceptions.NotFoundException:
805+
return obj
806+
807+
raise exceptions.ResourceTimeout(
808+
f"Object {obj.name} in {container} was not "
809+
f"deleted in {wait} seconds"
810+
)
811+
812+
def wait_for_delete_container(self, container, interval=2, wait=180):
813+
"""Waits for a container to be deleted.
814+
815+
:param container: The value can be either the name of a container or a
816+
:class:`~otcextensions.sdk.obs.v1.container.Container`
817+
instance.
818+
:param int interval: The time in seconds to wait between checks.
819+
:param int wait: The maximum time in seconds to wait for the container
820+
to be deleted.
821+
:returns: The last head response from the server.
822+
:raises: :class:`~openstack.exceptions.ResourceTimeout` if the
823+
container is not deleted in the specified time.
824+
"""
825+
container = self._get_resource(_container.Container, container)
826+
827+
for _ in os_utils.iterate_timeout(
828+
timeout=wait,
829+
message=f"Timeout waiting for container"
830+
f" {container.name} to delete",
831+
wait=interval,
832+
):
833+
try:
834+
self.get_container(container.name)
835+
except exceptions.NotFoundException:
836+
return container
837+
raise exceptions.ResourceTimeout(
838+
f"Container {container.name} was not deleted in {wait} seconds"
839+
)
840+
841+
def _get_cleanup_dependencies(self):
842+
return {
843+
'obs': {
844+
'before': ['network']
845+
}
846+
}
847+
848+
def _service_cleanup(
849+
self,
850+
dry_run=True,
851+
client_status_queue=None,
852+
identified_resources=None,
853+
filters=None,
854+
resource_evaluation_fn=None,
855+
skip_resources=None,
856+
cont_name=None,
857+
):
858+
pass
859+
containers = []
860+
for container in self.containers():
861+
objects = []
862+
for obj in self.objects(container=container.name):
863+
need_delete = self._service_cleanup_del_res(
864+
lambda o: self.delete_object(o, container=container.name),
865+
obj,
866+
dry_run=dry_run,
867+
client_status_queue=client_status_queue,
868+
identified_resources=identified_resources,
869+
filters=filters,
870+
resource_evaluation_fn=resource_evaluation_fn
871+
)
872+
if not dry_run and need_delete:
873+
objects.append(obj)
874+
for obj in objects:
875+
try:
876+
self.wait_for_delete_object(obj, container=container.name)
877+
except Exception as e:
878+
self.log.warning(
879+
f"Failed to delete object {obj.name} "
880+
f"from {container.name}: {e}"
881+
)
882+
need_delete = self._service_cleanup_del_res(
883+
self.delete_container,
884+
container,
885+
dry_run=dry_run,
886+
client_status_queue=client_status_queue,
887+
identified_resources=identified_resources,
888+
filters=filters,
889+
resource_evaluation_fn=resource_evaluation_fn
890+
)
891+
if not dry_run and need_delete:
892+
containers.append(container)
893+
for container in containers:
894+
try:
895+
self.wait_for_delete_container(container)
896+
except Exception as e:
897+
self.log.warning(
898+
f"Failed to delete container {container.name}: {e}"
899+
)

otcextensions/sdk/obs/v1/container.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Container(_base.BaseResource):
2929
resource_key = 'Bucket'
3030

3131
allow_get = True
32+
allow_fetch = True
3233
allow_head = True
3334
allow_list = True
3435
allow_create = True

otcextensions/sdk/obs/v1/obj.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class Object(_base.BaseResource):
3232

3333
allow_create = True
3434
allow_get = True
35+
allow_fetch = True
3536
allow_commit = True
3637
allow_delete = True
3738
allow_list = True
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
from otcextensions.tests.functional import base
13+
import uuid
14+
15+
16+
class TestObsCleanup(base.BaseFunctionalTest):
17+
uuid_v4 = uuid.uuid4().hex[:8]
18+
bucket_name = 'obs-test-' + uuid_v4
19+
object_name = f'obs{uuid_v4}.object'
20+
folder_name = f'folder{uuid_v4}/'
21+
data = str(uuid.uuid4())
22+
23+
def setUp(self):
24+
super(TestObsCleanup, self).setUp()
25+
self.client = self.conn.obs
26+
self.container = self.client.create_container(
27+
name=self.bucket_name,
28+
storage_acl='public-read-write',
29+
storage_class='STANDARD'
30+
)
31+
self.object = self.client.create_object(
32+
container=self.container,
33+
name=self.object_name,
34+
data=self.data
35+
)
36+
self.folder = self.client.create_object(
37+
container=self.container,
38+
name=self.folder_name
39+
)
40+
self.nested_object_name = f"{self.folder_name}nest_{self.uuid_v4}.txt"
41+
self.nested_object = self.client.create_object(
42+
container=self.container,
43+
name=self.nested_object_name,
44+
data="nested test data"
45+
)
46+
47+
def test_cleanup(self):
48+
containers = list(self.client.containers())
49+
self.assertGreaterEqual(len(containers), 1)
50+
self.client._service_cleanup(dry_run=False, cont_name=self.bucket_name)
51+
containers = list(self.client.containers())
52+
self.assertEqual(len(containers), 0)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
features:
3+
- |
4+
OBS cleanup implemented

0 commit comments

Comments
 (0)