Repository: ansible/ansible

Issue:
ec2_vpc_peer -> Must have duplicate tags in accept peer connection or task bombs and crashes.


##### ISSUE TYPE

 - Bug Report


##### COMPONENT NAME

modules/cloud/amazon/ec2_vpc_peer.py

##### ANSIBLE VERSION

```
ansible --version
ansible 2.4.2.0
  config file = /home/snobym/work/aws-k8s-kops/ansible.cfg
  configured module search path = [u'/home/snobym/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/snobym/.local/lib/python2.7/site-packages/ansible
  executable location = /home/snobym/.local/bin/ansible
  python version = 2.7.14 (default, Sep 23 2017, 22:06:14) [GCC 7.2.0]
```

##### CONFIGURATION

```
ansible-config dump --only-changed
ANSIBLE_NOCOWS(/home/snobym/work/aws-k8s-kops/ansible.cfg) = True
CACHE_PLUGIN(/home/snobym/work/aws-k8s-kops/ansible.cfg) = jsonfile
CACHE_PLUGIN_CONNECTION(/home/snobym/work/aws-k8s-kops/ansible.cfg) = .tmp/ansible_fact_cache
CACHE_PLUGIN_TIMEOUT(/home/snobym/work/aws-k8s-kops/ansible.cfg) = 86400
DEFAULT_CALLBACK_PLUGIN_PATH(/home/snobym/work/aws-k8s-kops/ansible.cfg) = [u'/home/snobym/work/aws-k8s-kops/callbacks']
DEFAULT_FORKS(/home/snobym/work/aws-k8s-kops/ansible.cfg) = 20
DEFAULT_HOST_LIST(/home/snobym/work/aws-k8s-kops/ansible.cfg) = [u'/home/snobym/work/aws-k8s-kops/inventory']
HOST_KEY_CHECKING(/home/snobym/work/aws-k8s-kops/ansible.cfg) = False
RETRY_FILES_ENABLED(/home/snobym/work/aws-k8s-kops/ansible.cfg) = False
```

##### OS / ENVIRONMENT
Running on Ubuntu 16.04 on an ec2 instance.


##### SUMMARY
I've used the ec2_vpc_peer module to create a peering connection with tags.  Just like in the example in the docs.  When I run the next command to locally accept the connection the backtrace shows an exception occurred because the module had no tags defined.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
# tasks file for vpc-peering
- name: "Get vpc ID of the east-prod.k8s.foo.com VPC"
  ec2_vpc_net_facts:
    region: "us-east-1"
    filters:
      "tag:Name": east-prod.k8s.foo.com
  register: vpc_data

- set_fact:
    K8S_VPC_ID: "{{ vpc_data.vpcs[0].id }}"


#
# Now get the VPC_ID of the Foo Prod VPC
#
- name: "Get vpc ID of the Foo Prod VPC"
  ec2_vpc_net_facts:
    region: "us-east-1"
    filters:
      "tag:Name": Foo Prod
  register: foo_prod

- set_fact:
    FOO_PROD_VPC_ID: "{{ foo_prod.vpcs[0].id }}"

#
# Doing some back error checking to ensure our values are correctly defined
#
- fail: msg="Variable '{{item}}' is not defined"
  when: item not in vars
  with_items:
    - K8S_VPC_ID
    - FOO_PROD_VPC_ID

- debug:
    msg: "K8S_VPC_ID is {{ K8S_VPC_ID }} "

- debug:
    msg: "FOO_PROD_VPC_ID is {{ FOO_PROD_VPC_ID }} "

- name: Create local Account VPC peering connection
  ec2_vpc_peer:
    region: us-east-1
    vpc_id: "{{ K8S_VPC_ID }}"
    peer_vpc_id: "{{ FOO_PROD_VPC_ID }}"
    state: present
    tags:
      Name: prod-east-k8s-to-prod-us-east
      Project: Kubernetes
  register: vpc_peer

- name: Accept local VPC peering request
  ec2_vpc_peer:
    region: us-east-1
    peering_id: "{{ vpc_peer.peering_id }}"
    state: accept
  register: action_peer

