Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: add retry conformance test cases #580

Merged
merged 8 commits into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions tests/conformance/retry_strategy_test_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
"cases": [
{
"instructions": ["return-503", "return-503"]
},
{
"instructions": ["return-reset-connection", "return-reset-connection"]
},
{
"instructions": ["return-reset-connection", "return-503"]
}
],
"methods": [
Expand Down Expand Up @@ -41,6 +47,12 @@
"cases": [
{
"instructions": ["return-503", "return-503"]
},
{
"instructions": ["return-reset-connection", "return-reset-connection"]
},
{
"instructions": ["return-reset-connection", "return-503"]
}
],
"methods": [
Expand All @@ -65,6 +77,9 @@
"cases": [
{
"instructions": ["return-503"]
},
{
"instructions": ["return-reset-connection"]
}
],
"methods": [
Expand All @@ -85,13 +100,31 @@
},
{
"id": 4,
"description": "non idempotent",
"description": "non_idempotent",
"cases": [
{
"instructions": []
"instructions": ["return-503"]
tritone marked this conversation as resolved.
Show resolved Hide resolved
},
{
"instructions": ["return-reset-connection"]
}
],
"methods": [],
"methods": [
{"name": "storage.bucket_acl.delete", "resources": ["BUCKET"]},
{"name": "storage.bucket_acl.insert", "resources": ["BUCKET"]},
{"name": "storage.bucket_acl.patch", "resources": ["BUCKET"]},
{"name": "storage.bucket_acl.update", "resources": ["BUCKET"]},
{"name": "storage.default_object_acl.delete", "resources": ["BUCKET"]},
{"name": "storage.default_object_acl.insert", "resources": ["BUCKET"]},
{"name": "storage.default_object_acl.patch", "resources": ["BUCKET"]},
{"name": "storage.default_object_acl.update", "resources": ["BUCKET"]},
{"name": "storage.hmacKey.create", "resources": []},
{"name": "storage.notifications.insert", "resources": ["BUCKET"]},
{"name": "storage.object_acl.delete", "resources": ["BUCKET", "OBJECT"]},
{"name": "storage.object_acl.insert", "resources": ["BUCKET", "OBJECT"]},
{"name": "storage.object_acl.patch", "resources": ["BUCKET", "OBJECT"]},
{"name": "storage.object_acl.update", "resources": ["BUCKET", "OBJECT"]}
],
"preconditionProvided": false,
"expectSuccess": false
}
Expand Down
222 changes: 210 additions & 12 deletions tests/conformance/test_conformance.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

from google.cloud import storage
from google.auth.credentials import AnonymousCredentials
from google.cloud.storage.hmac_key import HMACKeyMetadata

from . import _read_local_json

Expand All @@ -42,6 +43,9 @@

_STRING_CONTENT = "hello world"
_BYTE_CONTENT = b"12345678"
_BUCKET_ACL_PATCH_MSG = "BucketACL patch operations call storage.buckets.patch, but are never idempotent; Preconditions are irrelevant."
_DEFAULT_OBJECT_ACL_PATCH_MSG = "DefaultObjectACL patch operations call storage.buckets.patch, but are never idempotent; Preconditions are irrelevant."
_OBJECT_ACL_PATCH_MSG = "ObjectACL patch operations call storage.objects.patch, but are never idempotent; Preconditions are irrelevant."


########################################################################################################################################
Expand Down Expand Up @@ -171,6 +175,10 @@ def bucket_lock_retention_policy(client, _preconditions, **resources):
bucket.lock_retention_policy()


def client_get_service_account_email(client, _preconditions, **_):
client.get_service_account_email()


def notification_create(client, _preconditions, **resources):
bucket = client.get_bucket(resources.get("bucket").name)
notification = bucket.notification()
Expand Down Expand Up @@ -217,8 +225,44 @@ def client_list_hmac_keys(client, _preconditions, **_):
pass


def client_get_service_account_email(client, _preconditions, **_):
client.get_service_account_email()
def client_get_hmac_key_metadata(client, _preconditions, **resources):
access_id = resources.get("hmac_key").access_id
client.get_hmac_key_metadata(access_id=access_id)


def hmac_key_exists(client, _preconditions, **resources):
access_id = resources.get("hmac_key").access_id
hmac_key = HMACKeyMetadata(client, access_id=access_id)
hmac_key.exists()


def hmac_key_reload(client, _preconditions, **resources):
access_id = resources.get("hmac_key").access_id
hmac_key = HMACKeyMetadata(client, access_id=access_id)
hmac_key.reload()
cojenco marked this conversation as resolved.
Show resolved Hide resolved


def hmac_key_delete(client, _preconditions, **resources):
access_id = resources.get("hmac_key").access_id
hmac_key = HMACKeyMetadata(client, access_id=access_id)
hmac_key.state = "INACTIVE"
hmac_key.update()
hmac_key.delete()


def client_create_hmac_key(client, _preconditions, **_):
client.create_hmac_key(service_account_email=_CONF_TEST_SERVICE_ACCOUNT_EMAIL)


def hmac_key_update(client, _preconditions, **resources):
access_id = resources.get("hmac_key").access_id
etag = resources.get("hmac_key").etag
hmac_key = HMACKeyMetadata(client, access_id=access_id)
if _preconditions:
pytest.skip("Etag is not yet supported")
hmac_key.etag = etag
hmac_key.state = "INACTIVE"
hmac_key.update()


def bucket_patch(client, _preconditions, **resources):
Expand Down Expand Up @@ -423,6 +467,131 @@ def blob_create_resumable_upload_session(client, _preconditions, **resources):
blob.create_resumable_upload_session()


def blob_make_private(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_OBJECT_ACL_PATCH_MSG)
bucket = resources.get("bucket")
object = resources.get("object")
blob = client.bucket(bucket.name).blob(object.name)
blob.make_private()


def blob_make_public(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_OBJECT_ACL_PATCH_MSG)
bucket = resources.get("bucket")
object = resources.get("object")
blob = client.bucket(bucket.name).blob(object.name)
blob.make_public()


def bucket_make_private(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_BUCKET_ACL_PATCH_MSG)
bucket = client.bucket(resources.get("bucket").name)
bucket.make_private()


def bucket_make_public(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_BUCKET_ACL_PATCH_MSG)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be never-idempotent so this message should never come up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline, since this uses bucket.patch under the hood it could be idempotent if metageneration is specified. @cojenco to file separate issue for adding precondition support.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed #582 for adding preconditions and retry config support.

bucket = client.bucket(resources.get("bucket").name)
bucket.make_public()


def bucket_acl_reload(client, _preconditions, **resources):
bucket = client.bucket(resources.get("bucket").name)
bucket.acl.reload()


def bucket_acl_save(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_BUCKET_ACL_PATCH_MSG)
bucket = client.bucket(resources.get("bucket").name)
bucket.acl.reload()
bucket.acl.user(_CONF_TEST_SERVICE_ACCOUNT_EMAIL).grant_owner()
bucket.acl.save()


def bucket_acl_save_predefined(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_BUCKET_ACL_PATCH_MSG)
bucket = client.bucket(resources.get("bucket").name)
bucket.acl.save_predefined("bucketOwnerFullControl")


def bucket_acl_clear(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_BUCKET_ACL_PATCH_MSG)
bucket = client.bucket(resources.get("bucket").name)
bucket.acl.clear()


def default_object_acl_reload(client, _preconditions, **resources):
bucket = client.bucket(resources.get("bucket").name)
print(bucket.default_object_acl)
bucket.default_object_acl.reload()


def default_object_acl_save(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_DEFAULT_OBJECT_ACL_PATCH_MSG)
bucket = client.bucket(resources.get("bucket").name)
bucket.default_object_acl.reload()
bucket.default_object_acl.user(_CONF_TEST_SERVICE_ACCOUNT_EMAIL).grant_owner()
bucket.default_object_acl.save()


def default_object_acl_save_predefined(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_DEFAULT_OBJECT_ACL_PATCH_MSG)
bucket = client.bucket(resources.get("bucket").name)
bucket.default_object_acl.save_predefined("bucketOwnerFullControl")


def default_object_acl_clear(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_DEFAULT_OBJECT_ACL_PATCH_MSG)
bucket = client.bucket(resources.get("bucket").name)
bucket.default_object_acl.clear()


def object_acl_reload(client, _preconditions, **resources):
bucket = resources.get("bucket")
object = resources.get("object")
blob = client.bucket(bucket.name).blob(object.name)
blob.acl.reload()


def object_acl_save(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_OBJECT_ACL_PATCH_MSG)
bucket = resources.get("bucket")
object = resources.get("object")
blob = client.bucket(bucket.name).blob(object.name)
blob.acl.reload()
blob.acl.user(_CONF_TEST_SERVICE_ACCOUNT_EMAIL).grant_owner()
blob.acl.save()


def object_acl_save_predefined(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_OBJECT_ACL_PATCH_MSG)
bucket = resources.get("bucket")
object = resources.get("object")
blob = client.bucket(bucket.name).blob(object.name)
blob.acl.save_predefined("bucketOwnerFullControl")


def object_acl_clear(client, _preconditions, **resources):
if _preconditions:
pytest.skip(_OBJECT_ACL_PATCH_MSG)
bucket = resources.get("bucket")
object = resources.get("object")
blob = client.bucket(bucket.name).blob(object.name)
blob.acl.clear()


########################################################################################################################################
### Method Invocation Mapping ##########################################################################################################
########################################################################################################################################
Expand All @@ -434,7 +603,8 @@ def blob_create_resumable_upload_session(client, _preconditions, **resources):
# read or just a metadata get).

method_mapping = {
"storage.buckets.delete": [bucket_delete], # S1 start
"storage.bucket_acl.list": [bucket_acl_reload], # S1 start
"storage.buckets.delete": [bucket_delete],
"storage.buckets.get": [
client_get_bucket,
bucket_reload,
Expand All @@ -446,13 +616,22 @@ def blob_create_resumable_upload_session(client, _preconditions, **resources):
"storage.buckets.list": [client_list_buckets],
"storage.buckets.lockRetentionPolicy": [bucket_lock_retention_policy],
"storage.buckets.testIamPermissions": [bucket_test_iam_permissions],
"storage.default_object_acl.list": [default_object_acl_reload],
"storage.hmacKey.delete": [hmac_key_delete],
"storage.hmacKey.get": [
client_get_hmac_key_metadata,
hmac_key_exists,
hmac_key_reload,
],
"storage.hmacKey.list": [client_list_hmac_keys],
"storage.notifications.delete": [notification_delete],
"storage.notifications.get": [
bucket_get_notification,
notification_exists,
notification_reload,
],
"storage.notifications.list": [bucket_list_notifications],
"storage.object_acl.list": [object_acl_reload],
"storage.objects.get": [
bucket_get_blob,
blob_exists,
Expand All @@ -462,14 +641,22 @@ def blob_create_resumable_upload_session(client, _preconditions, **resources):
blob_download_as_text,
blobreader_read,
],
"storage.objects.list": [
client_list_blobs,
bucket_list_blobs,
bucket_delete,
], # S1 end
"storage.buckets.patch": [bucket_patch], # S2/S3 start
"storage.objects.list": [client_list_blobs, bucket_list_blobs, bucket_delete],
"storage.serviceaccount.get": [client_get_service_account_email], # S1 end
"storage.buckets.patch": [
bucket_patch,
bucket_make_public,
bucket_make_private,
bucket_acl_save,
tritone marked this conversation as resolved.
Show resolved Hide resolved
bucket_acl_save_predefined,
bucket_acl_clear,
default_object_acl_save,
default_object_acl_save_predefined,
default_object_acl_clear,
], # S2/S3 start
"storage.buckets.setIamPolicy": [bucket_set_iam_policy],
"storage.buckets.update": [bucket_update],
"storage.hmacKey.update": [hmac_key_update],
"storage.objects.compose": [blob_compose],
"storage.objects.copy": [bucket_copy_blob, bucket_rename_blob],
"storage.objects.delete": [
Expand All @@ -485,9 +672,18 @@ def blob_create_resumable_upload_session(client, _preconditions, **resources):
blobwriter_write,
blob_create_resumable_upload_session,
],
"storage.objects.patch": [blob_patch],
"storage.objects.patch": [
blob_patch,
object_acl_save,
tritone marked this conversation as resolved.
Show resolved Hide resolved
object_acl_save_predefined,
object_acl_clear,
blob_make_private,
blob_make_public,
],
"storage.objects.rewrite": [blob_rewrite, blob_update_storage_class],
"storage.objects.update": [blob_update], # S2/S3 end
"storage.hmacKey.create": [client_create_hmac_key], # S4 start
"storage.notifications.insert": [notification_create],
}


Expand Down Expand Up @@ -715,15 +911,17 @@ def run_test_case(
id = scenario["id"]
methods = scenario["methods"]
cases = scenario["cases"]
for c in cases:
for i, c in enumerate(cases):
for m in methods:
method_name = m["name"]
if method_name not in method_mapping:
logging.info("No tests for operation {}".format(method_name))
continue

for lib_func in method_mapping[method_name]:
test_name = "test-S{}-{}-{}".format(id, method_name, lib_func.__name__)
test_name = "test-S{}-{}-{}-{}".format(
id, method_name, lib_func.__name__, i
)
globals()[test_name] = functools.partial(
run_test_case, id, m, c, lib_func, host
)