s3_bucket: handle not implemented operations (#46746)

Don't fail when policy, requestPayment, tagging or versioning API is not
implemented by the endpoint and if related parameters policy, requester_pays,
tags or versioning are None.
This commit is contained in:
Pilou 2018-11-01 16:52:07 +01:00 committed by Brian Coca
parent 1814af9f34
commit d14f9dc685
2 changed files with 107 additions and 79 deletions

View file

@ -0,0 +1,5 @@
---
minor_changes:
- s3_bucket - avoid failure when ``policy``, ``requestPayment``, ``tags`` or
``versioning`` operations aren't supported by the endpoint and related
parameters aren't set

View file

@ -72,6 +72,11 @@ options:
extends_documentation_fragment:
- aws
- ec2
notes:
- If C(requestPayment), C(policy), C(tagging) or C(versioning)
operations/API aren't implemented by the endpoint, module doesn't fail
if related parameters I(requester_pays), I(policy), I(tags) or
I(versioning) are C(None).
'''
EXAMPLES = '''
@ -130,6 +135,7 @@ def create_or_update_bucket(s3_client, module, location):
tags = module.params.get("tags")
versioning = module.params.get("versioning")
changed = False
result = {}
try:
bucket_is_present = bucket_exists(s3_client, name)
@ -151,104 +157,121 @@ def create_or_update_bucket(s3_client, module, location):
# Versioning
try:
versioning_status = get_bucket_versioning(s3_client, name)
except (ClientError, BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to get bucket versioning")
except BotoCoreError as exp:
module.fail_json_aws(exp, msg="Failed to get bucket versioning")
except ClientError as exp:
if exp.response['Error']['Code'] != 'NotImplemented' or versioning is not None:
module.fail_json_aws(exp, msg="Failed to get bucket versioning")
else:
if versioning is not None:
required_versioning = None
if versioning and versioning_status.get('Status') != "Enabled":
required_versioning = 'Enabled'
elif not versioning and versioning_status.get('Status') == "Enabled":
required_versioning = 'Suspended'
if versioning is not None:
required_versioning = None
if versioning and versioning_status.get('Status') != "Enabled":
required_versioning = 'Enabled'
elif not versioning and versioning_status.get('Status') == "Enabled":
required_versioning = 'Suspended'
if required_versioning:
try:
put_bucket_versioning(s3_client, name, required_versioning)
changed = True
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to update bucket versioning")
if required_versioning:
try:
put_bucket_versioning(s3_client, name, required_versioning)
changed = True
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to update bucket versioning")
versioning_status = wait_versioning_is_applied(module, s3_client, name, required_versioning)
versioning_status = wait_versioning_is_applied(module, s3_client, name, required_versioning)
# This output format is there to ensure compatibility with previous versions of the module
versioning_return_value = {
'Versioning': versioning_status.get('Status', 'Disabled'),
'MfaDelete': versioning_status.get('MFADelete', 'Disabled'),
}
# This output format is there to ensure compatibility with previous versions of the module
result['versioning'] = {
'Versioning': versioning_status.get('Status', 'Disabled'),
'MfaDelete': versioning_status.get('MFADelete', 'Disabled'),
}
# Requester pays
try:
requester_pays_status = get_bucket_request_payment(s3_client, name)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to get bucket request payment")
except BotoCoreError as exp:
module.fail_json_aws(exp, msg="Failed to get bucket request payment")
except ClientError as exp:
if exp.response['Error']['Code'] != 'NotImplemented' or requester_pays is not None:
module.fail_json_aws(exp, msg="Failed to get bucket request payment")
else:
if requester_pays is not None:
payer = 'Requester' if requester_pays else 'BucketOwner'
if requester_pays_status != payer:
put_bucket_request_payment(s3_client, name, payer)
requester_pays_status = wait_payer_is_applied(module, s3_client, name, payer, should_fail=False)
if requester_pays_status is None:
# We have seen that it happens quite a lot of times that the put request was not taken into
# account, so we retry one more time
put_bucket_request_payment(s3_client, name, payer)
requester_pays_status = wait_payer_is_applied(module, s3_client, name, payer, should_fail=True)
changed = True
payer = 'Requester' if requester_pays else 'BucketOwner'
if requester_pays_status != payer:
put_bucket_request_payment(s3_client, name, payer)
requester_pays_status = wait_payer_is_applied(module, s3_client, name, payer, should_fail=False)
if requester_pays_status is None:
# We have seen that it happens quite a lot of times that the put request was not taken into
# account, so we retry one more time
put_bucket_request_payment(s3_client, name, payer)
requester_pays_status = wait_payer_is_applied(module, s3_client, name, payer, should_fail=True)
changed = True
result['requester_pays'] = requester_pays
# Policy
try:
current_policy = get_bucket_policy(s3_client, name)
except (ClientError, BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to get bucket policy")
except BotoCoreError as exp:
module.fail_json_aws(exp, msg="Failed to get bucket policy")
except ClientError as exp:
if exp.response['Error']['Code'] != 'NotImplemented' or policy is not None:
module.fail_json_aws(exp, msg="Failed to get bucket policy")
else:
if policy is not None:
if isinstance(policy, string_types):
policy = json.loads(policy)
if policy is not None:
if isinstance(policy, string_types):
policy = json.loads(policy)
if not policy and current_policy:
try:
delete_bucket_policy(s3_client, name)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to delete bucket policy")
current_policy = wait_policy_is_applied(module, s3_client, name, policy)
changed = True
elif compare_policies(current_policy, policy):
try:
put_bucket_policy(s3_client, name, policy)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to update bucket policy")
current_policy = wait_policy_is_applied(module, s3_client, name, policy, should_fail=False)
if current_policy is None:
# As for request payement, it happens quite a lot of times that the put request was not taken into
# account, so we retry one more time
put_bucket_policy(s3_client, name, policy)
current_policy = wait_policy_is_applied(module, s3_client, name, policy, should_fail=True)
changed = True
if not policy and current_policy:
try:
delete_bucket_policy(s3_client, name)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to delete bucket policy")
current_policy = wait_policy_is_applied(module, s3_client, name, policy)
changed = True
elif compare_policies(current_policy, policy):
try:
put_bucket_policy(s3_client, name, policy)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to update bucket policy")
current_policy = wait_policy_is_applied(module, s3_client, name, policy, should_fail=False)
if current_policy is None:
# As for request payement, it happens quite a lot of times that the put request was not taken into
# account, so we retry one more time
put_bucket_policy(s3_client, name, policy)
current_policy = wait_policy_is_applied(module, s3_client, name, policy, should_fail=True)
changed = True
result['policy'] = current_policy
# Tags
try:
current_tags_dict = get_current_bucket_tags_dict(s3_client, name)
except (ClientError, BotoCoreError) as e:
module.fail_json_aws(e, msg="Failed to get bucket tags")
except BotoCoreError as exp:
module.fail_json_aws(exp, msg="Failed to get bucket tags")
except ClientError as exp:
if exp.response['Error']['Code'] != 'NotImplemented' or tags is not None:
module.fail_json_aws(exp, msg="Failed to get bucket tags")
else:
if tags is not None:
# Tags are always returned as text
tags = dict((to_text(k), to_text(v)) for k, v in tags.items())
if current_tags_dict != tags:
if tags:
try:
put_bucket_tagging(s3_client, name, tags)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to update bucket tags")
else:
try:
delete_bucket_tagging(s3_client, name)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to delete bucket tags")
current_tags_dict = wait_tags_are_applied(module, s3_client, name, tags)
changed = True
if tags is not None:
# Tags are always returned as text
tags = dict((to_text(k), to_text(v)) for k, v in tags.items())
if current_tags_dict != tags:
if tags:
try:
put_bucket_tagging(s3_client, name, tags)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to update bucket tags")
else:
try:
delete_bucket_tagging(s3_client, name)
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Failed to delete bucket tags")
wait_tags_are_applied(module, s3_client, name, tags)
current_tags_dict = tags
changed = True
result['tags'] = current_tags_dict
module.exit_json(changed=changed, name=name, versioning=versioning_return_value,
requester_pays=requester_pays, policy=current_policy, tags=current_tags_dict)
module.exit_json(changed=changed, name=name, **result)
def bucket_exists(s3_client, bucket_name):
@ -399,7 +422,7 @@ def wait_tags_are_applied(module, s3_client, bucket_name, expected_tags_dict):
if current_tags_dict != expected_tags_dict:
time.sleep(5)
else:
return
return current_tags_dict
module.fail_json(msg="Bucket tags failed to apply in the expected time")