Skip to content

Commit

Permalink
cloudwatchlogs_log_group - Tagging support (#1233)
Browse files Browse the repository at this point in the history
cloudwatchlogs_log_group - Tagging support

SUMMARY

Ensure cloudwatchlogs_log_group returns values defined in RETURN docs
Add support for updating tags (including purge_tags)
split cloudwatchlogs_log_group tests
Add some basic integration tests for cloudwatchlogs_log_group_info

ISSUE TYPE

Bugfix Pull Request
Feature Pull Request

COMPONENT NAME
cloudwatchlogs_log_group
cloudwatchlogs_log_group_info
ADDITIONAL INFORMATION

Reviewed-by: Joseph Torcasso <None>
Reviewed-by: Mark Chappell <None>
  • Loading branch information
tremble authored Jun 11, 2022
1 parent 7ca53c7 commit 3f8d2fc
Show file tree
Hide file tree
Showing 9 changed files with 554 additions and 102 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
minor_changes:
- cloudwatchlogs_log_group - now consistently returns the values as defined in the return documentation (https://github.com/ansible-collections/community.aws/pull/1233).
- cloudwatchlogs_log_group - adds support for updating tags (https://github.com/ansible-collections/community.aws/pull/1233).
- cloudwatchlogs_log_group - adds support for returning tags (https://github.com/ansible-collections/community.aws/pull/1233).
- cloudwatchlogs_log_group_info - adds support for returning tags (https://github.com/ansible-collections/community.aws/pull/1233).
226 changes: 137 additions & 89 deletions plugins/modules/cloudwatchlogs_log_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,58 +13,55 @@
version_added: 1.0.0
short_description: create or delete log_group in CloudWatchLogs
notes:
- For details of the parameters and returns see U(http://boto3.readthedocs.io/en/latest/reference/services/logs.html).
- For details of the parameters and returns see U(http://boto3.readthedocs.io/en/latest/reference/services/logs.html).
- Support for I(purge_tags) was added in release 4.0.0.
description:
- Create or delete log_group in CloudWatchLogs.
- Create or delete log_group in CloudWatchLogs.
author:
- Willian Ricardo (@willricardo) <[email protected]>
- Willian Ricardo (@willricardo) <[email protected]>
options:
state:
description:
- Whether the rule is present or absent.
choices: ["present", "absent"]
default: present
required: false
type: str
log_group_name:
description:
- The name of the log group.
required: true
type: str
kms_key_id:
description:
- The Amazon Resource Name (ARN) of the CMK to use when encrypting log data.
required: false
type: str
tags:
description:
- The key-value pairs to use for the tags.
required: false
type: dict
retention:
description:
- The number of days to retain the log events in the specified log group.
- "Valid values are: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653]"
- Mutually exclusive with I(purge_retention_policy).
required: false
type: int
purge_retention_policy:
description:
- "Whether to purge the retention policy or not."
- "Mutually exclusive with I(retention) and I(overwrite)."
default: false
required: false
type: bool
overwrite:
description:
- Whether an existing log group should be overwritten on create.
- Mutually exclusive with I(purge_retention_policy).
default: false
required: false
type: bool
state:
description:
- Whether the rule is present or absent.
choices: ["present", "absent"]
default: present
required: false
type: str
log_group_name:
description:
- The name of the log group.
required: true
type: str
kms_key_id:
description:
- The Amazon Resource Name (ARN) of the CMK to use when encrypting log data.
required: false
type: str
retention:
description:
- The number of days to retain the log events in the specified log group.
- "Valid values are: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653]"
- Mutually exclusive with I(purge_retention_policy).
required: false
type: int
purge_retention_policy:
description:
- "Whether to purge the retention policy or not."
- "Mutually exclusive with I(retention) and I(overwrite)."
default: false
required: false
type: bool
overwrite:
description:
- Whether an existing log group should be overwritten on create.
- Mutually exclusive with I(purge_retention_policy).
default: false
required: false
type: bool
extends_documentation_fragment:
- amazon.aws.aws
- amazon.aws.ec2
- amazon.aws.aws
- amazon.aws.ec2
- amazon.aws.tags
'''

Expand Down Expand Up @@ -96,6 +93,7 @@
description: Return the list of complex objects representing log groups
returned: success
type: complex
version_added: 4.0.0
contains:
log_group_name:
description: The name of the log group.
Expand Down Expand Up @@ -125,6 +123,10 @@
description: The Amazon Resource Name (ARN) of the CMK to use when encrypting log data.
returned: always
type: str
tags:
description: A dictionary representing the tags on the log group.
returned: always
type: dict
'''

try:
Expand All @@ -135,6 +137,8 @@
from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict

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.tagging import compare_aws_tags


def create_log_group(client, log_group_name, kms_key_id, tags, retention, module):
Expand All @@ -154,15 +158,11 @@ def create_log_group(client, log_group_name, kms_key_id, tags, retention, module
log_group_name=log_group_name,
retention=retention, module=module)

desc_log_group = describe_log_group(client=client,
log_group_name=log_group_name,
module=module)
found_log_group = describe_log_group(client=client, log_group_name=log_group_name, module=module)

if 'logGroups' in desc_log_group:
for i in desc_log_group['logGroups']:
if log_group_name == i['logGroupName']:
return i
module.fail_json(msg="The aws CloudWatchLogs log group was not created. \n please try again!")
if not found_log_group:
module.fail_json(msg="The aws CloudWatchLogs log group was not created. \n please try again!")
return found_log_group


def input_retention_policy(client, log_group_name, retention, module):
Expand All @@ -187,35 +187,78 @@ def delete_retention_policy(client, log_group_name, module):


def delete_log_group(client, log_group_name, module):
desc_log_group = describe_log_group(client=client,
log_group_name=log_group_name,
module=module)

try:
if 'logGroups' in desc_log_group:
for i in desc_log_group['logGroups']:
if log_group_name == i['logGroupName']:
client.delete_log_group(logGroupName=log_group_name)

except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
client.delete_log_group(logGroupName=log_group_name)
except is_boto3_error_code('ResourceNotFoundException'):
return {}
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg="Unable to delete log group {0}".format(log_group_name))


def describe_log_group(client, log_group_name, module):
try:
desc_log_group = client.describe_log_groups(logGroupNamePrefix=log_group_name)
return desc_log_group
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Unable to describe log group {0}".format(log_group_name))

matching_logs = [log for log in desc_log_group.get('logGroups', []) if log['logGroupName'] == log_group_name]

if not matching_logs:
return {}

found_log_group = matching_logs[0]

try:
tags = client.list_tags_log_group(logGroupName=log_group_name)
except is_boto3_error_code('AccessDeniedException'):
tags = {}
module.warn('Permission denied listing tags for log group {0}'.format(log_group_name))
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg="Unable to describe tags for log group {0}".format(log_group_name))

found_log_group['tags'] = tags.get('tags', {})
return found_log_group


def format_result(found_log_group):
# Prior to 4.0.0 we documented returning log_groups=[log_group], but returned **log_group
# Return both to avoid a breaking change.
log_group = camel_dict_to_snake_dict(found_log_group, ignore_list=['tags'])
return dict(log_groups=[log_group], **log_group)


def ensure_tags(client, found_log_group, desired_tags, purge_tags, module):
if desired_tags is None:
return False

group_name = module.params.get('log_group_name')
current_tags = found_log_group.get('tags', {})
tags_to_add, tags_to_remove = compare_aws_tags(current_tags, desired_tags, purge_tags)

if not tags_to_add and not tags_to_remove:
return False
if module.check_mode:
return True

try:
if tags_to_remove:
client.untag_log_group(logGroupName=group_name, tags=tags_to_remove)
if tags_to_add:
client.tag_log_group(logGroupName=group_name, tags=tags_to_add)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg='Failed to update tags')

return True


def main():
argument_spec = dict(
log_group_name=dict(required=True, type='str'),
state=dict(choices=['present', 'absent'],
default='present'),
kms_key_id=dict(required=False, type='str'),
tags=dict(required=False, type='dict'),
tags=dict(required=False, type='dict', aliases=['resource_tags']),
purge_tags=dict(required=False, type='bool', default=True),
retention=dict(required=False, type='int'),
purge_retention_policy=dict(required=False, type='bool', default=False),
overwrite=dict(required=False, type='bool', default=False),
Expand All @@ -233,12 +276,7 @@ def main():
changed = False

# Determine if the log group exists
desc_log_group = describe_log_group(client=logs, log_group_name=module.params['log_group_name'], module=module)
found_log_group = {}
for i in desc_log_group.get('logGroups', []):
if module.params['log_group_name'] == i['logGroupName']:
found_log_group = i
break
found_log_group = describe_log_group(client=logs, log_group_name=module.params['log_group_name'], module=module)

if state == 'present':
if found_log_group:
Expand All @@ -251,20 +289,29 @@ def main():
tags=module.params['tags'],
retention=module.params['retention'],
module=module)
elif module.params['purge_retention_policy']:
if found_log_group.get('retentionInDays'):
changed = True
delete_retention_policy(client=logs,
log_group_name=module.params['log_group_name'],
module=module)
elif module.params['retention'] != found_log_group.get('retentionInDays'):
if module.params['retention'] is not None:
changed = True
input_retention_policy(client=logs,
log_group_name=module.params['log_group_name'],
retention=module.params['retention'],
module=module)
found_log_group['retentionInDays'] = module.params['retention']
else:
changed |= ensure_tags(client=logs,
found_log_group=found_log_group,
desired_tags=module.params['tags'],
purge_tags=module.params['purge_tags'],
module=module)
if module.params['purge_retention_policy']:
if found_log_group.get('retentionInDays'):
changed = True
delete_retention_policy(client=logs,
log_group_name=module.params['log_group_name'],
module=module)
elif module.params['retention'] != found_log_group.get('retentionInDays'):
if module.params['retention'] is not None:
changed = True
input_retention_policy(client=logs,
log_group_name=module.params['log_group_name'],
retention=module.params['retention'],
module=module)
if changed:
found_log_group = describe_log_group(client=logs,
log_group_name=module.params['log_group_name'],
module=module)

elif not found_log_group:
changed = True
Expand All @@ -275,7 +322,8 @@ def main():
retention=module.params['retention'],
module=module)

module.exit_json(changed=changed, **camel_dict_to_snake_dict(found_log_group))
result = format_result(found_log_group)
module.exit_json(changed=changed, **result)

elif state == 'absent':
if found_log_group:
Expand Down
39 changes: 28 additions & 11 deletions plugins/modules/cloudwatchlogs_log_group_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,17 @@
version_added: 1.0.0
short_description: Get information about log_group in CloudWatchLogs
description:
- Lists the specified log groups. You can list all your log groups or filter the results by prefix.
- Lists the specified log groups. You can list all your log groups or filter the results by prefix.
author:
- Willian Ricardo (@willricardo) <[email protected]>
- Willian Ricardo (@willricardo) <[email protected]>
options:
log_group_name:
description:
- The name or prefix of the log group to filter by.
type: str
log_group_name:
description:
- The name or prefix of the log group to filter by.
type: str
extends_documentation_fragment:
- amazon.aws.aws
- amazon.aws.ec2
- amazon.aws.aws
- amazon.aws.ec2
'''

EXAMPLES = '''
Expand Down Expand Up @@ -67,6 +66,11 @@
description: The Amazon Resource Name (ARN) of the CMK to use when encrypting log data.
returned: always
type: str
tags:
description: A dictionary representing the tags on the log group.
returned: always
type: dict
version_added: 4.0.0
'''

try:
Expand All @@ -77,6 +81,7 @@
from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict

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


def describe_log_group(client, log_group_name, module):
Expand All @@ -86,10 +91,22 @@ def describe_log_group(client, log_group_name, module):
try:
paginator = client.get_paginator('describe_log_groups')
desc_log_group = paginator.paginate(**params).build_full_result()
return desc_log_group
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Unable to describe log group {0}".format(log_group_name))

for log_group in desc_log_group['logGroups']:
log_group_name = log_group['logGroupName']
try:
tags = client.list_tags_log_group(logGroupName=log_group_name)
except is_boto3_error_code('AccessDeniedException'):
tags = {}
module.warn('Permission denied listing tags for log group {0}'.format(log_group_name))
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except
module.fail_json_aws(e, msg="Unable to describe tags for log group {0}".format(log_group_name))
log_group['tags'] = tags.get('tags', {})

return desc_log_group


def main():
argument_spec = dict(
Expand All @@ -109,7 +126,7 @@ def main():
final_log_group_snake = []

for log_group in desc_log_group['logGroups']:
final_log_group_snake.append(camel_dict_to_snake_dict(log_group))
final_log_group_snake.append(camel_dict_to_snake_dict(log_group, ignore_list=['tags']))

desc_log_group_result = dict(changed=False, log_groups=final_log_group_snake)
module.exit_json(**desc_log_group_result)
Expand Down
Loading

0 comments on commit 3f8d2fc

Please sign in to comment.