```



##### EXPECTED RESULTS
I expected the peering connection to be accepted, after that no changes should happen.

##### ACTUAL RESULTS
It looks on every run whether the peering connection has been accepted or not the tags are deleted then created again, but  since in my accept task I don't list any tags the module throws an exception and gives a back trace because there are no tags to create.  To work around this I simply duplicate the tags in the accept call.  These are the same tags I used in the create call.

<!--- Paste verbatim command output between quotes below -->
```
TASK [vpc-peering : Accept local VPC peering request] ******************************************************************************************************************************************
task path: /home/snobym/work/aws-k8s-kops/roles/vpc-peering/tasks/main.yml:53
Using module file /home/snobym/.local/lib/python2.7/site-packages/ansible/modules/cloud/amazon/ec2_vpc_peer.py
<east-prod> ESTABLISH LOCAL CONNECTION FOR USER: snobym
<east-prod> EXEC /bin/sh -c 'echo ~ && sleep 0'
<east-prod> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577 `" && echo ansible-tmp-1518528910.29-192160277860577="` echo /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577 `" ) && sleep 0'
<east-prod> PUT /tmp/tmpZyrTq7 TO /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577/ec2_vpc_peer.py
<east-prod> EXEC /bin/sh -c 'chmod u+x /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577/ /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577/ec2_vpc_peer.py && sleep 0'
<east-prod> EXEC /bin/sh -c '/usr/bin/python /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577/ec2_vpc_peer.py; rm -rf "/home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577/" > /dev/null 2>&1 && sleep 0'
The full traceback is:
  File "/tmp/ansible_02oSze/ansible_module_ec2_vpc_peer.py", line 334, in create_tags
    client.create_tags(Resources=[pcx_id], Tags=load_tags(module))
  File "/home/snobym/.local/lib/python2.7/site-packages/botocore/client.py", line 317, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/home/snobym/.local/lib/python2.7/site-packages/botocore/client.py", line 615, in _make_api_call
    raise error_class(parsed_response, operation_name)

fatal: [east-prod]: FAILED! => {
    "changed": false,
    "invocation": {
        "module_args": {
            "aws_access_key": null,
            "aws_secret_key": null,
            "ec2_url": null,
            "peer_owner_id": null,
            "peer_vpc_id": null,
            "peering_id": "pcx-bf342cd6",
            "profile": null,
            "region": "us-east-1",
            "security_token": null,
            "state": "accept",
            "tags": null,
            "validate_certs": true,
            "vpc_id": null
        }
    },
    "msg": "An error occurred (InvalidParameterValue) when calling the CreateTags operation: Value ( empty ) for parameter tagSet is invalid. You must specify one or more tags to create"
}

```
The module should probably NOT delete and recreate the tags on the accept call.

ec2_vpc_peer -> Must have duplicate tags in accept peer connection or task bombs and crashes.


##### ISSUE TYPE

 - Bug Report


##### COMPONENT NAME

modules/cloud/amazon/ec2_vpc_peer.py

##### ANSIBLE VERSION

```
ansible --version
ansible 2.4.2.0
  config file = /home/snobym/work/aws-k8s-kops/ansible.cfg
  configured module search path = [u'/home/snobym/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/snobym/.local/lib/python2.7/site-packages/ansible
  executable location = /home/snobym/.local/bin/ansible
  python version = 2.7.14 (default, Sep 23 2017, 22:06:14) [GCC 7.2.0]
```

##### CONFIGURATION

```
ansible-config dump --only-changed
ANSIBLE_NOCOWS(/home/snobym/work/aws-k8s-kops/ansible.cfg) = True
CACHE_PLUGIN(/home/snobym/work/aws-k8s-kops/ansible.cfg) = jsonfile
CACHE_PLUGIN_CONNECTION(/home/snobym/work/aws-k8s-kops/ansible.cfg) = .tmp/ansible_fact_cache
CACHE_PLUGIN_TIMEOUT(/home/snobym/work/aws-k8s-kops/ansible.cfg) = 86400
DEFAULT_CALLBACK_PLUGIN_PATH(/home/snobym/work/aws-k8s-kops/ansible.cfg) = [u'/home/snobym/work/aws-k8s-kops/callbacks']
DEFAULT_FORKS(/home/snobym/work/aws-k8s-kops/ansible.cfg) = 20
DEFAULT_HOST_LIST(/home/snobym/work/aws-k8s-kops/ansible.cfg) = [u'/home/snobym/work/aws-k8s-kops/inventory']
HOST_KEY_CHECKING(/home/snobym/work/aws-k8s-kops/ansible.cfg) = False
RETRY_FILES_ENABLED(/home/snobym/work/aws-k8s-kops/ansible.cfg) = False
```

##### OS / ENVIRONMENT
Running on Ubuntu 16.04 on an ec2 instance.


##### SUMMARY
I've used the ec2_vpc_peer module to create a peering connection with tags.  Just like in the example in the docs.  When I run the next command to locally accept the connection the backtrace shows an exception occurred because the module had no tags defined.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
# tasks file for vpc-peering
- name: "Get vpc ID of the east-prod.k8s.foo.com VPC"
  ec2_vpc_net_facts:
    region: "us-east-1"
    filters:
      "tag:Name": east-prod.k8s.foo.com
  register: vpc_data

- set_fact:
    K8S_VPC_ID: "{{ vpc_data.vpcs[0].id }}"


#
# Now get the VPC_ID of the Foo Prod VPC
#
- name: "Get vpc ID of the Foo Prod VPC"
  ec2_vpc_net_facts:
    region: "us-east-1"
    filters:
      "tag:Name": Foo Prod
  register: foo_prod

