Skip to content

Commit

Permalink
feat: delete google file (#81)
Browse files Browse the repository at this point in the history
* feat: delete google file

* add newlines to ends of files
  • Loading branch information
ZakirG authored Sep 21, 2020
1 parent b8dee25 commit eff3c48
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 19 deletions.
35 changes: 34 additions & 1 deletion cirrus/google_cloud/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from google.oauth2.service_account import Credentials as ServiceAccountCredentials
from googleapiclient.errors import HttpError as GoogleHttpError
from requests.exceptions import HTTPError as requestsHttpError
from urllib.parse import quote as urlquote

from cirrus.config import config
from cirrus.core import CloudManager
Expand Down Expand Up @@ -53,6 +54,7 @@

GOOGLE_IAM_API_URL = "https://iam.googleapis.com/v1/"
GOOGLE_CLOUD_RESOURCE_URL = "https://cloudresourcemanager.googleapis.com/v1/"
GOOGLE_STORAGE_API_URL = "https://storage.googleapis.com/storage/v1/"
GOOGLE_DIRECTORY_API_URL = "https://www.googleapis.com/admin/directory/v1/"
GOOGLE_LOGGING_EMAIL = "[email protected]"

Expand Down Expand Up @@ -661,6 +663,37 @@ def give_group_access_to_bucket(self, group_email, bucket_name, access=None):

bucket.update()

@backoff.on_exception(backoff.expo, Exception, **BACKOFF_SETTINGS)
def delete_data_file(self, bucket_name, object_name):
"""
Delete a file within the provided bucket with the provided file ID.
Args:
bucket_name (str): name of Google Cloud Storage bucket containing file to delete
object_name (str): name of file to delete
Returns:
status (int): the status code of the response returned by the Google Storage API
"""
encoded_object_name = urlquote(object_name, safe="")
api_url = _get_google_api_url(
"b/" + bucket_name + "/o/" + encoded_object_name, GOOGLE_STORAGE_API_URL
)

try:
response = self._authed_request("DELETE", api_url)
except GoogleHttpError as err:
logger.error(err)
raise

print(
"DELETE method to {} returned status {}".format(
api_url, response.status_code
)
)

return response.status_code

@backoff.on_exception(backoff.expo, Exception, **BACKOFF_SETTINGS)
def get_service_account(self, account):
"""
Expand Down Expand Up @@ -1753,7 +1786,7 @@ def _get_project_iam_policy(self, project_id=None):

def _get_google_api_url(relative_path, root_api_url):
"""
Return the url for a Gooel API given the root url, relative path.
Return the url for a Google API given the root url, relative path.
Add the config.GOOGLE_API_KEY from the environment to the request.
Args:
Expand Down
125 changes: 107 additions & 18 deletions test/test_google_cloud_manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,9 +587,10 @@ def test_create_group(test_cloud_manager):
assert group["name"] == new_group_name

# check if new name and email are somewhere in the args to insert
args, kwargs = (
test_cloud_manager._admin_service.groups.return_value.insert.call_args
)
(
args,
kwargs,
) = test_cloud_manager._admin_service.groups.return_value.insert.call_args
assert any(new_group_name in str(arg) for arg in args) or any(
new_group_name in str(kwarg) for kwarg in kwargs.values()
)
Expand Down Expand Up @@ -639,9 +640,10 @@ def test_create_group_already_exists(test_cloud_manager):
assert group["name"] == new_group_name

# check if new name and email are somewhere in the args to insert
args, kwargs = (
test_cloud_manager._admin_service.groups.return_value.insert.call_args
)
(
args,
kwargs,
) = test_cloud_manager._admin_service.groups.return_value.insert.call_args
assert any(new_group_name in str(arg) for arg in args) or any(
new_group_name in str(kwarg) for kwarg in kwargs.values()
)
Expand Down Expand Up @@ -800,9 +802,10 @@ def test_add_member_to_group(test_cloud_manager):
assert group["id"] == new_member_id

# check if ngroup id and email are somewhere in the args to insert
args, kwargs = (
test_cloud_manager._admin_service.members.return_value.insert.call_args
)
(
args,
kwargs,
) = test_cloud_manager._admin_service.members.return_value.insert.call_args
assert any(new_member_email in str(arg) for arg in args) or any(
new_member_email in str(kwarg) for kwarg in kwargs.values()
)
Expand Down Expand Up @@ -831,9 +834,10 @@ def test_remove_member_from_group(test_cloud_manager):
assert not response

# check if group id and email are somewhere in the args to delete
args, kwargs = (
test_cloud_manager._admin_service.members.return_value.delete.call_args
)
(
args,
kwargs,
) = test_cloud_manager._admin_service.members.return_value.delete.call_args
assert any(new_member_email in str(arg) for arg in args) or any(
new_member_email in str(kwarg) for kwarg in kwargs.values()
)
Expand Down Expand Up @@ -1034,9 +1038,10 @@ def test_delete_group(test_cloud_manager, google_return_value):

# Test #
assert response == {}
args, kwargs = (
test_cloud_manager._admin_service.groups.return_value.delete.call_args
)
(
args,
kwargs,
) = test_cloud_manager._admin_service.groups.return_value.delete.call_args
assert any((group_id == arg) for arg in args) or any(
(group_id == kwarg) for kwarg in kwargs.values()
)
Expand All @@ -1060,9 +1065,10 @@ def test_delete_group_doesnt_exist(test_cloud_manager):

# Test #
assert response == {}
args, kwargs = (
test_cloud_manager._admin_service.groups.return_value.delete.call_args
)
(
args,
kwargs,
) = test_cloud_manager._admin_service.groups.return_value.delete.call_args
assert any((group_id == arg) for arg in args) or any(
(group_id == kwarg) for kwarg in kwargs.values()
)
Expand Down Expand Up @@ -1551,5 +1557,88 @@ def test_authorized_session_unhandled_exception_retry(test_cloud_manager):
assert logger_error.call_count >= 1


def test_delete_data_file(test_cloud_manager):
"""
Test that deleting an object actually calls the google API with
given bucket and object name
"""
# Setup #
test_cloud_manager._authed_session.delete.return_value = _fake_response(200)

bucket = "some_bucket"
object_name = "some_object"

# Call #
test_cloud_manager.delete_data_file(bucket, object_name)

# Test #
assert test_cloud_manager._authed_session.delete.called is True

# Naive check to see if the object appears in the call to delete
args, kwargs = test_cloud_manager._authed_session.delete.call_args
assert any(bucket in str(arg) for arg in args) or any(
bucket in str(kwarg) for kwarg in kwargs.values()
)
assert any(object_name in str(arg) for arg in args) or any(
object_name in str(kwarg) for kwarg in kwargs.values()
)


def test_delete_data_file_doesnt_exist(test_cloud_manager):
"""
Test that deleting an object actually calls the google API with
given bucket and object_name and if it DOES NOT exist, this is still successful
"""
# Setup #
test_cloud_manager._authed_session.delete.return_value = _fake_response(404)

bucket = "some_bucket"
object_name = "some_object"

# Call #
test_cloud_manager.delete_data_file(bucket, object_name)

# Test #
assert test_cloud_manager._authed_session.delete.called is True

# Naive check to see if the object appears in the call to delete
args, kwargs = test_cloud_manager._authed_session.delete.call_args
assert any(bucket in str(arg) for arg in args) or any(
bucket in str(kwarg) for kwarg in kwargs.values()
)
assert any(object_name in str(arg) for arg in args) or any(
object_name in str(kwarg) for kwarg in kwargs.values()
)


def test_delete_data_file_error_handling(test_cloud_manager):
"""
Test that errors are thrown by cirrus appropriately if Google returns with
uncaught status code.
"""
# Setup #

bucket = "some_bucket"
object_name = "some_object"

class FakeResponseWithStatusNotStatusCode:
def __init__(self, status_numeral):
self.status = status_numeral
self.reason = "reason goes here"

# Call #
with patch(
"cirrus.google_cloud.manager.GoogleCloudManager._authed_request",
side_effect=HttpError(
resp=FakeResponseWithStatusNotStatusCode(500),
content=bytes("Failed to delete for unknown reason", "utf-8"),
),
):
with pytest.raises(Exception) as execinfo:
test_cloud_manager.delete_data_file(bucket, object_name)

assert str(execinfo.value) == '<HttpError 500 "reason goes here">'


if __name__ == "__main__":
pytest.main(["-x", "-v", "."])

0 comments on commit eff3c48

Please sign in to comment.