ability to use lambda target in elb_target_group (#57394)
* enable elb_lambda_target test
This commit is contained in:
parent
e07c4f41d7
commit
196347ff32
5 changed files with 332 additions and 88 deletions
|
@ -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,9 +421,10 @@ def create_or_update_target_group(connection, module):
|
|||
new_target_group = False
|
||||
params = dict()
|
||||
params['Name'] = module.params.get("name")
|
||||
params['Protocol'] = module.params.get("protocol").upper()
|
||||
params['Port'] = module.params.get("port")
|
||||
params['VpcId'] = module.params.get("vpc_id")
|
||||
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")
|
||||
tags = module.params.get("tags")
|
||||
purge_tags = module.params.get("purge_tags")
|
||||
deregistration_delay_timeout = module.params.get("deregistration_delay_timeout")
|
||||
|
@ -505,90 +538,128 @@ 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"):
|
||||
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')))
|
||||
if module.params.get("target_type") != "lambda":
|
||||
params['Targets'] = module.params.get("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")
|
||||
|
||||
current_instance_ids = []
|
||||
|
||||
for instance in current_targets['TargetHealthDescriptions']:
|
||||
current_instance_ids.append(instance['Target']['Id'])
|
||||
|
||||
new_instance_ids = []
|
||||
for instance in params['Targets']:
|
||||
new_instance_ids.append(instance['Id'])
|
||||
|
||||
add_instances = set(new_instance_ids) - set(current_instance_ids)
|
||||
|
||||
if add_instances:
|
||||
instances_to_add = []
|
||||
# Correct type of target ports
|
||||
for target in params['Targets']:
|
||||
if target['Id'] in add_instances:
|
||||
instances_to_add.append({'Id': target['Id'], 'Port': target['Port']})
|
||||
target['Port'] = int(target.get('Port', module.params.get('port')))
|
||||
|
||||
changed = True
|
||||
current_instance_ids = []
|
||||
|
||||
for instance in current_targets['TargetHealthDescriptions']:
|
||||
current_instance_ids.append(instance['Target']['Id'])
|
||||
|
||||
new_instance_ids = []
|
||||
for instance in params['Targets']:
|
||||
new_instance_ids.append(instance['Id'])
|
||||
|
||||
add_instances = set(new_instance_ids) - set(current_instance_ids)
|
||||
|
||||
if add_instances:
|
||||
instances_to_add = []
|
||||
for target in params['Targets']:
|
||||
if target['Id'] in add_instances:
|
||||
instances_to_add.append({'Id': target['Id'], 'Port': target['Port']})
|
||||
|
||||
changed = True
|
||||
try:
|
||||
connection.register_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_add)
|
||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't register targets")
|
||||
|
||||
if module.params.get("wait"):
|
||||
status_achieved, registered_instances = wait_for_status(connection, module, tg['TargetGroupArn'], instances_to_add, 'healthy')
|
||||
if not status_achieved:
|
||||
module.fail_json(msg='Error waiting for target registration to be healthy - please check the AWS console')
|
||||
|
||||
remove_instances = set(current_instance_ids) - set(new_instance_ids)
|
||||
|
||||
if remove_instances:
|
||||
instances_to_remove = []
|
||||
for target in current_targets['TargetHealthDescriptions']:
|
||||
if target['Target']['Id'] in remove_instances:
|
||||
instances_to_remove.append({'Id': target['Target']['Id'], 'Port': target['Target']['Port']})
|
||||
|
||||
changed = True
|
||||
try:
|
||||
connection.deregister_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_remove)
|
||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't remove targets")
|
||||
|
||||
if module.params.get("wait"):
|
||||
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:
|
||||
connection.register_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_add)
|
||||
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 register targets")
|
||||
module.fail_json_aws(
|
||||
e, msg="Couldn't register targets")
|
||||
else:
|
||||
if module.params.get("target_type") != "lambda":
|
||||
|
||||
if module.params.get("wait"):
|
||||
status_achieved, registered_instances = wait_for_status(connection, module, tg['TargetGroupArn'], instances_to_add, 'healthy')
|
||||
if not status_achieved:
|
||||
module.fail_json(msg='Error waiting for target registration to be healthy - please check the AWS console')
|
||||
current_instances = current_targets['TargetHealthDescriptions']
|
||||
|
||||
remove_instances = set(current_instance_ids) - set(new_instance_ids)
|
||||
|
||||
if remove_instances:
|
||||
instances_to_remove = []
|
||||
for target in current_targets['TargetHealthDescriptions']:
|
||||
if target['Target']['Id'] in remove_instances:
|
||||
if current_instances:
|
||||
instances_to_remove = []
|
||||
for target in current_targets['TargetHealthDescriptions']:
|
||||
instances_to_remove.append({'Id': target['Target']['Id'], 'Port': target['Target']['Port']})
|
||||
|
||||
changed = True
|
||||
try:
|
||||
connection.deregister_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_remove)
|
||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't remove targets")
|
||||
changed = True
|
||||
try:
|
||||
connection.deregister_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_remove)
|
||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't remove targets")
|
||||
|
||||
if module.params.get("wait"):
|
||||
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')
|
||||
else:
|
||||
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 health")
|
||||
if module.params.get("wait"):
|
||||
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')
|
||||
|
||||
current_instances = current_targets['TargetHealthDescriptions']
|
||||
|
||||
if current_instances:
|
||||
instances_to_remove = []
|
||||
for target in current_targets['TargetHealthDescriptions']:
|
||||
instances_to_remove.append({'Id': target['Target']['Id'], 'Port': target['Target']['Port']})
|
||||
|
||||
changed = True
|
||||
try:
|
||||
connection.deregister_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=instances_to_remove)
|
||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't remove targets")
|
||||
|
||||
if module.params.get("wait"):
|
||||
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,16 +671,33 @@ def create_or_update_target_group(connection, module):
|
|||
tg = get_target_group(connection, module)
|
||||
|
||||
if module.params.get("targets"):
|
||||
params['Targets'] = module.params.get("targets")
|
||||
try:
|
||||
connection.register_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=params['Targets'])
|
||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't register targets")
|
||||
if module.params.get("target_type") != "lambda":
|
||||
params['Targets'] = module.params.get("targets")
|
||||
try:
|
||||
connection.register_targets(TargetGroupArn=tg['TargetGroupArn'], Targets=params['Targets'])
|
||||
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't register targets")
|
||||
|
||||
if module.params.get("wait"):
|
||||
status_achieved, registered_instances = wait_for_status(connection, module, tg['TargetGroupArn'], params['Targets'], 'healthy')
|
||||
if not status_achieved:
|
||||
module.fail_json(msg='Error waiting for target registration to be healthy - please check the AWS console')
|
||||
if module.params.get("wait"):
|
||||
status_achieved, registered_instances = wait_for_status(connection, module, tg['TargetGroupArn'], params['Targets'], 'healthy')
|
||||
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…
Reference in a new issue