- set_fact:
    FOO_PROD_VPC_ID: "{{ foo_prod.vpcs[0].id }}"

#
# Doing some back error checking to ensure our values are correctly defined
#
- fail: msg="Variable '{{item}}' is not defined"
  when: item not in vars
  with_items:
    - K8S_VPC_ID
    - FOO_PROD_VPC_ID

- debug:
    msg: "K8S_VPC_ID is {{ K8S_VPC_ID }} "

- debug:
    msg: "FOO_PROD_VPC_ID is {{ FOO_PROD_VPC_ID }} "

- name: Create local Account VPC peering connection
  ec2_vpc_peer:
    region: us-east-1
    vpc_id: "{{ K8S_VPC_ID }}"
    peer_vpc_id: "{{ FOO_PROD_VPC_ID }}"
    state: present
    tags:
      Name: prod-east-k8s-to-prod-us-east
      Project: Kubernetes
  register: vpc_peer

- name: Accept local VPC peering request
  ec2_vpc_peer:
    region: us-east-1
    peering_id: "{{ vpc_peer.peering_id }}"
    state: accept
  register: action_peer

```



##### EXPECTED RESULTS
I expected the peering connection to be accepted, after that no changes should happen.

##### ACTUAL RESULTS
It looks on every run whether the peering connection has been accepted or not the tags are deleted then created again, but  since in my accept task I don't list any tags the module throws an exception and gives a back trace because there are no tags to create.  To work around this I simply duplicate the tags in the accept call.  These are the same tags I used in the create call.

<!--- Paste verbatim command output between quotes below -->
```
TASK [vpc-peering : Accept local VPC peering request] ******************************************************************************************************************************************
task path: /home/snobym/work/aws-k8s-kops/roles/vpc-peering/tasks/main.yml:53
Using module file /home/snobym/.local/lib/python2.7/site-packages/ansible/modules/cloud/amazon/ec2_vpc_peer.py
<east-prod> ESTABLISH LOCAL CONNECTION FOR USER: snobym
<east-prod> EXEC /bin/sh -c 'echo ~ && sleep 0'
<east-prod> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577 `" && echo ansible-tmp-1518528910.29-192160277860577="` echo /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577 `" ) && sleep 0'
<east-prod> PUT /tmp/tmpZyrTq7 TO /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577/ec2_vpc_peer.py
<east-prod> EXEC /bin/sh -c 'chmod u+x /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577/ /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577/ec2_vpc_peer.py && sleep 0'
<east-prod> EXEC /bin/sh -c '/usr/bin/python /home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577/ec2_vpc_peer.py; rm -rf "/home/snobym/.ansible/tmp/ansible-tmp-1518528910.29-192160277860577/" > /dev/null 2>&1 && sleep 0'
The full traceback is:
  File "/tmp/ansible_02oSze/ansible_module_ec2_vpc_peer.py", line 334, in create_tags
    client.create_tags(Resources=[pcx_id], Tags=load_tags(module))
  File "/home/snobym/.local/lib/python2.7/site-packages/botocore/client.py", line 317, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/home/snobym/.local/lib/python2.7/site-packages/botocore/client.py", line 615, in _make_api_call
    raise error_class(parsed_response, operation_name)

fatal: [east-prod]: FAILED! => {
    "changed": false,
    "invocation": {
        "module_args": {
            "aws_access_key": null,
            "aws_secret_key": null,
            "ec2_url": null,
            "peer_owner_id": null,
            "peer_vpc_id": null,
            "peering_id": "pcx-bf342cd6",
            "profile": null,
            "region": "us-east-1",
            "security_token": null,
            "state": "accept",
            "tags": null,
            "validate_certs": true,
            "vpc_id": null
        }
    },
    "msg": "An error occurred (InvalidParameterValue) when calling the CreateTags operation: Value ( empty ) for parameter tagSet is invalid. You must specify one or more tags to create"
}

```
The module should probably NOT delete and recreate the tags on the accept call.



Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/amazon/ec2_vpc_peer.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/ec2_vpc_peer.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @mmochan @willthames
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Files identified in the description:
* [lib/ansible/modules/cloud/amazon/ec2_vpc_peer.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/ec2_vpc_peer.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @mmochan @willthames
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/ec2_vpc_peer.py b/lib/ansible/modules/cloud/amazon/ec2_vpc_peer.py
--- a/lib/ansible/modules/cloud/amazon/ec2_vpc_peer.py
+++ b/lib/ansible/modules/cloud/amazon/ec2_vpc_peer.py
@@ -238,12 +238,10 @@ def tags_changed(pcx_id, client, module):
         tags = [item for sublist in tag_values for item in sublist]
         if sorted(pcx_tags) == sorted(tags):
             changed = False
-            return changed
-        else:
+        elif tags:
             delete_tags(pcx_id, client, module)
             create_tags(pcx_id, client, module)
             changed = True
-            return changed
     return changed
 
 


Test patch: