ec2_group: fix regression for targets that are a list containing strings and lists (#45594)
* Fix targets that may be a list containing strings and lists which worked prior to 2.6.
* Add ec2_group integration tests for lists of nested targets
* changelog
* Add diff mode support for lists of targets containing strings and lists.
(cherry picked from commit d7ca3f2bd3
)
This commit is contained in:
parent
79cebbf933
commit
98e31e98c8
4 changed files with 268 additions and 4 deletions
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
bugfixes:
|
||||||
|
- ec2_group - Sanitize the ingress and egress rules before operating on them by flattening any lists
|
||||||
|
within lists describing the target CIDR(s) into a list of strings. Prior to Ansible 2.6 the ec2_group
|
||||||
|
module accepted a list of strings, a list of lists, or a combination of strings and lists within a list.
|
||||||
|
https://github.com/ansible/ansible/pull/45594
|
|
@ -293,6 +293,7 @@ owner_id:
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import itertools
|
import itertools
|
||||||
|
from copy import deepcopy
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from ansible.module_utils.aws.core import AnsibleAWSModule, is_boto3_error_code
|
from ansible.module_utils.aws.core import AnsibleAWSModule, is_boto3_error_code
|
||||||
|
@ -890,6 +891,7 @@ def get_diff_final_resource(client, module, security_group):
|
||||||
final_rules = []
|
final_rules = []
|
||||||
else:
|
else:
|
||||||
final_rules = list(security_group_rules)
|
final_rules = list(security_group_rules)
|
||||||
|
specified_rules = flatten_nested_targets(module, deepcopy(specified_rules))
|
||||||
for rule in specified_rules:
|
for rule in specified_rules:
|
||||||
format_rule = {
|
format_rule = {
|
||||||
'from_port': None, 'to_port': None, 'ip_protocol': rule.get('proto', 'tcp'),
|
'from_port': None, 'to_port': None, 'ip_protocol': rule.get('proto', 'tcp'),
|
||||||
|
@ -900,7 +902,7 @@ def get_diff_final_resource(client, module, security_group):
|
||||||
format_rule.pop('from_port')
|
format_rule.pop('from_port')
|
||||||
format_rule.pop('to_port')
|
format_rule.pop('to_port')
|
||||||
elif rule.get('ports'):
|
elif rule.get('ports'):
|
||||||
if rule.get('ports') and isinstance(rule.get('ports'), string_types):
|
if rule.get('ports') and (isinstance(rule['ports'], string_types) or isinstance(rule['ports'], int)):
|
||||||
rule['ports'] = [rule['ports']]
|
rule['ports'] = [rule['ports']]
|
||||||
for port in rule.get('ports'):
|
for port in rule.get('ports'):
|
||||||
if isinstance(port, string_types) and '-' in port:
|
if isinstance(port, string_types) and '-' in port:
|
||||||
|
@ -916,7 +918,9 @@ def get_diff_final_resource(client, module, security_group):
|
||||||
if rule.get('rule_desc'):
|
if rule.get('rule_desc'):
|
||||||
format_rule[rule_key] = [{source_type: rule[source_type], 'description': rule['rule_desc']}]
|
format_rule[rule_key] = [{source_type: rule[source_type], 'description': rule['rule_desc']}]
|
||||||
else:
|
else:
|
||||||
format_rule[rule_key] = [{source_type: rule[source_type]}]
|
if not isinstance(rule[source_type], list):
|
||||||
|
rule[source_type] = [rule[source_type]]
|
||||||
|
format_rule[rule_key] = [{source_type: target} for target in rule[source_type]]
|
||||||
if rule.get('group_id') or rule.get('group_name'):
|
if rule.get('group_id') or rule.get('group_name'):
|
||||||
rule_sg = camel_dict_to_snake_dict(group_exists(client, module, module.params['vpc_id'], rule.get('group_id'), rule.get('group_name'))[0])
|
rule_sg = camel_dict_to_snake_dict(group_exists(client, module, module.params['vpc_id'], rule.get('group_id'), rule.get('group_name'))[0])
|
||||||
format_rule['user_id_group_pairs'] = [{
|
format_rule['user_id_group_pairs'] = [{
|
||||||
|
@ -952,6 +956,27 @@ def get_diff_final_resource(client, module, security_group):
|
||||||
'vpc_id': security_group.get('vpc_id', module.params['vpc_id'])}
|
'vpc_id': security_group.get('vpc_id', module.params['vpc_id'])}
|
||||||
|
|
||||||
|
|
||||||
|
def flatten_nested_targets(module, rules):
|
||||||
|
def _flatten(targets):
|
||||||
|
for target in targets:
|
||||||
|
if isinstance(target, list):
|
||||||
|
for t in _flatten(target):
|
||||||
|
yield t
|
||||||
|
elif isinstance(target, string_types):
|
||||||
|
yield target
|
||||||
|
|
||||||
|
if rules is not None:
|
||||||
|
for rule in rules:
|
||||||
|
target_list_type = None
|
||||||
|
if isinstance(rule.get('cidr_ip'), list):
|
||||||
|
target_list_type = 'cidr_ip'
|
||||||
|
elif isinstance(rule.get('cidr_ipv6'), list):
|
||||||
|
target_list_type = 'cidr_ipv6'
|
||||||
|
if target_list_type is not None:
|
||||||
|
rule[target_list_type] = list(_flatten(rule[target_list_type]))
|
||||||
|
return rules
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
name=dict(),
|
name=dict(),
|
||||||
|
@ -977,8 +1002,10 @@ def main():
|
||||||
group_id = module.params['group_id']
|
group_id = module.params['group_id']
|
||||||
description = module.params['description']
|
description = module.params['description']
|
||||||
vpc_id = module.params['vpc_id']
|
vpc_id = module.params['vpc_id']
|
||||||
rules = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(module.params['rules'])))
|
rules = flatten_nested_targets(module, deepcopy(module.params['rules']))
|
||||||
rules_egress = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(module.params['rules_egress'])))
|
rules_egress = flatten_nested_targets(module, deepcopy(module.params['rules_egress']))
|
||||||
|
rules = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(rules)))
|
||||||
|
rules_egress = deduplicate_rules_args(rules_expand_sources(rules_expand_ports(rules_egress)))
|
||||||
state = module.params.get('state')
|
state = module.params.get('state')
|
||||||
purge_rules = module.params['purge_rules']
|
purge_rules = module.params['purge_rules']
|
||||||
purge_rules_egress = module.params['purge_rules_egress']
|
purge_rules_egress = module.params['purge_rules_egress']
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
- include: ./rule_group_create.yml
|
- include: ./rule_group_create.yml
|
||||||
- include: ./egress_tests.yml
|
- include: ./egress_tests.yml
|
||||||
- include: ./data_validation.yml
|
- include: ./data_validation.yml
|
||||||
|
- include: ./multi_nested_target.yml
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
- name: test state=absent (CHECK MODE)
|
- name: test state=absent (CHECK MODE)
|
||||||
|
|
230
test/integration/targets/ec2_group/tasks/multi_nested_target.yml
Normal file
230
test/integration/targets/ec2_group/tasks/multi_nested_target.yml
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
---
|
||||||
|
- 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: test state=present for multiple ipv6 and ipv4 targets (expected changed=true) (CHECK MODE)
|
||||||
|
ec2_group:
|
||||||
|
name: '{{ ec2_group_name }}'
|
||||||
|
description: '{{ ec2_group_description }}'
|
||||||
|
state: present
|
||||||
|
rules:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8182
|
||||||
|
to_port: 8182
|
||||||
|
cidr_ipv6:
|
||||||
|
- "64:ff9b::/96"
|
||||||
|
- ["2620::/32"]
|
||||||
|
- proto: "tcp"
|
||||||
|
ports: 5665
|
||||||
|
cidr_ip:
|
||||||
|
- 172.16.1.0/24
|
||||||
|
- 172.16.17.0/24
|
||||||
|
- ["10.0.0.0/24", "20.0.0.0/24"]
|
||||||
|
<<: *aws_connection_info
|
||||||
|
check_mode: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert state=present (expected changed=true)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'result.changed'
|
||||||
|
|
||||||
|
- name: test state=present for multiple ipv6 and ipv4 targets (expected changed=true)
|
||||||
|
ec2_group:
|
||||||
|
name: '{{ ec2_group_name }}'
|
||||||
|
description: '{{ ec2_group_description }}'
|
||||||
|
state: present
|
||||||
|
rules:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8182
|
||||||
|
to_port: 8182
|
||||||
|
cidr_ipv6:
|
||||||
|
- "64:ff9b::/96"
|
||||||
|
- ["2620::/32"]
|
||||||
|
- proto: "tcp"
|
||||||
|
ports: 5665
|
||||||
|
cidr_ip:
|
||||||
|
- 172.16.1.0/24
|
||||||
|
- 172.16.17.0/24
|
||||||
|
- ["10.0.0.0/24", "20.0.0.0/24"]
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert state=present (expected changed=true)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'result.changed'
|
||||||
|
- 'result.ip_permissions | length == 2'
|
||||||
|
- 'result.ip_permissions[0].ip_ranges | length == 4 or result.ip_permissions[1].ip_ranges | length == 4'
|
||||||
|
- 'result.ip_permissions[0].ipv6_ranges | length == 2 or result.ip_permissions[1].ipv6_ranges | length == 2'
|
||||||
|
|
||||||
|
- name: test state=present for multiple ipv6 and ipv4 targets (expected changed=false) (CHECK MODE)
|
||||||
|
ec2_group:
|
||||||
|
name: '{{ ec2_group_name }}'
|
||||||
|
description: '{{ ec2_group_description }}'
|
||||||
|
state: present
|
||||||
|
rules:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8182
|
||||||
|
to_port: 8182
|
||||||
|
cidr_ipv6:
|
||||||
|
- "64:ff9b::/96"
|
||||||
|
- ["2620::/32"]
|
||||||
|
- proto: "tcp"
|
||||||
|
ports: 5665
|
||||||
|
cidr_ip:
|
||||||
|
- 172.16.1.0/24
|
||||||
|
- 172.16.17.0/24
|
||||||
|
- ["10.0.0.0/24", "20.0.0.0/24"]
|
||||||
|
<<: *aws_connection_info
|
||||||
|
check_mode: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert state=present (expected changed=true)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'not result.changed'
|
||||||
|
|
||||||
|
- name: test state=present for multiple ipv6 and ipv4 targets (expected changed=false)
|
||||||
|
ec2_group:
|
||||||
|
name: '{{ ec2_group_name }}'
|
||||||
|
description: '{{ ec2_group_description }}'
|
||||||
|
state: present
|
||||||
|
rules:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8182
|
||||||
|
to_port: 8182
|
||||||
|
cidr_ipv6:
|
||||||
|
- "64:ff9b::/96"
|
||||||
|
- ["2620::/32"]
|
||||||
|
- proto: "tcp"
|
||||||
|
ports: 5665
|
||||||
|
cidr_ip:
|
||||||
|
- 172.16.1.0/24
|
||||||
|
- 172.16.17.0/24
|
||||||
|
- ["10.0.0.0/24", "20.0.0.0/24"]
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert state=present (expected changed=true)
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'not result.changed'
|
||||||
|
|
||||||
|
- name: test state=present purging a nested ipv4 target (expected changed=true) (CHECK MODE)
|
||||||
|
ec2_group:
|
||||||
|
name: '{{ ec2_group_name }}'
|
||||||
|
description: '{{ ec2_group_description }}'
|
||||||
|
state: present
|
||||||
|
rules:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8182
|
||||||
|
to_port: 8182
|
||||||
|
cidr_ipv6:
|
||||||
|
- "64:ff9b::/96"
|
||||||
|
- ["2620::/32"]
|
||||||
|
- proto: "tcp"
|
||||||
|
ports: 5665
|
||||||
|
cidr_ip:
|
||||||
|
- 172.16.1.0/24
|
||||||
|
- 172.16.17.0/24
|
||||||
|
- ["10.0.0.0/24"]
|
||||||
|
<<: *aws_connection_info
|
||||||
|
check_mode: true
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- result.changed
|
||||||
|
|
||||||
|
- name: test state=present purging a nested ipv4 target (expected changed=true)
|
||||||
|
ec2_group:
|
||||||
|
name: '{{ ec2_group_name }}'
|
||||||
|
description: '{{ ec2_group_description }}'
|
||||||
|
state: present
|
||||||
|
rules:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8182
|
||||||
|
to_port: 8182
|
||||||
|
cidr_ipv6:
|
||||||
|
- "64:ff9b::/96"
|
||||||
|
- ["2620::/32"]
|
||||||
|
- proto: "tcp"
|
||||||
|
ports: 5665
|
||||||
|
cidr_ip:
|
||||||
|
- 172.16.1.0/24
|
||||||
|
- 172.16.17.0/24
|
||||||
|
- ["10.0.0.0/24"]
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- result.changed
|
||||||
|
- 'result.ip_permissions[0].ip_ranges | length == 3 or result.ip_permissions[1].ip_ranges | length == 3'
|
||||||
|
- 'result.ip_permissions[0].ipv6_ranges | length == 2 or result.ip_permissions[1].ipv6_ranges | length == 2'
|
||||||
|
|
||||||
|
- name: test state=present with both associated ipv6 targets nested (expected changed=false)
|
||||||
|
ec2_group:
|
||||||
|
name: '{{ ec2_group_name }}'
|
||||||
|
description: '{{ ec2_group_description }}'
|
||||||
|
state: present
|
||||||
|
rules:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8182
|
||||||
|
to_port: 8182
|
||||||
|
cidr_ipv6:
|
||||||
|
- ["2620::/32", "64:ff9b::/96"]
|
||||||
|
- proto: "tcp"
|
||||||
|
ports: 5665
|
||||||
|
cidr_ip:
|
||||||
|
- 172.16.1.0/24
|
||||||
|
- 172.16.17.0/24
|
||||||
|
- ["10.0.0.0/24"]
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- not result.changed
|
||||||
|
|
||||||
|
- name: test state=present add another nested ipv6 target (expected changed=true)
|
||||||
|
ec2_group:
|
||||||
|
name: '{{ ec2_group_name }}'
|
||||||
|
description: '{{ ec2_group_description }}'
|
||||||
|
state: present
|
||||||
|
rules:
|
||||||
|
- proto: "tcp"
|
||||||
|
from_port: 8182
|
||||||
|
to_port: 8182
|
||||||
|
cidr_ipv6:
|
||||||
|
- ["2620::/32", "64:ff9b::/96"]
|
||||||
|
- ["2001:DB8:A0B:12F0::1/64"]
|
||||||
|
- proto: "tcp"
|
||||||
|
ports: 5665
|
||||||
|
cidr_ip:
|
||||||
|
- 172.16.1.0/24
|
||||||
|
- 172.16.17.0/24
|
||||||
|
- ["10.0.0.0/24"]
|
||||||
|
<<: *aws_connection_info
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- result.changed
|
||||||
|
- 'result.ip_permissions[0].ip_ranges | length == 3 or result.ip_permissions[1].ip_ranges | length == 3'
|
||||||
|
- 'result.ip_permissions[0].ipv6_ranges | length == 3 or result.ip_permissions[1].ipv6_ranges | length == 3'
|
||||||
|
|
||||||
|
- name: delete it
|
||||||
|
ec2_group:
|
||||||
|
name: '{{ ec2_group_name }}'
|
||||||
|
state: absent
|
||||||
|
<<: *aws_connection_info
|
Loading…
Reference in a new issue