Browse Source

keep merged files until release

master
Markus Bergholz 3 months ago
parent
commit
0ab0f01b88
3 changed files with 1084 additions and 1 deletions
  1. +2
    -1
      README.md
  2. +828
    -0
      elb_target_group.py
  3. +254
    -0
      gitlab_project_variable.py

+ 2
- 1
README.md View File

@@ -1,6 +1,7 @@
# library

my ansible modules which are stock in pull request
my ansible modules which are stock in pull request
I keept the files here until the ansible version is released

# working on


+ 828
- 0
elb_target_group.py View File

@@ -0,0 +1,828 @@
#!/usr/bin/python
# Copyright: Ansible Project
# 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


ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}

DOCUMENTATION = '''
---
module: elb_target_group
short_description: Manage a target group for an Application or Network load balancer
description:
- Manage an AWS Elastic Load Balancer target group. See
U(https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html) or
U(https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html) for details.
version_added: "2.4"
requirements: [ boto3 ]
author: "Rob White (@wimnat)"
options:
deregistration_delay_timeout:
description:
- The amount time for Elastic Load Balancing to wait before changing the state of a deregistering target from draining to unused.
The range is 0-3600 seconds.
health_check_protocol:
description:
- The protocol the load balancer uses when performing health checks on targets.
required: false
choices: [ 'http', 'https', 'tcp' ]
health_check_port:
description:
- The port the load balancer uses when performing health checks on targets.
Can be set to 'traffic-port' to match target port.
required: false
default: "The port on which each target receives traffic from the load balancer."
health_check_path:
description:
- The ping path that is the destination on the targets for health checks. The path must be defined in order to set a health check.
required: false
health_check_interval:
description:
- The approximate amount of time, in seconds, between health checks of an individual target.
required: false
health_check_timeout:
description:
- The amount of time, in seconds, during which no response from a target means a failed health check.
required: false
healthy_threshold_count:
description:
- The number of consecutive health checks successes required before considering an unhealthy target healthy.
required: false
modify_targets:
description:
- Whether or not to alter existing targets in the group to match what is passed with the module
required: false
default: yes
type: bool
name:
description:
- The name of the target group.
required: true
port:
description:
- The port on which the targets receive traffic. This port is used unless you specify a port override when registering the target. Required if
I(state) is C(present).
required: false
protocol:
description:
- The protocol to use for routing traffic to the targets. Required when I(state) is C(present).
required: false
choices: [ 'http', 'https', 'tcp' ]
purge_tags:
description:
- If yes, existing tags will be purged from the resource to match exactly what is defined by I(tags) parameter. If the tag parameter is not set then
tags will not be modified.
required: false
default: yes
type: bool
state:
description:
- Create or destroy the target group.
required: true
choices: [ 'present', 'absent' ]
stickiness_enabled:
description:
- Indicates whether sticky sessions are enabled.
type: bool
stickiness_lb_cookie_duration:
description:
- The time period, in seconds, during which requests from a client should be routed to the same target. After this time period expires, the load
balancer-generated cookie is considered stale. The range is 1 second to 1 week (604800 seconds).
stickiness_type:
description:
- The type of sticky sessions. The possible value is lb_cookie.
default: lb_cookie
successful_response_codes:
description:
- The HTTP codes to use when checking for a successful response from a target.
- Accepts multiple values (for example, "200,202") or a range of values (for example, "200-299").
- Requires the I(health_check_protocol) parameter to be set.
required: false
tags:
description:
- A dictionary of one or more tags to assign to the target group.
required: false
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), 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', 'lambda']
version_added: 2.5
targets:
description:
- A list of targets to assign to the target group. This parameter defaults to an empty list. Unless you set the 'modify_targets' parameter then
all existing targets will be removed from the group. The list should be an Id and a Port parameter. See the Examples for detail.
required: false
unhealthy_threshold_count:
description:
- The number of consecutive health check failures required before considering a target unhealthy.
required: false
vpc_id:
description:
- The identifier of the virtual private cloud (VPC). Required when I(state) is C(present).
required: false
wait:
description:
- Whether or not to wait for the target group.
type: bool
default: false
version_added: "2.4"
wait_timeout:
description:
- The time to wait for the target group.
default: 200
version_added: "2.4"
extends_documentation_fragment:
- aws
- ec2
notes:
- Once a target group has been created, only its health check can then be modified using subsequent calls
'''

EXAMPLES = '''
# Note: These examples do not set authentication details, see the AWS Guide for details.

# Create a target group with a default health check
- elb_target_group:
name: mytargetgroup
protocol: http
port: 80
vpc_id: vpc-01234567
state: present

# Modify the target group with a custom health check
- elb_target_group:
name: mytargetgroup
protocol: http
port: 80
vpc_id: vpc-01234567
health_check_path: /
successful_response_codes: "200,250-260"
state: present

# Delete a target group
- elb_target_group:
name: mytargetgroup
state: absent

# Create a target group with instance targets
- elb_target_group:
name: mytargetgroup
protocol: http
port: 81
vpc_id: vpc-01234567
health_check_path: /
successful_response_codes: "200,250-260"
targets:
- Id: i-01234567
Port: 80
- Id: i-98765432
Port: 80
state: present
wait_timeout: 200
wait: True

# Create a target group with IP address targets
- elb_target_group:
name: mytargetgroup
protocol: http
port: 81
vpc_id: vpc-01234567
health_check_path: /
successful_response_codes: "200,250-260"
target_type: ip
targets:
- Id: 10.0.0.10
Port: 80
AvailabilityZone: all
- Id: 10.0.0.20
Port: 80
state: present
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 = '''
deregistration_delay_timeout_seconds:
description: The amount time for Elastic Load Balancing to wait before changing the state of a deregistering target from draining to unused.
returned: when state present
type: int
sample: 300
health_check_interval_seconds:
description: The approximate amount of time, in seconds, between health checks of an individual target.
returned: when state present
type: int
sample: 30
health_check_path:
description: The destination for the health check request.
returned: when state present
type: str
sample: /index.html
health_check_port:
description: The port to use to connect with the target.
returned: when state present
type: str
sample: traffic-port
health_check_protocol:
description: The protocol to use to connect with the target.
returned: when state present
type: str
sample: HTTP
health_check_timeout_seconds:
description: The amount of time, in seconds, during which no response means a failed health check.
returned: when state present
type: int
sample: 5
healthy_threshold_count:
description: The number of consecutive health checks successes required before considering an unhealthy target healthy.
returned: when state present
type: int
sample: 5
load_balancer_arns:
description: The Amazon Resource Names (ARN) of the load balancers that route traffic to this target group.
returned: when state present
type: list
sample: []
matcher:
description: The HTTP codes to use when checking for a successful response from a target.
returned: when state present
type: dict
sample: {
"http_code": "200"
}
port:
description: The port on which the targets are listening.
returned: when state present
type: int
sample: 80
protocol:
description: The protocol to use for routing traffic to the targets.
returned: when state present
type: str
sample: HTTP
stickiness_enabled:
description: Indicates whether sticky sessions are enabled.
returned: when state present
type: bool
sample: true
stickiness_lb_cookie_duration_seconds:
description: The time period, in seconds, during which requests from a client should be routed to the same target.
returned: when state present
type: int
sample: 86400
stickiness_type:
description: The type of sticky sessions.
returned: when state present
type: str
sample: lb_cookie
tags:
description: The tags attached to the target group.
returned: when state present
type: dict
sample: "{
'Tag': 'Example'
}"
target_group_arn:
description: The Amazon Resource Name (ARN) of the target group.
returned: when state present
type: str
sample: "arn:aws:elasticloadbalancing:ap-southeast-2:01234567890:targetgroup/mytargetgroup/aabbccddee0044332211"
target_group_name:
description: The name of the target group.
returned: when state present
type: str
sample: mytargetgroup
unhealthy_threshold_count:
description: The number of consecutive health check failures required before considering the target unhealthy.
returned: when state present
type: int
sample: 2
vpc_id:
description: The ID of the VPC for the targets.
returned: when state present
type: str
sample: vpc-0123456
'''

import time

try:
import botocore
except ImportError:
pass # handled by AnsibleAWSModule

from ansible.module_utils.aws.core import AnsibleAWSModule
from ansible.module_utils.ec2 import (camel_dict_to_snake_dict, boto3_tag_list_to_ansible_dict, ec2_argument_spec,
compare_aws_tags, ansible_dict_to_boto3_tag_list)
from distutils.version import LooseVersion


def get_tg_attributes(connection, module, tg_arn):
try:
tg_attributes = boto3_tag_list_to_ansible_dict(connection.describe_target_group_attributes(TargetGroupArn=tg_arn)['Attributes'])
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Couldn't get target group attributes")

# Replace '.' with '_' in attribute key names to make it more Ansibley
return dict((k.replace('.', '_'), v) for k, v in tg_attributes.items())


def get_target_group_tags(connection, module, target_group_arn):
try:
return connection.describe_tags(ResourceArns=[target_group_arn])['TagDescriptions'][0]['Tags']
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Couldn't get target group tags")


def get_target_group(connection, module):
try:
target_group_paginator = connection.get_paginator('describe_target_groups')
return (target_group_paginator.paginate(Names=[module.params.get("name")]).build_full_result())['TargetGroups'][0]
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
if e.response['Error']['Code'] == 'TargetGroupNotFound':
return None
else:
module.fail_json_aws(e, msg="Couldn't get target group")


def wait_for_status(connection, module, target_group_arn, targets, status):
polling_increment_secs = 5
max_retries = (module.params.get('wait_timeout') // polling_increment_secs)
status_achieved = False

for x in range(0, max_retries):
try:
response = connection.describe_target_health(TargetGroupArn=target_group_arn, Targets=targets)
if response['TargetHealthDescriptions'][0]['TargetHealth']['State'] == status:
status_achieved = True
break
else:
time.sleep(polling_increment_secs)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Couldn't describe target health")

result = response
return status_achieved, result


def fail_if_ip_target_type_not_supported(module):
if LooseVersion(botocore.__version__) < LooseVersion('1.7.2'):
module.fail_json(msg="target_type ip requires botocore version 1.7.2 or later. Version %s is installed" %
botocore.__version__)


def create_or_update_target_group(connection, module):

changed = False
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")
tags = module.params.get("tags")
purge_tags = module.params.get("purge_tags")
deregistration_delay_timeout = module.params.get("deregistration_delay_timeout")
stickiness_enabled = module.params.get("stickiness_enabled")
stickiness_lb_cookie_duration = module.params.get("stickiness_lb_cookie_duration")
stickiness_type = module.params.get("stickiness_type")

health_option_keys = [
"health_check_path", "health_check_protocol", "health_check_interval", "health_check_timeout",
"healthy_threshold_count", "unhealthy_threshold_count", "successful_response_codes"
]
health_options = any([module.params[health_option_key] is not None for health_option_key in health_option_keys])

# Set health check if anything set
if health_options:

if module.params.get("health_check_protocol") is not None:
params['HealthCheckProtocol'] = module.params.get("health_check_protocol").upper()

if module.params.get("health_check_port") is not None:
params['HealthCheckPort'] = module.params.get("health_check_port")

if module.params.get("health_check_interval") is not None:
params['HealthCheckIntervalSeconds'] = module.params.get("health_check_interval")

if module.params.get("health_check_timeout") is not None:
params['HealthCheckTimeoutSeconds'] = module.params.get("health_check_timeout")

if module.params.get("healthy_threshold_count") is not None:
params['HealthyThresholdCount'] = module.params.get("healthy_threshold_count")

if module.params.get("unhealthy_threshold_count") is not None:
params['UnhealthyThresholdCount'] = module.params.get("unhealthy_threshold_count")

# Only need to check response code and path for http(s) health checks
if module.params.get("health_check_protocol") is not None and module.params.get("health_check_protocol").upper() != 'TCP':

if module.params.get("health_check_path") is not None:
params['HealthCheckPath'] = module.params.get("health_check_path")

if module.params.get("successful_response_codes") is not None:
params['Matcher'] = {}
params['Matcher']['HttpCode'] = module.params.get("successful_response_codes")

# Get target type
if module.params.get("target_type") is not None:
params['TargetType'] = module.params.get("target_type")
if params['TargetType'] == 'ip':
fail_if_ip_target_type_not_supported(module)

# Get target group
tg = get_target_group(connection, module)

if tg:
diffs = [param for param in ('Port', 'Protocol', 'VpcId')
if tg.get(param) != params.get(param)]
if diffs:
module.fail_json(msg="Cannot modify %s parameter(s) for a target group" %
", ".join(diffs))
# Target group exists so check health check parameters match what has been passed
health_check_params = dict()

# Modify health check if anything set
if health_options:

# Health check protocol
if 'HealthCheckProtocol' in params and tg['HealthCheckProtocol'] != params['HealthCheckProtocol']:
health_check_params['HealthCheckProtocol'] = params['HealthCheckProtocol']

# Health check port
if 'HealthCheckPort' in params and tg['HealthCheckPort'] != params['HealthCheckPort']:
health_check_params['HealthCheckPort'] = params['HealthCheckPort']

# Health check interval
if 'HealthCheckIntervalSeconds' in params and tg['HealthCheckIntervalSeconds'] != params['HealthCheckIntervalSeconds']:
health_check_params['HealthCheckIntervalSeconds'] = params['HealthCheckIntervalSeconds']

# Health check timeout
if 'HealthCheckTimeoutSeconds' in params and tg['HealthCheckTimeoutSeconds'] != params['HealthCheckTimeoutSeconds']:
health_check_params['HealthCheckTimeoutSeconds'] = params['HealthCheckTimeoutSeconds']

# Healthy threshold
if 'HealthyThresholdCount' in params and tg['HealthyThresholdCount'] != params['HealthyThresholdCount']:
health_check_params['HealthyThresholdCount'] = params['HealthyThresholdCount']

# Unhealthy threshold
if 'UnhealthyThresholdCount' in params and tg['UnhealthyThresholdCount'] != params['UnhealthyThresholdCount']:
health_check_params['UnhealthyThresholdCount'] = params['UnhealthyThresholdCount']

# Only need to check response code and path for http(s) health checks
if tg['HealthCheckProtocol'] != 'TCP':
# Health check path
if 'HealthCheckPath'in params and tg['HealthCheckPath'] != params['HealthCheckPath']:
health_check_params['HealthCheckPath'] = params['HealthCheckPath']

# Matcher (successful response codes)
# TODO: required and here?
if 'Matcher' in params:
current_matcher_list = tg['Matcher']['HttpCode'].split(',')
requested_matcher_list = params['Matcher']['HttpCode'].split(',')
if set(current_matcher_list) != set(requested_matcher_list):
health_check_params['Matcher'] = {}
health_check_params['Matcher']['HttpCode'] = ','.join(requested_matcher_list)

try:
if health_check_params:
connection.modify_target_group(TargetGroupArn=tg['TargetGroupArn'], **health_check_params)
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Couldn't update target group")

# 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')))

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:
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")
else:
if module.params.get("target_type") != "lambda":

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)
changed = True
new_target_group = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Couldn't create target group")

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'])
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')

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 = []

# Get current attributes
current_tg_attributes = get_tg_attributes(connection, module, tg['TargetGroupArn'])

if deregistration_delay_timeout is not None:
if str(deregistration_delay_timeout) != current_tg_attributes['deregistration_delay_timeout_seconds']:
update_attributes.append({'Key': 'deregistration_delay.timeout_seconds', 'Value': str(deregistration_delay_timeout)})
if stickiness_enabled is not None:
if stickiness_enabled and current_tg_attributes['stickiness_enabled'] != "true":
update_attributes.append({'Key': 'stickiness.enabled', 'Value': 'true'})
if stickiness_lb_cookie_duration is not None:
if str(stickiness_lb_cookie_duration) != current_tg_attributes['stickiness_lb_cookie_duration_seconds']:
update_attributes.append({'Key': 'stickiness.lb_cookie.duration_seconds', 'Value': str(stickiness_lb_cookie_duration)})
if stickiness_type is not None and "stickiness_type" in current_tg_attributes:
if stickiness_type != current_tg_attributes['stickiness_type']:
update_attributes.append({'Key': 'stickiness.type', 'Value': stickiness_type})

if update_attributes:
try:
connection.modify_target_group_attributes(TargetGroupArn=tg['TargetGroupArn'], Attributes=update_attributes)
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
# Something went wrong setting attributes. If this target group was created during this task, delete it to leave a consistent state
if new_target_group:
connection.delete_target_group(TargetGroupArn=tg['TargetGroupArn'])
module.fail_json_aws(e, msg="Couldn't delete target group")

# Tags - only need to play with tags if tags parameter has been set to something
if tags:
# Get tags
current_tags = get_target_group_tags(connection, module, tg['TargetGroupArn'])

# Delete necessary tags
tags_need_modify, tags_to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(current_tags), tags, purge_tags)
if tags_to_delete:
try:
connection.remove_tags(ResourceArns=[tg['TargetGroupArn']], TagKeys=tags_to_delete)
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Couldn't delete tags from target group")
changed = True

# Add/update tags
if tags_need_modify:
try:
connection.add_tags(ResourceArns=[tg['TargetGroupArn']], Tags=ansible_dict_to_boto3_tag_list(tags_need_modify))
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Couldn't add tags to target group")
changed = True

# Get the target group again
tg = get_target_group(connection, module)

# Get the target group attributes again
tg.update(get_tg_attributes(connection, module, tg['TargetGroupArn']))

# Convert tg to snake_case
snaked_tg = camel_dict_to_snake_dict(tg)

snaked_tg['tags'] = boto3_tag_list_to_ansible_dict(get_target_group_tags(connection, module, tg['TargetGroupArn']))

module.exit_json(changed=changed, **snaked_tg)


def delete_target_group(connection, module):
changed = False
tg = get_target_group(connection, module)

if tg:
try:
connection.delete_target_group(TargetGroupArn=tg['TargetGroupArn'])
changed = True
except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
module.fail_json_aws(e, msg="Couldn't delete target group")

module.exit_json(changed=changed)


def main():
argument_spec = ec2_argument_spec()
argument_spec.update(
dict(
deregistration_delay_timeout=dict(type='int'),
health_check_protocol=dict(choices=['http', 'https', 'tcp', 'HTTP', 'HTTPS', 'TCP']),
health_check_port=dict(),
health_check_path=dict(),
health_check_interval=dict(type='int'),
health_check_timeout=dict(type='int'),
healthy_threshold_count=dict(type='int'),
modify_targets=dict(default=True, type='bool'),
name=dict(required=True),
port=dict(type='int'),
protocol=dict(choices=['http', 'https', 'tcp', 'HTTP', 'HTTPS', 'TCP']),
purge_tags=dict(default=True, type='bool'),
stickiness_enabled=dict(type='bool'),
stickiness_type=dict(default='lb_cookie'),
stickiness_lb_cookie_duration=dict(type='int'),
state=dict(required=True, choices=['present', 'absent']),
successful_response_codes=dict(),
tags=dict(default={}, type='dict'),
target_type=dict(default='instance', choices=['instance', 'ip', 'lambda']),
targets=dict(type='list'),
unhealthy_threshold_count=dict(type='int'),
vpc_id=dict(),
wait_timeout=dict(type='int', default=200),
wait=dict(type='bool', default=False)
)
)

module = AnsibleAWSModule(argument_spec=argument_spec,
required_if=[
['target_type', 'instance', ['protocol', 'port', 'vpc_id']],
['target_type', 'ip', ['protocol', 'port', 'vpc_id']],
]
)

connection = module.client('elbv2')

if module.params.get('state') == 'present':
create_or_update_target_group(connection, module)
else:
delete_target_group(connection, module)


if __name__ == '__main__':
main()

+ 254
- 0
gitlab_project_variable.py View File

@@ -0,0 +1,254 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2019, Markus Bergholz (markuman@gmail.com)
# 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

ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}


DOCUMENTATION = '''
module: gitlab_project_variable
short_description: Creates/updates/deletes GitLab Projects Variables
description:
- When a project variable does not exist, it will be created.
- When a project variable does exist, its value will be updated when the values are different.
- Variables which are untouched in the playbook, but are not untouched in the GitLab project,
they stay untouched (I(purge) is C(false)) or will be deleted (I(purge) is C(true)).
version_added: "2.9"
author:
- "Markus Bergholz (@markuman)"
requirements:
- python >= 2.7
- python-gitlab python module
extends_documentation_fragment:
- auth_basic
options:
state:
description:
- Create or delete project variable.
- Possible values are present and absent.
default: present
type: str
choices: ["present", "absent"]
api_token:
description:
- GitLab access token with API permissions.
required: true
type: str
project:
description:
- The path and name of the project.
required: true
type: str
purge:
description:
- When set to true, all variables which are not untoucheded in the task will be deleted.
default: false
type: bool
vars:
description:
- A list of key value pairs.
default: {}
type: dict
'''


EXAMPLES = '''
- name: Set or update some CI/CD variables
gitlab_project_variable:
api_url: https://gitlab.com
api_token: secret_access_token
project: markuman/dotfiles
purge: false
vars:
ACCESS_KEY_ID: abc123
SECRET_ACCESS_KEY: 321cba

