You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by ra...@apache.org on 2017/02/16 14:40:09 UTC

incubator-ariatosca git commit: ARIA-108 Add API for deleting a resource for resource storage

Repository: incubator-ariatosca
Updated Branches:
  refs/heads/ARIA-108-delete-resource-api [created] 390ffcb21


ARIA-108 Add API for deleting a resource for resource storage


Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/390ffcb2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/390ffcb2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/390ffcb2

Branch: refs/heads/ARIA-108-delete-resource-api
Commit: 390ffcb2132b4f533cdc2c84d45f36a464f9063d
Parents: b619335
Author: Ran Ziv <ra...@gigaspaces.com>
Authored: Thu Feb 16 16:38:51 2017 +0200
Committer: Ran Ziv <ra...@gigaspaces.com>
Committed: Thu Feb 16 16:38:51 2017 +0200

----------------------------------------------------------------------
 aria/storage/api.py                    | 15 ++++-
 aria/storage/filesystem_rapi.py        | 38 ++++++++-----
 tests/storage/test_resource_storage.py | 86 ++++++++++++++++++++++++++++-
 3 files changed, 122 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/390ffcb2/aria/storage/api.py
----------------------------------------------------------------------
diff --git a/aria/storage/api.py b/aria/storage/api.py
index 09a4dd9..ed8a2ff 100644
--- a/aria/storage/api.py
+++ b/aria/storage/api.py
@@ -135,7 +135,7 @@ class ResourceAPI(StorageAPI):
         """
         return self._name
 
-    def read(self, entry_id, path=None, **kwargs):
+    def read(self, entry_id, path, **kwargs):
         """
         Get a bytesteam from the storage.
 
@@ -144,7 +144,18 @@ class ResourceAPI(StorageAPI):
         :param kwargs:
         :return:
         """
-        raise NotImplementedError('Subclass must implement abstract data method')
+        raise NotImplementedError('Subclass must implement abstract read method')
+
+    def delete(self, entry_id, path, **kwargs):
+        """
+        Delete a resource from the storage.
+
+        :param entry_id:
+        :param path:
+        :param kwargs:
+        :return:
+        """
+        raise NotImplementedError('Subclass must implement abstract delete method')
 
     def download(self, entry_id, destination, path=None, **kwargs):
         """

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/390ffcb2/aria/storage/filesystem_rapi.py
----------------------------------------------------------------------
diff --git a/aria/storage/filesystem_rapi.py b/aria/storage/filesystem_rapi.py
index 6693dbd..f8973ce 100644
--- a/aria/storage/filesystem_rapi.py
+++ b/aria/storage/filesystem_rapi.py
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 """
-SQLalchemy based RAPI
+File system based RAPI
 """
 import os
 import shutil
@@ -92,14 +92,13 @@ class FileSystemResourceAPI(api.ResourceAPI):
         except (OSError, IOError):
             pass
 
-    def read(self, entry_id, path=None, **_):
+    def read(self, entry_id, path, **_):
         """
         Retrieve the content of a file system storage resource.
 
-        :param str entry_type: the type of the entry.
         :param str entry_id: the id of the entry.
-        :param str path: a path to a specific resource.
-        :return: the content of the file
+        :param str path: a path to the specific resource to read.
+        :return: the content of the file.
         :rtype: bytes
         """
         resource_relative_path = os.path.join(self.name, entry_id, path or '')
@@ -110,7 +109,8 @@ class FileSystemResourceAPI(api.ResourceAPI):
         if not os.path.isfile(resource):
             resources = os.listdir(resource)
             if len(resources) != 1:
-                raise exceptions.StorageError('No resource in path: {0}'.format(resource))
+                raise exceptions.StorageError('Failed to read {0}; Reading a directory is '
+                    'only allowed when it contains a single resource'.format(resource))
             resource = os.path.join(resource, resources[0])
         with open(resource, 'rb') as resource_file:
             return resource_file.read()
@@ -119,10 +119,9 @@ class FileSystemResourceAPI(api.ResourceAPI):
         """
         Download a specific file or dir from the file system resource storage.
 
-        :param str entry_type: the name of the entry.
-        :param str entry_id: the id of the entry
-        :param str destination: the destination of the files.
-        :param str path: a path on the remote machine relative to the root of the entry.
+        :param str entry_id: the id of the entry.
+        :param str destination: the destination to download to
+        :param str path: the path to download relative to the root of the entry (otherwise all).
         """
         resource_relative_path = os.path.join(self.name, entry_id, path or '')
         resource = os.path.join(self.directory, resource_relative_path)
@@ -138,9 +137,8 @@ class FileSystemResourceAPI(api.ResourceAPI):
         """
         Uploads a specific file or dir to the file system resource storage.
 
-        :param str entry_type: the name of the entry.
-        :param str entry_id: the id of the entry
-        :param source: the source of  the files to upload.
+        :param str entry_id: the id of the entry.
+        :param source: the source of the files to upload.
         :param path: the destination of the file/s relative to the entry root dir.
         """
         resource_directory = os.path.join(self.directory, self.name, entry_id)
@@ -151,3 +149,17 @@ class FileSystemResourceAPI(api.ResourceAPI):
             shutil.copy2(source, destination)
         else:
             dir_util.copy_tree(source, destination)                                       # pylint: disable=no-member
+
+    def delete(self, entry_id, path=None, **_):
+        """
+        Deletes a file system storage resource.
+
+        :param str entry_id: the id of the entry.
+        :param str path: a path to delete relative to the root of the entry (otherwise all).
+        """
+        destination = os.path.join(self.directory, self.name, entry_id, path or '')
+        if os.path.exists(destination):
+            if os.path.isfile(destination):
+                os.remove(destination)
+            else:
+                shutil.rmtree(destination)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/390ffcb2/tests/storage/test_resource_storage.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_resource_storage.py b/tests/storage/test_resource_storage.py
index 9b5f782..5ad414f 100644
--- a/tests/storage/test_resource_storage.py
+++ b/tests/storage/test_resource_storage.py
@@ -111,7 +111,7 @@ class TestResourceStorage(TestFileSystem):
         tmpfile_path = tempfile.mkstemp(suffix=self.__class__.__name__, dir=self.path)[1]
         self._upload(storage, tmpfile_path, 'blueprint_id')
 
-        assert storage.blueprint.read(entry_id='blueprint_id') == 'fake context'
+        assert storage.blueprint.read(entry_id='blueprint_id', path=os.path.basename(tmpfile_path)) == 'fake context'
 
     def test_upload_dir(self):
         storage = self._create_storage()
@@ -188,4 +188,86 @@ class TestResourceStorage(TestFileSystem):
         storage.blueprint.upload(entry_id='blueprint_id', source=tmp_dir)
 
         with pytest.raises(exceptions.StorageError):
-            storage.blueprint.read(entry_id='blueprint_id')
+            storage.blueprint.read(entry_id='blueprint_id', path='')
+
+    def test_delete_resource(self):
+        storage = self._create_storage()
+        self._create(storage)
+        tmpfile_path = tempfile.mkstemp(suffix=self.__class__.__name__, dir=self.path)[1]
+        self._upload(storage, tmpfile_path, 'blueprint_id')
+        tmpfile2_path = tempfile.mkstemp(suffix=self.__class__.__name__, dir=self.path)[1]
+        self._upload(storage, tmpfile2_path, 'blueprint_id')
+
+        # deleting the first resource and expecting an error on read
+        storage.blueprint.delete(entry_id='blueprint_id', path=os.path.basename(tmpfile_path))
+        with pytest.raises(exceptions.StorageError):
+            storage.blueprint.read(entry_id='blueprint_id', path=os.path.basename(tmpfile_path))
+        # the second resource should still be available for reading
+        assert storage.blueprint.read(entry_id='blueprint_id', path=os.path.basename(tmpfile2_path)) == 'fake context'
+
+    def test_delete_directory(self):
+        storage = self._create_storage()
+        self._create(storage)
+        temp_destination_dir = tempfile.mkdtemp(dir=self.path)
+
+        tmp_dir = tempfile.mkdtemp(suffix=self.__class__.__name__, dir=self.path)
+        second_level_tmp_dir = tempfile.mkdtemp(dir=tmp_dir)
+        tmp_filename = tempfile.mkstemp(dir=second_level_tmp_dir)[1]
+        self._upload_dir(storage, tmp_dir, tmp_filename, id='blueprint_id')
+        file_path_in_dir = os.path.join(
+            os.path.basename(second_level_tmp_dir),
+            os.path.basename(tmp_filename))
+
+        # should be able to read the file and download the directory..
+        assert storage.blueprint.read(
+            entry_id='blueprint_id',
+            path=file_path_in_dir) == 'fake context'
+        storage.blueprint.download(
+            entry_id='blueprint_id',
+            path=os.path.basename(second_level_tmp_dir),
+            destination=temp_destination_dir)        
+        
+        # after deletion, the file and directory should both be gone
+        storage.blueprint.delete(
+            entry_id='blueprint_id',
+            path=os.path.basename(second_level_tmp_dir))
+        with pytest.raises(exceptions.StorageError):
+            assert storage.blueprint.read(
+                entry_id='blueprint_id',
+                path=file_path_in_dir) == 'fake context'
+        with pytest.raises(exceptions.StorageError):
+            storage.blueprint.download(
+                entry_id='blueprint_id', 
+                path=os.path.basename(second_level_tmp_dir),
+                destination=temp_destination_dir)        
+
+    def test_delete_all_resources(self):
+        storage = self._create_storage()
+        self._create(storage)
+        temp_destination_dir = tempfile.mkdtemp(dir=self.path)
+
+        tmp_dir = tempfile.mkdtemp(suffix=self.__class__.__name__, dir=self.path)
+        second_level_tmp_dir = tempfile.mkdtemp(dir=tmp_dir)
+        tmp_filename = tempfile.mkstemp(dir=second_level_tmp_dir)[1]
+        self._upload_dir(storage, tmp_dir, tmp_filename, id='blueprint_id')
+        file_path_in_dir = os.path.join(
+            os.path.basename(second_level_tmp_dir),
+            os.path.basename(tmp_filename))
+        
+        # deleting without specifying a path - delete all resources of this entry
+        storage.blueprint.delete(entry_id='blueprint_id')
+        with pytest.raises(exceptions.StorageError):
+            assert storage.blueprint.read(
+                entry_id='blueprint_id',
+                path=file_path_in_dir) == 'fake context'
+        with pytest.raises(exceptions.StorageError):
+            storage.blueprint.download(
+                entry_id='blueprint_id', 
+                path=os.path.basename(second_level_tmp_dir),
+                destination=temp_destination_dir)      
+
+    def test_delete_nonexisting_resource(self):
+        storage = self._create_storage()
+        self._create(storage)
+        # deleting a nonexisting resource - no effect is expected to happen
+        storage.blueprint.delete(entry_id='blueprint_id', path='fake-file')