From 7d59e6929991838441e9d0671d56c76c6f87ba14 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 19 Feb 2021 23:03:46 +0100 Subject: [PATCH] Cleanup lambda_alias (#396) * Update lambda_alias to use AnsibleAWSModule.client * Update lambda_alias to use fail_json_aws * Replace custom snake/camel conversion * lambda_alias replace use of AWSConnection with passing a standard (wrapped) boto3 connection * Enable Retries * Fix idempotency when description isn't set. * Don't throw an exception when attempting to create a new alias in check mode * Add revision_id to return docs * Add integration tests * add changelog --- changelogs/fragments/396-lambda_alias.yml | 4 + plugins/modules/lambda_alias.py | 116 +--- .../integration/targets/lambda_alias/aliases | 2 + .../targets/lambda_alias/defaults/main.yml | 10 + .../targets/lambda_alias/files/mini_lambda.py | 48 ++ .../files/minimal_trust_policy.json | 12 + .../targets/lambda_alias/meta/main.yml | 3 + .../targets/lambda_alias/tasks/main.yml | 622 ++++++++++++++++++ 8 files changed, 735 insertions(+), 82 deletions(-) create mode 100644 changelogs/fragments/396-lambda_alias.yml create mode 100644 tests/integration/targets/lambda_alias/aliases create mode 100644 tests/integration/targets/lambda_alias/defaults/main.yml create mode 100644 tests/integration/targets/lambda_alias/files/mini_lambda.py create mode 100644 tests/integration/targets/lambda_alias/files/minimal_trust_policy.json create mode 100644 tests/integration/targets/lambda_alias/meta/main.yml create mode 100644 tests/integration/targets/lambda_alias/tasks/main.yml diff --git a/changelogs/fragments/396-lambda_alias.yml b/changelogs/fragments/396-lambda_alias.yml new file mode 100644 index 00000000000..e8929cd6c51 --- /dev/null +++ b/changelogs/fragments/396-lambda_alias.yml @@ -0,0 +1,4 @@ +minor_changes: +- lambda_alias - use common helper functions to create AWS connections (https://github.com/ansible-collections/community.aws/pull/396). +- lambda_alias - use common helper functions to perform snake_case to CamelCase conversions (https://github.com/ansible-collections/community.aws/pull/396). +- lambda_alias - add retries on common AWS failures (https://github.com/ansible-collections/community.aws/pull/396). diff --git a/plugins/modules/lambda_alias.py b/plugins/modules/lambda_alias.py index aeacb6e3b75..9ccfbef7ea6 100644 --- a/plugins/modules/lambda_alias.py +++ b/plugins/modules/lambda_alias.py @@ -139,6 +139,11 @@ returned: success type: str sample: dev +revision_id: + description: A unique identifier that changes when you update the alias. + returned: success + type: str + sample: 12345678-1234-1234-1234-123456789abc ''' import re @@ -149,67 +154,16 @@ pass # Handled by AnsibleAWSModule from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict +from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_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.ec2 import boto3_conn -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info - - -class AWSConnection: - """ - Create the connection object and client objects as required. - """ - - def __init__(self, ansible_obj, resources, boto3_=True): - - try: - self.region, self.endpoint, aws_connect_kwargs = get_aws_connection_info(ansible_obj, boto3=boto3_) - - self.resource_client = dict() - if not resources: - resources = ['lambda'] - - resources.append('iam') - - for resource in resources: - aws_connect_kwargs.update(dict(region=self.region, - endpoint=self.endpoint, - conn_type='client', - resource=resource - )) - self.resource_client[resource] = boto3_conn(ansible_obj, **aws_connect_kwargs) - - # if region is not provided, then get default profile/session region - if not self.region: - self.region = self.resource_client['lambda'].meta.region_name - - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - ansible_obj.fail_json(msg="Unable to connect, authorize or access resource: {0}".format(e)) - - try: - self.account_id = self.resource_client['iam'].get_user()['User']['Arn'].split(':')[4] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError, ValueError, KeyError, IndexError): - self.account_id = '' - - def client(self, resource='lambda'): - return self.resource_client[resource] - - -def pc(key): - """ - Changes python key into Pascale case equivalent. For example, 'this_function_name' becomes 'ThisFunctionName'. - - :param key: - :return: - """ - - return "".join([token.capitalize() for token in key.split('_')]) +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry def set_api_params(module, module_params): """ - Sets module parameters to those expected by the boto3 API. + Sets non-None module parameters to those expected by the boto3 API. :param module: :param module_params: @@ -221,17 +175,16 @@ def set_api_params(module, module_params): for param in module_params: module_param = module.params.get(param, None) if module_param: - api_params[pc(param)] = module_param + api_params[param] = module_param - return api_params + return snake_dict_to_camel_dict(api_params, capitalize_first=True) -def validate_params(module, aws): +def validate_params(module): """ Performs basic parameter validation. - :param module: Ansible module reference - :param aws: AWS client connection + :param module: AnsibleAWSModule reference :return: """ @@ -254,23 +207,21 @@ def validate_params(module, aws): return -def get_lambda_alias(module, aws): +def get_lambda_alias(module, client): """ Returns the lambda function alias if it exists. - :param module: Ansible module reference - :param aws: AWS client connection + :param module: AnsibleAWSModule + :param client: (wrapped) boto3 lambda client :return: """ - client = aws.client('lambda') - # set API parameters api_params = set_api_params(module, ('function_name', 'name')) # check if alias exists and get facts try: - results = client.get_alias(**api_params) + results = client.get_alias(aws_retry=True, **api_params) except is_boto3_error_code('ResourceNotFoundException'): results = None except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except @@ -279,31 +230,33 @@ def get_lambda_alias(module, aws): return results -def lambda_alias(module, aws): +def lambda_alias(module, client): """ Adds, updates or deletes lambda function aliases. - :param module: Ansible module reference - :param aws: AWS client connection + :param module: AnsibleAWSModule + :param client: (wrapped) boto3 lambda client :return dict: """ - client = aws.client('lambda') results = dict() changed = False current_state = 'absent' state = module.params['state'] - facts = get_lambda_alias(module, aws) + facts = get_lambda_alias(module, client) if facts: current_state = 'present' if state == 'present': if current_state == 'present': + snake_facts = camel_dict_to_snake_dict(facts) # check if alias has changed -- only version and description can change alias_params = ('function_version', 'description') for param in alias_params: - if module.params.get(param) != facts.get(pc(param)): + if module.params.get(param) is None: + continue + if module.params.get(param) != snake_facts.get(param): changed = True break @@ -313,9 +266,9 @@ def lambda_alias(module, aws): if not module.check_mode: try: - results = client.update_alias(**api_params) + results = client.update_alias(aws_retry=True, **api_params) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json(msg='Error updating function alias: {0}'.format(e)) + module.fail_json_aws(e, msg='Error updating function alias') else: # create new function alias @@ -323,10 +276,10 @@ def lambda_alias(module, aws): try: if not module.check_mode: - results = client.create_alias(**api_params) + results = client.create_alias(aws_retry=True, **api_params) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json(msg='Error creating function alias: {0}'.format(e)) + module.fail_json_aws(e, msg='Error creating function alias') else: # state = 'absent' if current_state == 'present': @@ -335,12 +288,12 @@ def lambda_alias(module, aws): try: if not module.check_mode: - results = client.delete_alias(**api_params) + results = client.delete_alias(aws_retry=True, **api_params) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json(msg='Error deleting function alias: {0}'.format(e)) + module.fail_json_aws(e, msg='Error deleting function alias') - return dict(changed=changed, **dict(results or facts)) + return dict(changed=changed, **dict(results or facts or {})) def main(): @@ -364,11 +317,10 @@ def main(): required_together=[], ) - aws = AWSConnection(module, ['lambda']) - - validate_params(module, aws) + client = module.client('lambda', retry_decorator=AWSRetry.jittered_backoff()) - results = lambda_alias(module, aws) + validate_params(module) + results = lambda_alias(module, client) module.exit_json(**camel_dict_to_snake_dict(results)) diff --git a/tests/integration/targets/lambda_alias/aliases b/tests/integration/targets/lambda_alias/aliases new file mode 100644 index 00000000000..157ce0c9d4c --- /dev/null +++ b/tests/integration/targets/lambda_alias/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group3 diff --git a/tests/integration/targets/lambda_alias/defaults/main.yml b/tests/integration/targets/lambda_alias/defaults/main.yml new file mode 100644 index 00000000000..ea29794efe0 --- /dev/null +++ b/tests/integration/targets/lambda_alias/defaults/main.yml @@ -0,0 +1,10 @@ +--- +# defaults file for lambda integration test +lambda_function_name: '{{ resource_prefix }}' +# IAM role names have to be less than 64 characters +# The 8 digit identifier at the end of resource_prefix helps determine during +# which test something was created and allows tests to be run in parallel +# Shippable resource_prefixes are in the format shippable-123456-123, so in those cases +# we need both sets of digits to keep the resource name unique +unique_id: "{{ resource_prefix | regex_search('(\\d+-?)(\\d+)$') }}" +lambda_role_name: 'ansible-test-{{ unique_id }}-lambda' diff --git a/tests/integration/targets/lambda_alias/files/mini_lambda.py b/tests/integration/targets/lambda_alias/files/mini_lambda.py new file mode 100644 index 00000000000..901f6b55a77 --- /dev/null +++ b/tests/integration/targets/lambda_alias/files/mini_lambda.py @@ -0,0 +1,48 @@ +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json +import os + + +def handler(event, context): + """ + The handler function is the function which gets called each time + the lambda is run. + """ + # printing goes to the cloudwatch log allowing us to simply debug the lambda if we can find + # the log entry. + print("got event:\n" + json.dumps(event)) + + # if the name parameter isn't present this can throw an exception + # which will result in an amazon chosen failure from the lambda + # which can be completely fine. + + name = event["name"] + + # we can use environment variables as part of the configuration of the lambda + # which can change the behaviour of the lambda without needing a new upload + + extra = os.environ.get("EXTRA_MESSAGE") + if extra is not None and len(extra) > 0: + greeting = "hello {0}. {1}".format(name, extra) + else: + greeting = "hello " + name + + return {"message": greeting} + + +def main(): + """ + This main function will normally never be called during normal + lambda use. It is here for testing the lambda program only. + """ + event = {"name": "james"} + context = None + print(handler(event, context)) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/lambda_alias/files/minimal_trust_policy.json b/tests/integration/targets/lambda_alias/files/minimal_trust_policy.json new file mode 100644 index 00000000000..fb84ae9de15 --- /dev/null +++ b/tests/integration/targets/lambda_alias/files/minimal_trust_policy.json @@ -0,0 +1,12 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/tests/integration/targets/lambda_alias/meta/main.yml b/tests/integration/targets/lambda_alias/meta/main.yml new file mode 100644 index 00000000000..1f64f1169a9 --- /dev/null +++ b/tests/integration/targets/lambda_alias/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 diff --git a/tests/integration/targets/lambda_alias/tasks/main.yml b/tests/integration/targets/lambda_alias/tasks/main.yml new file mode 100644 index 00000000000..6796d14cca6 --- /dev/null +++ b/tests/integration/targets/lambda_alias/tasks/main.yml @@ -0,0 +1,622 @@ +- name: set connection information for AWS modules and run tests + module_defaults: + group/aws: + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' + collections: + - community.general + block: + # ============================================================== + # Preparation + - name: create minimal lambda role + iam_role: + name: '{{ lambda_role_name }}' + assume_role_policy_document: '{{ lookup("file", "minimal_trust_policy.json") }}' + create_instance_profile: false + managed_policies: + - 'arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess' + register: iam_role + - name: wait 10 seconds for role to become available + pause: + seconds: 10 + when: iam_role.changed + - name: move lambda into place for archive module + copy: + src: mini_lambda.py + dest: '{{ output_dir }}/mini_lambda.py' + mode: preserve + - name: bundle lambda into a zip + register: zip_res + archive: + format: zip + path: '{{ output_dir }}/mini_lambda.py' + dest: '{{ output_dir }}/mini_lambda.zip' + + - name: Upload test lambda (version 1) + lambda: + name: '{{ lambda_function_name }}' + runtime: 'python2.7' + handler: 'mini_lambda.handler' + role: '{{ lambda_role_name }}' + zip_file: '{{ zip_res.dest }}' + register: lambda_a + - name: assert lambda upload succeeded + assert: + that: + - lambda_a is changed + + - name: Update lambda (version 2) + lambda: + name: '{{ lambda_function_name }}' + runtime: 'python3.6' + handler: 'mini_lambda.handler' + role: '{{ lambda_role_name }}' + register: lambda_b + - name: assert that update succeeded + assert: + that: + - lambda_b is changed + + - name: Update lambda (version 3 / LATEST) + lambda: + name: '{{ lambda_function_name }}' + runtime: 'python3.7' + handler: 'mini_lambda.handler' + role: '{{ lambda_role_name }}' + register: lambda_c + - name: assert that update succeeded + assert: + that: + - lambda_c is changed + + - name: Store Lambda info + vars: + _full_arn: '{{ lambda_a.configuration.function_arn }}' + set_fact: + lambda_arn: '{{ ":".join(_full_arn.split(":")[:-1]) }}' + + # ============================================================== + # Creation of an alias + - name: Create an alias (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + check_mode: True + register: create_alias + - name: Check changed + assert: + that: + - create_alias is changed + + - name: Create an alias + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + register: create_alias + - name: Check changed and returned values + assert: + that: + - create_alias is changed + - '"alias_arn" in create_alias' + - create_alias.alias_arn.startswith(lambda_arn) + - create_alias.alias_arn.endswith("Testing") + - '"description" in create_alias' + - create_alias.description == "" + - '"function_version" in create_alias' + - create_alias.function_version == "$LATEST" + - '"name" in create_alias' + - create_alias.name == "Testing" + - '"revision_id" in create_alias' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + - name: Create an alias - idempotency (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + check_mode: True + register: create_alias + - name: Check not changed + assert: + that: + - create_alias is not changed + + - name: Create an alias - idempotecy + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + register: create_alias + - name: Check not changed + assert: + that: + - create_alias is not changed + - '"alias_arn" in create_alias' + - create_alias.alias_arn.startswith(lambda_arn) + - create_alias.alias_arn.endswith("Testing") + - '"description" in create_alias' + - create_alias.description == "" + - '"function_version" in create_alias' + - create_alias.function_version == "$LATEST" + - '"name" in create_alias' + - create_alias.name == "Testing" + - '"revision_id" in create_alias' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + # ============================================================== + # Update description of an alias when none set to start + - name: Update an alias description (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + description: 'Description 1' + check_mode: True + register: update_alias_description + - name: Check changed + assert: + that: + - update_alias_description is changed + + - name: Update an alias description + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + description: 'Description 1' + register: update_alias_description + - name: Check changed and returned values + assert: + that: + - update_alias_description is changed + - '"alias_arn" in update_alias_description' + - update_alias_description.alias_arn.startswith(lambda_arn) + - update_alias_description.alias_arn.endswith("Testing") + - '"description" in update_alias_description' + - update_alias_description.description == "Description 1" + - '"function_version" in update_alias_description' + - update_alias_description.function_version == "$LATEST" + - '"name" in update_alias_description' + - update_alias_description.name == "Testing" + - '"revision_id" in update_alias_description' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + - name: Update an alias description - idempotency (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + description: 'Description 1' + check_mode: True + register: update_alias_description + - name: Check not changed + assert: + that: + - update_alias_description is not changed + + - name: Update an alias description - idempotecy + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + description: 'Description 1' + register: update_alias_description + - name: Check not changed + assert: + that: + - update_alias_description is not changed + - '"alias_arn" in update_alias_description' + - update_alias_description.alias_arn.startswith(lambda_arn) + - update_alias_description.alias_arn.endswith("Testing") + - '"description" in update_alias_description' + - update_alias_description.description == "Description 1" + - '"function_version" in update_alias_description' + - update_alias_description.function_version == "$LATEST" + - '"name" in update_alias_description' + - update_alias_description.name == "Testing" + - '"revision_id" in update_alias_description' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + # ============================================================== + # Update description of an alias when one set to start + - name: Update an alias description again (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + description: 'description 2' + check_mode: True + register: update_alias_description + - name: Check changed + assert: + that: + - update_alias_description is changed + + - name: Update an alias description again + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + description: 'description 2' + register: update_alias_description + - name: Check changed and returned values + assert: + that: + - update_alias_description is changed + - '"alias_arn" in update_alias_description' + - update_alias_description.alias_arn.startswith(lambda_arn) + - update_alias_description.alias_arn.endswith("Testing") + - '"description" in update_alias_description' + - update_alias_description.description == "description 2" + - '"function_version" in update_alias_description' + - update_alias_description.function_version == "$LATEST" + - '"name" in update_alias_description' + - update_alias_description.name == "Testing" + - '"revision_id" in update_alias_description' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + # ============================================================== + # Update version of an alias + - name: Update an alias version (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + function_version: 1 + check_mode: True + register: update_alias_version + - name: Check changed + assert: + that: + - update_alias_version is changed + + - name: Update an alias version + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + function_version: 1 + register: update_alias_version + - name: Check changed and returned values + assert: + that: + - update_alias_version is changed + - '"alias_arn" in update_alias_version' + - update_alias_version.alias_arn.startswith(lambda_arn) + - update_alias_version.alias_arn.endswith("Testing") + - '"description" in update_alias_version' + - update_alias_version.description == "description 2" + - '"function_version" in update_alias_version' + - update_alias_version.function_version == "1" + - '"name" in update_alias_version' + - update_alias_version.name == "Testing" + - '"revision_id" in update_alias_version' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + - name: Update an alias version - idempotency (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + function_version: 1 + check_mode: True + register: update_alias_version + - name: Check not changed + assert: + that: + - update_alias_version is not changed + + - name: Update an alias version - idempotecy + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + function_version: 1 + register: update_alias_version + - name: Check not changed + assert: + that: + - update_alias_version is not changed + - '"alias_arn" in update_alias_version' + - update_alias_version.alias_arn.startswith(lambda_arn) + - update_alias_version.alias_arn.endswith("Testing") + - '"description" in update_alias_version' + - update_alias_version.description == "description 2" + - '"function_version" in update_alias_version' + - update_alias_version.function_version == "1" + - '"name" in update_alias_version' + - update_alias_version.name == "Testing" + - '"revision_id" in update_alias_version' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + - name: Update an alias version to implied LATEST (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + # docs state that when not defined defaults to LATEST + #function_version: 1 + check_mode: True + register: update_alias_version + - name: Check changed + assert: + that: + - update_alias_version is changed + + - name: Update an alias version to implied LATEST + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + # docs state that when not defined defaults to LATEST + #function_version: 1 + register: update_alias_version + - name: Check changed and returned values + assert: + that: + - update_alias_version is changed + - '"alias_arn" in update_alias_version' + - update_alias_version.alias_arn.startswith(lambda_arn) + - update_alias_version.alias_arn.endswith("Testing") + - '"description" in update_alias_version' + - update_alias_version.description == "description 2" + - '"function_version" in update_alias_version' + - update_alias_version.function_version == "$LATEST" + - '"name" in update_alias_version' + - update_alias_version.name == "Testing" + - '"revision_id" in update_alias_version' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + # Make sure that 0 also causes a change + - name: Update an alias version + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + function_version: 1 + register: update_alias_version + - name: Check not changed + assert: + that: + - update_alias_version is changed + - '"alias_arn" in update_alias_version' + - update_alias_version.alias_arn.startswith(lambda_arn) + - update_alias_version.alias_arn.endswith("Testing") + - '"description" in update_alias_version' + - update_alias_version.description == "description 2" + - '"function_version" in update_alias_version' + - update_alias_version.function_version == "1" + - '"name" in update_alias_version' + - update_alias_version.name == "Testing" + - '"revision_id" in update_alias_version' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + - name: Update an alias version to explicit LATEST with 0 (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + function_version: 0 + check_mode: True + register: update_alias_version + - name: Check changed + assert: + that: + - update_alias_version is changed + + - name: Update an alias version to explicit LATEST with 0 + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + function_version: 0 + register: update_alias_version + - name: Check changed and returned values + assert: + that: + - update_alias_version is changed + - '"alias_arn" in update_alias_version' + - update_alias_version.alias_arn.startswith(lambda_arn) + - update_alias_version.alias_arn.endswith("Testing") + - '"description" in update_alias_version' + - update_alias_version.description == "description 2" + - '"function_version" in update_alias_version' + - update_alias_version.function_version == "$LATEST" + - '"name" in update_alias_version' + - update_alias_version.name == "Testing" + - '"revision_id" in update_alias_version' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + - name: Update an alias version to explicit LATEST with 0 - idempotency (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + function_version: 0 + check_mode: True + register: update_alias_version + - name: Check changed + assert: + that: + - update_alias_version is not changed + + - name: Update an alias version to explicit LATEST with 0 - idempotecy + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + name: Testing + function_version: 0 + register: update_alias_version + - name: Check changed and returned values + assert: + that: + - update_alias_version is not changed + - '"alias_arn" in update_alias_version' + - update_alias_version.alias_arn.startswith(lambda_arn) + - update_alias_version.alias_arn.endswith("Testing") + - '"description" in update_alias_version' + - update_alias_version.description == "description 2" + - '"function_version" in update_alias_version' + - update_alias_version.function_version == "$LATEST" + - '"name" in update_alias_version' + - update_alias_version.name == "Testing" + - '"revision_id" in update_alias_version' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + # ============================================================== + # Creation of an alias with all options + - name: Create an alias with all options (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + description: 'Hello world' + name: stable + function_version: 1 + check_mode: True + register: create_alias + - name: Check changed + assert: + that: + - create_alias is changed + + - name: Create an alias with all options + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + description: 'Hello world' + name: stable + function_version: 1 + register: create_alias + - name: Check changed and returned values + assert: + that: + - create_alias is changed + - '"alias_arn" in create_alias' + - create_alias.alias_arn.startswith(lambda_arn) + - create_alias.alias_arn.endswith("stable") + - '"description" in create_alias' + - create_alias.description == "Hello world" + - '"function_version" in create_alias' + - create_alias.function_version == "1" + - '"name" in create_alias' + - create_alias.name == "stable" + - '"revision_id" in create_alias' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + - name: Create an alias with all options - idempotency (check mode) + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + description: 'Hello world' + name: stable + function_version: 1 + check_mode: True + register: create_alias + - name: Check not changed + assert: + that: + - create_alias is not changed + + - name: Create an alias wth all options - idempotecy + lambda_alias: + state: present + function_name: '{{ lambda_function_name }}' + description: 'Hello world' + name: stable + function_version: 1 + register: create_alias + - name: Check not changed + assert: + that: + - create_alias is not changed + - '"alias_arn" in create_alias' + - create_alias.alias_arn.startswith(lambda_arn) + - create_alias.alias_arn.endswith("stable") + - '"description" in create_alias' + - create_alias.description == "Hello world" + - '"function_version" in create_alias' + - create_alias.function_version == "1" + - '"name" in create_alias' + - create_alias.name == "stable" + - '"revision_id" in create_alias' + # The revision_id doesn't line up with the revision IDs of the versions + # It will change any time the alias is updated + + # ============================================================== + # Deletion of an alias + - name: Delete an alias (check mode) + lambda_alias: + state: absent + function_name: '{{ lambda_function_name }}' + name: Testing + check_mode: True + register: delete_alias + - name: Check changed + assert: + that: + - delete_alias is changed + + - name: Delete an alias + lambda_alias: + state: absent + function_name: '{{ lambda_function_name }}' + name: Testing + register: delete_alias + - name: Check changed + assert: + that: + - delete_alias is changed + + - name: Delete an alias - idempotency (check mode) + lambda_alias: + state: absent + function_name: '{{ lambda_function_name }}' + name: Testing + check_mode: True + register: delete_alias + - name: Check not changed + assert: + that: + - delete_alias is not changed + + - name: Delete an alias - idempotecy + lambda_alias: + state: absent + function_name: '{{ lambda_function_name }}' + name: Testing + register: delete_alias + - name: Check not changed + assert: + that: + - delete_alias is not changed + + # ============================================================== + # Cleanup + always: + - name: ensure function is absent at end of test + lambda: + name: '{{lambda_function_name}}' + state: absent + ignore_errors: true + - name: ensure role has been removed at end of test + iam_role: + name: '{{ lambda_role_name }}' + state: absent + delete_instance_profile: True + ignore_errors: true