Skip to content

Commit

Permalink
use AWSRetry instead of manual waiter
Browse files Browse the repository at this point in the history
  • Loading branch information
jatorcasso committed Apr 13, 2022
1 parent df717e1 commit 3de86fa
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
minor_changes:
- iam_user - wrap login profile action API calls with AWSRetry (when wait is True), clean up return struct (https://github.com/ansible-collections/community.aws/pull/1059).
bugfixes:
- iam_user_info - gracefully handle when no users are found (https://github.com/ansible-collections/community.aws/pull/1059).
4 changes: 0 additions & 4 deletions changelogs/fragments/1059-iam_user-add_waiters_stabilize.yml

This file was deleted.

99 changes: 35 additions & 64 deletions plugins/modules/iam_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,9 @@
from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict
from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags
from time import sleep
from time import time
from random import randint


def compare_attached_policies(current_attached_policies, new_attached_policies):
Expand Down Expand Up @@ -254,33 +252,6 @@ def wait_iam_exists(connection, module):
module.fail_json_aws(e, msg='Failed while waiting on IAM user creation')


def wait_iam_login_profile_updated(connection, module, user_params):
'''
Wait until IAM login profile is updated prior to returning.
Parameters:
connection: IAM client
module: AWSModule
user_params (dict): Parameters to feed into update_login_profile
'''
max_backoff = 64
timeout = module.params.get('wait_timeout')
failure_counter = 0
start_time = time()

while True:
if time() - start_time >= timeout:
module.fail_json(msg='Error waiting for login profile to be updated - please check the AWS console.')
try:
connection.update_login_profile(**user_params)
break
except is_boto3_error_code('EntityTemporarilyUnmodifiable'):
sleep_time = min(2 ** failure_counter + randint(1, 1000) / 1000, max_backoff)
sleep(sleep_time)
failure_counter += 1
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg='Failure while waiting for login profile to be updated.')


def create_or_update_login_profile(connection, module, wait):

# Apply new password / update password for the user
Expand All @@ -292,14 +263,20 @@ def create_or_update_login_profile(connection, module, wait):

try:
retval = connection.update_login_profile(**user_params)
if bool(wait):
wait_iam_login_profile_updated(connection, module, user_params)
if wait:
AWSRetry.jittered_backoff(
delay=1,
catch_extra_error_codes=['EntityTemporarilyUnmodifiable']
)(connection.update_login_profile)(**user_params)
except is_boto3_error_code('NoSuchEntity'):
# Login profile does not yet exist - create it
try:
retval = connection.create_login_profile(**user_params)
if bool(wait):
wait_iam_login_profile_updated(connection, module, user_params)
if wait:
AWSRetry.jittered_backoff(
delay=1,
catch_extra_error_codes=['EntityTemporarilyUnmodifiable']
)(connection.update_login_profile)(**user_params)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Unable to create user login profile")
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
Expand All @@ -309,33 +286,30 @@ def create_or_update_login_profile(connection, module, wait):


def delete_login_profile(connection, module, user_name, wait):

try:
connection.delete_login_profile(UserName=user_name)
except is_boto3_error_code('NoSuchEntity'):
'''
Deletes a users login profile.
Parameters:
connection: IAM client
module: AWSModule
user_name (str): Username of user
wait (bool): Whether or not the module waits for full deletion to reflect on AWS
Returns:
(bool): True if login profile deleted, False if no login profile found to delete
'''
# User does not have login profile - nothing to delete
if not user_has_login_policy(connection, module, user_name):
return False
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg="Unable to delete user login profile")

if bool(wait):
max_backoff = 64
timeout = module.params.get('wait_timeout')
failure_counter = 0
start_time = time()

while True:
if time() - start_time >= timeout:
module.fail_json(msg='Error waiting for login profile to be deleted - please check the AWS console.')
try:
connection.get_login_profile(UserName=user_name)
except is_boto3_error_code('EntityTemporarilyUnmodifiable'):
sleep_time = min(2 ** failure_counter + randint(1, 1000) / 1000, max_backoff)
sleep(sleep_time)
failure_counter += 1
except is_boto3_error_code('NoSuchEntity'): # pylint: disable=duplicate-except
break
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg='Failure while waiting for login profile to be deleted.')

if not module.check_mode:
try:
if wait:
AWSRetry.jittered_backoff(catch_extra_error_codes=['EntityTemporarilyUnmodifiable'])(connection.delete_login_profile)(UserName=user_name)
else:
connection.delete_login_profile(UserName=user_name)
except is_boto3_error_code('NoSuchEntity'):
pass
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg="Unable to delete user login profile")

return True

Expand Down Expand Up @@ -395,10 +369,7 @@ def create_or_update_user(connection, module):
new_login_profile = True

elif module.params.get('remove_password'):
if user_has_login_policy(connection, module, params['UserName']):
if module.check_mode:
module.exit_json(changed=True)
login_profile_result = delete_login_profile(connection, module, params['UserName'], wait)
login_profile_result = delete_login_profile(connection, module, params['UserName'], wait)

changed = bool(update_result) or bool(login_profile_result)

Expand Down

0 comments on commit 3de86fa

Please sign in to comment.