Skip to content

Commit

Permalink
lambda - Add support for purge_tags (#1202)
Browse files Browse the repository at this point in the history
lambda - Add support for purge_tags

SUMMARY

Use tagging fragment
Add purge_tags
Add resource_tags alias to tags
fix bug with returned tag names getting snake cased
fix bug where the lambda module was modifying tags in check mode
Tweak tagging to require an explicit tags: {} to remove tags

ISSUE TYPE

Bugfix Pull Request
Docs Pull Request
Feature Pull Request

COMPONENT NAME
lambda
ADDITIONAL INFORMATION

Reviewed-by: Joseph Torcasso <None>
Reviewed-by: Mark Chappell <None>
  • Loading branch information
tremble authored Jun 3, 2022
1 parent a538562 commit 982580a
Show file tree
Hide file tree
Showing 5 changed files with 320 additions and 47 deletions.
6 changes: 6 additions & 0 deletions changelogs/fragments/1202-tagging-lambda.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
bugfixes:
- lambda - fix bug where the lambda module was modifying tags in check mode (https://github.com/ansible-collections/community.aws/pull/1202).
- lambda - fix bug where tag keys were mangled in the return values (https://github.com/ansible-collections/community.aws/pull/1202).
minor_changes:
- lambda - ``resource_tags`` has been added as an alias for the ``tags`` parameter (https://github.com/ansible-collections/community.aws/pull/1202).
- lambda - the behavior for ``tags`` has been updated, to remove all tags the ``tags`` parameter must be explicitly set to the empty dict ``{}`` and ``purge_tags`` to ``True`` (https://github.com/ansible-collections/community.aws/pull/1202).
48 changes: 32 additions & 16 deletions plugins/modules/lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,6 @@
- Set mode to 'Active' to sample and trace incoming requests with AWS X-Ray. Turned off (set to 'PassThrough') by default.
choices: ['Active', 'PassThrough']
type: str
tags:
description:
- Tag dict to apply to the function.
type: dict
kms_key_arn:
description:
- The KMS key ARN used to encrypt the function's environment variables.
Expand All @@ -117,6 +113,7 @@
extends_documentation_fragment:
- amazon.aws.aws
- amazon.aws.ec2
- amazon.aws.tags
'''

Expand Down Expand Up @@ -391,7 +388,10 @@ def sha256sum(filename):
return hex_digest


def set_tag(client, module, tags, function):
def set_tag(client, module, tags, function, purge_tags):

if tags is None:
return False

changed = False
arn = function['Configuration']['FunctionArn']
Expand All @@ -401,7 +401,13 @@ def set_tag(client, module, tags, function):
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Unable to list tags")

tags_to_add, tags_to_remove = compare_aws_tags(current_tags, tags, purge_tags=True)
tags_to_add, tags_to_remove = compare_aws_tags(current_tags, tags, purge_tags=purge_tags)

if not tags_to_remove and not tags_to_add:
return False

if module.check_mode:
return True

try:
if tags_to_remove:
Expand Down Expand Up @@ -438,6 +444,14 @@ def wait_for_lambda(client, module, name):
module.fail_json_aws(e, msg='Failed while waiting on lambda to finish updating')


def format_response(response):
tags = response.get("Tags", {})
result = camel_dict_to_snake_dict(response)
# Lambda returns a dict rather than the usual boto3 list of dicts
result["tags"] = tags
return result


def main():
argument_spec = dict(
name=dict(required=True),
Expand All @@ -458,7 +472,8 @@ def main():
dead_letter_arn=dict(),
kms_key_arn=dict(type='str', no_log=False),
tracing_mode=dict(choices=['Active', 'PassThrough']),
tags=dict(type='dict'),
tags=dict(type='dict', aliases=['resource_tags']),
purge_tags=dict(type='bool', default=True),
)

mutually_exclusive = [['zip_file', 's3_key'],
Expand Down Expand Up @@ -494,6 +509,7 @@ def main():
dead_letter_arn = module.params.get('dead_letter_arn')
tracing_mode = module.params.get('tracing_mode')
tags = module.params.get('tags')
purge_tags = module.params.get('purge_tags')
kms_key_arn = module.params.get('kms_key_arn')

check_mode = module.check_mode
Expand Down Expand Up @@ -614,7 +630,7 @@ def main():

# Tag Function
if tags is not None:
if set_tag(client, module, tags, current_function):
if set_tag(client, module, tags, current_function, purge_tags):
changed = True

# Upload new code if needed (e.g. code checksum has changed)
Expand All @@ -634,9 +650,9 @@ def main():
response = get_current_function(client, name, qualifier=current_version)
if not response:
module.fail_json(msg='Unable to get function information after updating')

response = format_response(response)
# We're done
module.exit_json(changed=changed, **camel_dict_to_snake_dict(response))
module.exit_json(changed=changed, **response)

# Function doesn't exists, create new Lambda function
elif state == 'present':
Expand Down Expand Up @@ -691,6 +707,10 @@ def main():
func_kwargs.update({'VpcConfig': {'SubnetIds': vpc_subnet_ids,
'SecurityGroupIds': vpc_security_group_ids}})

# Tag Function
if tags:
func_kwargs.update({'Tags': tags})

# Function would have been created if not check mode
if check_mode:
module.exit_json(changed=True)
Expand All @@ -704,15 +724,11 @@ def main():
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e, msg="Trying to create function")

# Tag Function
if tags is not None:
if set_tag(client, module, tags, get_current_function(client, name)):
changed = True

response = get_current_function(client, name, qualifier=current_version)
if not response:
module.fail_json(msg='Unable to get function information after creating')
module.exit_json(changed=changed, **camel_dict_to_snake_dict(response))
response = format_response(response)
module.exit_json(changed=changed, **response)

# Delete existing Lambda function
if state == 'absent' and current_function:
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/targets/lambda/defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
# we hash the resource_prefix to get a shorter, unique string
lambda_function_name: '{{ tiny_prefix }}'
lambda_role_name: 'ansible-test-{{ tiny_prefix }}-lambda'

lambda_python_runtime: 'python3.9'
lambda_python_handler: 'mini_lambda.handler'
64 changes: 33 additions & 31 deletions tests/integration/targets/lambda/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
- name: test with no role or handler
lambda:
name: ansible-testing-fake-should-not-be-created
runtime: python3.6
runtime: '{{ lambda_python_runtime }}'
register: result
ignore_errors: true
- name: assert failure when called with no parameters
Expand All @@ -83,7 +83,7 @@
- name: test state=present with security group but no vpc
lambda:
name: '{{ lambda_function_name }}'
runtime: 'python3.6'
runtime: '{{ lambda_python_runtime }}'
role: '{{ lambda_role_name }}'
zip_file: '{{ zip_res.dest }}'
handler: '{{ omit }}'
Expand All @@ -106,8 +106,8 @@
- name: test state=present - upload the lambda (check mode)
lambda:
name: '{{ lambda_function_name }}'
runtime: python3.6
handler: mini_lambda.handler
runtime: '{{ lambda_python_runtime }}'
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
zip_file: '{{ zip_res.dest }}'
register: result
Expand All @@ -120,8 +120,8 @@
- name: test state=present - upload the lambda
lambda:
name: '{{ lambda_function_name }}'
runtime: python3.6
handler: mini_lambda.handler
runtime: '{{ lambda_python_runtime }}'
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
zip_file: '{{ zip_res.dest }}'
register: result
Expand All @@ -131,6 +131,8 @@
- result.changed
- result.configuration.tracing_config.mode == "PassThrough"

- include_tasks: 'tagging.yml'

# Test basic operation of Uploaded lambda
- name: test lambda works (check mode)
execute_lambda:
Expand Down Expand Up @@ -163,7 +165,7 @@
name: '{{lambda_function_name}}'
runtime: nodejs14.x
tracing_mode: Active
handler: mini_lambda.handler
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
tags:
CamelCase: 'ACamelCaseValue'
Expand All @@ -182,7 +184,7 @@
name: '{{lambda_function_name}}'
runtime: nodejs14.x
tracing_mode: Active
handler: mini_lambda.handler
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
tags:
CamelCase: 'ACamelCaseValue'
Expand All @@ -202,7 +204,7 @@
name: '{{lambda_function_name}}'
runtime: nodejs14.x
tracing_mode: Active
handler: mini_lambda.handler
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
tags:
CamelCase: 'ACamelCaseValue'
Expand All @@ -221,7 +223,7 @@
name: '{{lambda_function_name}}'
runtime: nodejs14.x
tracing_mode: Active
handler: mini_lambda.handler
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
tags:
CamelCase: 'ACamelCaseValue'
Expand All @@ -239,17 +241,17 @@
- name: reset config updates for the following tests
lambda:
name: '{{lambda_function_name}}'
runtime: python3.6
runtime: '{{ lambda_python_runtime }}'
tracing_mode: PassThrough
handler: mini_lambda.handler
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
register: result
- name: assert that reset succeeded
assert:
that:
- result is not failed
- result.changed == True
- result.configuration.runtime == 'python3.6'
- result.configuration.runtime == lambda_python_runtime
- result.configuration.tracing_config.mode == 'PassThrough'

# Test lambda_info
Expand All @@ -264,10 +266,10 @@
- lambda_infos_all is not failed
- lambda_infos_all.function | length > 0
- lambda_infos_all.function[lambda_function_name].function_name == lambda_function_name
- lambda_infos_all.function[lambda_function_name].runtime == "python3.6"
- lambda_infos_all.function[lambda_function_name].runtime == lambda_python_runtime
- lambda_infos_all.function[lambda_function_name].description == ""
- lambda_infos_all.function[lambda_function_name].function_arn is defined
- lambda_infos_all.function[lambda_function_name].handler == "mini_lambda.handler"
- lambda_infos_all.function[lambda_function_name].handler == lambda_python_handler
- lambda_infos_all.function[lambda_function_name].versions is defined
- lambda_infos_all.function[lambda_function_name].aliases is defined
- lambda_infos_all.function[lambda_function_name].policy is defined
Expand All @@ -284,10 +286,10 @@
- lambda_infos_query_config is not failed
- lambda_infos_query_config.function | length > 0
- lambda_infos_query_config.function[lambda_function_name].function_name == lambda_function_name
- lambda_infos_query_config.function[lambda_function_name].runtime == "python3.6"
- lambda_infos_query_config.function[lambda_function_name].runtime == lambda_python_runtime
- lambda_infos_query_config.function[lambda_function_name].description == ""
- lambda_infos_query_config.function[lambda_function_name].function_arn is defined
- lambda_infos_query_config.function[lambda_function_name].handler == "mini_lambda.handler"
- lambda_infos_query_config.function[lambda_function_name].handler == lambda_python_handler
- lambda_infos_query_config.function[lambda_function_name].versions is not defined
- lambda_infos_query_config.function[lambda_function_name].aliases is not defined
- lambda_infos_query_config.function[lambda_function_name].policy is not defined
Expand Down Expand Up @@ -400,10 +402,10 @@
- name: test state=present with all nullable variables explicitly set to null
lambda:
name: '{{lambda_function_name}}'
runtime: python3.6
runtime: '{{ lambda_python_runtime }}'
role: '{{ lambda_role_name }}'
zip_file: '{{zip_res.dest}}'
handler: mini_lambda.handler
handler: '{{ lambda_python_handler }}'
description: null
vpc_subnet_ids: null
vpc_security_group_ids: null
Expand All @@ -419,8 +421,8 @@
- name: test putting an environment variable changes lambda (check mode)
lambda:
name: '{{lambda_function_name}}'
runtime: python3.6
handler: mini_lambda.handler
runtime: '{{ lambda_python_runtime }}'
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
zip_file: '{{zip_res.dest}}'
environment_variables:
Expand All @@ -436,8 +438,8 @@
- name: test putting an environment variable changes lambda
lambda:
name: '{{lambda_function_name}}'
runtime: python3.6
handler: mini_lambda.handler
runtime: '{{ lambda_python_runtime }}'
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
zip_file: '{{zip_res.dest}}'
environment_variables:
Expand Down Expand Up @@ -517,35 +519,35 @@
- name: parallel lambda creation 1/4
lambda:
name: '{{lambda_function_name}}_1'
runtime: python3.6
handler: mini_lambda.handler
runtime: '{{ lambda_python_runtime }}'
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
zip_file: '{{zip_res.dest}}'
async: 1000
register: async_1
- name: parallel lambda creation 2/4
lambda:
name: '{{lambda_function_name}}_2'
runtime: python3.6
handler: mini_lambda.handler
runtime: '{{ lambda_python_runtime }}'
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
zip_file: '{{zip_res.dest}}'
async: 1000
register: async_2
- name: parallel lambda creation 3/4
lambda:
name: '{{lambda_function_name}}_3'
runtime: python3.6
handler: mini_lambda.handler
runtime: '{{ lambda_python_runtime }}'
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
zip_file: '{{zip_res.dest}}'
async: 1000
register: async_3
- name: parallel lambda creation 4/4
lambda:
name: '{{lambda_function_name}}_4'
runtime: python3.6
handler: mini_lambda.handler
runtime: '{{ lambda_python_runtime }}'
handler: '{{ lambda_python_handler }}'
role: '{{ lambda_role_name }}'
zip_file: '{{zip_res.dest}}'
register: result
Expand Down
Loading

0 comments on commit 982580a

Please sign in to comment.