ability to use lambda target in elb_target_group (#57394)

* enable elb_lambda_target test
pull/59831/head
Markus Bergholz 5 years ago committed by Jill R
parent e07c4f41d7
commit 196347ff32

@ -110,14 +110,15 @@ options:
target_type:
description:
- The type of target that you must specify when registering targets with this target group. The possible values are
C(instance) (targets are specified by instance ID) or C(ip) (targets are specified by IP address).
Note that you can't specify targets for a target group using both instance IDs and IP addresses.
C(instance) (targets are specified by instance ID), C(ip) (targets are specified by IP address) or C(lambda) (target is specified by ARN).
Note that you can't specify targets for a target group using more than one type. Target type lambda only accept one target. When more than
one target is specified, only the first one is used. All additional targets are ignored.
If the target type is ip, specify IP addresses from the subnets of the virtual private cloud (VPC) for the target
group, the RFC 1918 range (10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16), and the RFC 6598 range (100.64.0.0/10).
You can't specify publicly routable IP addresses.
required: false
default: instance
choices: ['instance', 'ip']
choices: ['instance', 'ip', 'lambda']
version_added: 2.5
targets:
description:
@ -212,6 +213,37 @@ EXAMPLES = '''
wait_timeout: 200
wait: True
# Using lambda as targets require that the target group
# itself is allow to invoke the lambda function.
# therefore you need first to create an empty target group
# to receive its arn, second, allow the target group
# to invoke the lamba function and third, add the target
# to the target group
- name: first, create empty target group
elb_target_group:
name: my-lambda-targetgroup
target_type: lambda
state: present
modify_targets: False
register: out
- name: second, allow invoke of the lambda
lambda_policy:
state: "{{ state | default('present') }}"
function_name: my-lambda-function
statement_id: someID
action: lambda:InvokeFunction
principal: elasticloadbalancing.amazonaws.com
source_arn: "{{ out.target_group_arn }}"
- name: third, add target
elb_target_group:
name: my-lambda-targetgroup
target_type: lambda
state: present
targets:
- Id: arn:aws:lambda:eu-central-1:123456789012:function:my-lambda-function
'''
RETURN = '''
@ -389,6 +421,7 @@ def create_or_update_target_group(connection, module):
new_target_group = False
params = dict()
params['Name'] = module.params.get("name")
if module.params.get("target_type") != "lambda":
params['Protocol'] = module.params.get("protocol").upper()
params['Port'] = module.params.get("port")
params['VpcId'] = module.params.get("vpc_id")
@ -505,21 +538,23 @@ def create_or_update_target_group(connection, module):
# Do we need to modify targets?
if module.params.get("modify_targets"):
# get list of current target instances. I can't see anything like a describe targets in the doco so
# describe_target_health seems to be the only way to get them
try:
current_targets = connection.describe_target_health(
TargetGroupArn=tg['TargetGroupArn'])
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Couldn't get target group health")
if module.params.get("targets"):
if module.params.get("target_type") != "lambda":
params['Targets'] = module.params.get("targets")
# Correct type of target ports
for target in params['Targets']:
target['Port'] = int(target.get('Port', module.params.get('port')))
# get list of current target instances. I can't see anything like a describe targets in the doco so
# describe_target_health seems to be the only way to get them
try:
current_targets = connection.describe_target_health(TargetGroupArn=tg['TargetGroupArn'])
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Couldn't get target group health")
current_instance_ids = []
for instance in current_targets['TargetHealthDescriptions']:
@ -566,11 +601,36 @@ def create_or_update_target_group(connection, module):
status_achieved, registered_instances = wait_for_status(connection, module, tg['TargetGroupArn'], instances_to_remove, 'unused')
if not status_achieved:
module.fail_json(msg='Error waiting for target deregistration - please check the AWS console')
# register lambda target
else:
try:
current_targets = connection.describe_target_health(TargetGroupArn=tg['TargetGroupArn'])
changed = False
target = module.params.get("targets")[0]
if len(current_targets["TargetHealthDescriptions"]) == 0:
changed = True
else:
for item in current_targets["TargetHealthDescriptions"]:
if target["Id"] != item["Target"]["Id"]:
changed = True
break # only one target is possible with lambda
if changed:
if target.get("Id"):
response = connection.register_targets(
TargetGroupArn=tg['TargetGroupArn'],
Targets=[
{
"Id": target['Id']
}
]
)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Couldn't get target health")
module.fail_json_aws(
e, msg="Couldn't register targets")
else:
if module.params.get("target_type") != "lambda":
current_instances = current_targets['TargetHealthDescriptions']
@ -589,6 +649,17 @@ def create_or_update_target_group(connection, module):
status_achieved, registered_instances = wait_for_status(connection, module, tg['TargetGroupArn'], instances_to_remove, 'unused')
if not status_achieved:
module.fail_json(msg='Error waiting for target deregistration - please check the AWS console')
# remove lambda targets
else:
changed = False
if current_targets["TargetHealthDescriptions"]:
changed = True
# only one target is possible with lambda
target_to_remove = current_targets["TargetHealthDescriptions"][0]["Target"]["Id"]
if changed:
connection.deregister_targets(
TargetGroupArn=tg['TargetGroupArn'], Targets=[{"Id": target_to_remove}])
else:
try:
connection.create_target_group(**params)
@ -600,6 +671,7 @@ def create_or_update_target_group(connection, module):
tg = get_target_group(connection, module)
if module.params.get("targets"):
if module.params.get("target_type") != "lambda":
params['Targets'] = module.params.get("targets")
try:
connection.register_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=params['Targets'])
@ -611,6 +683,22 @@ def create_or_update_target_group(connection, module):
if not status_achieved:
module.fail_json(msg='Error waiting for target registration to be healthy - please check the AWS console')
else:
try:
target = module.params.get("targets")[0]
response = connection.register_targets(
TargetGroupArn=tg['TargetGroupArn'],
Targets=[
{
"Id": target["Id"]
}
]
)
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(
e, msg="Couldn't register targets")
# Now set target group attributes
update_attributes = []
@ -712,7 +800,7 @@ def main():
state=dict(required=True, choices=['present', 'absent']),
successful_response_codes=dict(),
tags=dict(default={}, type='dict'),
target_type=dict(default='instance', choices=['instance', 'ip']),
target_type=dict(default='instance', choices=['instance', 'ip', 'lambda']),
targets=dict(type='list'),
unhealthy_threshold_count=dict(type='int'),
vpc_id=dict(),
@ -722,7 +810,11 @@ def main():
)
module = AnsibleAWSModule(argument_spec=argument_spec,
required_if=[['state', 'present', ['protocol', 'port', 'vpc_id']]])
required_if=[
['target_type', 'instance', ['protocol', 'port', 'vpc_id']],
['target_type', 'ip', ['protocol', 'port', 'vpc_id']],
]
)
connection = module.client('elbv2')

@ -3,4 +3,5 @@
environment: "{{ ansible_test.environment }}"
roles:
- elb_lambda_target
- elb_target

@ -0,0 +1,8 @@
import json
def lambda_handler(event, context):
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}

@ -0,0 +1,8 @@
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Principal": { "Service": "lambda.amazonaws.com" },
"Action": "sts:AssumeRole"
}
}

@ -0,0 +1,135 @@
---
- name: set up aws connection info
set_fact:
aws_connection_info: &aws_connection_info
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
security_token: "{{ security_token }}"
region: "{{ aws_region }}"
no_log: yes
- name: set up lambda as elb_target
block:
- name: create zip to deploy lambda code
archive:
path: "{{ role_path }}/files/ansible_lambda_target.py"
dest: /tmp/lambda.zip
format: zip
- name: "create or update service-role for lambda"
iam_role:
<<: *aws_connection_info
name: ansible_lambda_execution
assume_role_policy_document: "{{ lookup('file', role_path + '/files/assume-role.json') }}"
managed_policy:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
register: ROLE_ARN
- name: when it is to fast, the role is not usable.
pause:
minutes: 1
- name: deploy lambda.zip to ansible_lambda_target function
lambda:
<<: *aws_connection_info
name: "ansible_lambda_target"
state: present
zip_file: "/tmp/lambda.zip"
runtime: "python3.7"
role: "{{ ROLE_ARN.arn }}"
handler: "ansible_lambda_target.lambda_handler"
timeout: 30
register: lambda_function
retries: 3
delay: 15
until: lambda_function.changed
- name: create empty target group
elb_target_group:
<<: *aws_connection_info
name: ansible-lambda-targetgroup
target_type: lambda
state: present
modify_targets: False
register: elb_target_group
- name: tg is created, state must be changed
assert:
that:
- elb_target_group.changed
- name: allow elb to invoke the lambda function
lambda_policy:
<<: *aws_connection_info
state: present
function_name: ansible_lambda_target
version: "{{ lambda_function.configuration.version }}"
statement_id: elb1
action: lambda:InvokeFunction
principal: elasticloadbalancing.amazonaws.com
source_arn: "{{ elb_target_group.target_group_arn }}"
- name: add lambda to elb target
elb_target_group:
<<: *aws_connection_info
name: ansible-lambda-targetgroup
target_type: lambda
state: present
targets:
- Id: "{{ lambda_function.configuration.function_arn }}"
register: elb_target_group
- name: target is updated, state must be changed
assert:
that:
- elb_target_group.changed
- name: re-add lambda to elb target (idempotency)
elb_target_group:
<<: *aws_connection_info
name: ansible-lambda-targetgroup
target_type: lambda
state: present
targets:
- Id: "{{ lambda_function.configuration.function_arn }}"
register: elb_target_group
- name: target is still the same, state must not be changed (idempotency)
assert:
that:
- not elb_target_group.changed
- name: remove lambda target from target group
elb_target_group:
<<: *aws_connection_info
name: ansible-lambda-targetgroup
target_type: lambda
state: absent
targets: []
register: elb_target_group
- name: target is still the same, state must not be changed (idempotency)
assert:
that:
- elb_target_group.changed
always:
- name: remove elb target group
elb_target_group:
<<: *aws_connection_info
name: ansible-lambda-targetgroup
target_type: lambda
state: absent
- name: remove lambda function
lambda:
<<: *aws_connection_info
name: "ansible_lambda_target"
state: absent
- name: remove iam role for lambda
iam_role:
<<: *aws_connection_info
name: ansible_lambda_execution
state: absent
Loading…
Cancel
Save