- name: Delete one variable
gitlab_project_variable:
api_url: https://gitlab.com
api_token: secret_access_token
project: markuman/dotfiles
state: absent
vars:
ACCESS_KEY_ID: abc123
'''

RETURN = '''
project_variable:
description: Four lists of the variablenames which were added, updated, removed or exist.
returned: always
type: dict
contains:
added:
description: A list of variables which were created.
returned: always
type: list
sample: "['ACCESS_KEY_ID', 'SECRET_ACCESS_KEY']"
untouched:
description: A list of variables which exist.
returned: always
type: list
sample: "['ACCESS_KEY_ID', 'SECRET_ACCESS_KEY']"
removed:
description: A list of variables which were deleted.
returned: always
type: list
sample: "['ACCESS_KEY_ID', 'SECRET_ACCESS_KEY']"
updated:
description: A list of variables whose values were changed.
returned: always
type: list
sample: "['ACCESS_KEY_ID', 'SECRET_ACCESS_KEY']"
'''

import traceback

from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils._text import to_native
from ansible.module_utils.api import basic_auth_argument_spec


GITLAB_IMP_ERR = None
try:
import gitlab
HAS_GITLAB_PACKAGE = True
except Exception:
GITLAB_IMP_ERR = traceback.format_exc()
HAS_GITLAB_PACKAGE = False


class GitlabProjectVariables(object):

def __init__(self, module, gitlab_instance):
self.repo = gitlab_instance
self.project = self.get_project(module.params['project'])
self._module = module

def get_project(self, project_name):
return self.repo.projects.get(project_name)

def list_all_project_variables(self):
return self.project.variables.list()

def create_variable(self, key, value):
if self._module.check_mode:
return
return self.project.variables.create({"key": key, "value": value})

def update_variable(self, var, value):
if var.value == value:
return False
if self._module.check_mode:
return True
var.value = value
var.save()
return True

def delete_variable(self, key):
if self._module.check_mode:
return
return self.project.variables.delete(key)


def native_python_main(this_gitlab, purge, var_list, state):

change = False
return_value = dict(added=list(), updated=list(), removed=list(), untouched=list())

gitlab_keys = this_gitlab.list_all_project_variables()
existing_variables = [x.get_id() for x in gitlab_keys]

for key in var_list:
if key in existing_variables:
index = existing_variables.index(key)
existing_variables[index] = None

if state == 'present':
single_change = this_gitlab.update_variable(
gitlab_keys[index], var_list[key])
change = single_change or change
if single_change:
return_value['updated'].append(key)
else:
return_value['untouched'].append(key)

elif state == 'absent':
this_gitlab.delete_variable(key)
change = True
return_value['removed'].append(key)

elif key not in existing_variables and state == 'present':
this_gitlab.create_variable(key, var_list[key])
change = True
return_value['added'].append(key)

existing_variables = list(filter(None, existing_variables))
if purge:
for item in existing_variables:
this_gitlab.delete_variable(item)
change = True
return_value['removed'].append(item)
else:
return_value['untouched'].extend(existing_variables)

return change, return_value


def main():
argument_spec = basic_auth_argument_spec()
argument_spec.update(
api_token=dict(type='str', required=True, no_log=True),
project=dict(type='str', required=True),
purge=dict(type='bool', required=False, default=False),
vars=dict(type='dict', required=False, default=dict()),
state=dict(type='str', default="present", choices=["absent", "present"])
)

module = AnsibleModule(
argument_spec=argument_spec,
mutually_exclusive=[
['api_username', 'api_token'],
['api_password', 'api_token'],
],
required_together=[
['api_username', 'api_password'],
],
required_one_of=[
['api_username', 'api_token']
],
supports_check_mode=True
)

api_url = module.params['api_url']
gitlab_token = module.params['api_token']
purge = module.params['purge']
var_list = module.params['vars']
state = module.params['state']

if not HAS_GITLAB_PACKAGE:
module.fail_json(msg=missing_required_lib("python-gitlab"), exception=GITLAB_IMP_ERR)

try:
gitlab_instance = gitlab.Gitlab(url=api_url, private_token=gitlab_token)
gitlab_instance.auth()
except (gitlab.exceptions.GitlabAuthenticationError, gitlab.exceptions.GitlabGetError) as e:
module.fail_json(msg="Failed to connect to GitLab server: %s" % to_native(e))
except (gitlab.exceptions.GitlabHttpError) as e:
module.fail_json(msg="Failed to connect to Gitlab server: %s. \
Gitlab remove Session API now that private tokens are removed from user API endpoints since version 10.2" % to_native(e))

this_gitlab = GitlabProjectVariables(module=module, gitlab_instance=gitlab_instance)

change, return_value = native_python_main(this_gitlab, purge, var_list, state)

module.exit_json(changed=change, project_variable=return_value)


if __name__ == '__main__':
main()

Loading…
Cancel
Save