Repository: ansible/ansible

Issue:
Regression in Iptables module - throws TypeError: can only join an iterable with devel (2.3), worked with 2.2
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
Iptables

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
<!---
nothing special
-->

##### OS / ENVIRONMENT
<!---
ubuntu 16.04.2
-->

##### SUMMARY
TypeError: can only join an iterable appears on tasks that work just fine with 2.2.x releases.

##### 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
- hosts: localhost
  vars:
      main_wan_ip: "192.168.1.233"
      main_lxd_iface: "lxdbr0"
      lxd_provisioning_inventory:
      - name: "proxy"
        image: "ubuntu/xenial/amd64"
        nat:
        - { wan_port: "443", lxd_port: "443" }
        - { wan_port: "80",  lxd_port: "80" }
      - name: "srv-1"
        image: "ubuntu/xenial/amd64"
      - name: "srv-2"
        image: "ubuntu/xenial/amd64"
  roles:
    - role: "mage-vmhost"
    - role: "mage.lxd-provisioning"
```

problematic task

```yaml


# iptables -t nat -A PREROUTING -d "${wan_ipv4}" -p ${protocol} --dport "${wan_port}" -j DNAT --to "${container_ipv4}":"${container_port}"
- name: "Making sure prerouting set properly for all containers"
  iptables: table=nat chain=PREROUTING destination="{{ item.1.wan_ip | default(main_wan_ip) }}" protocol="{{ item.1.protocol | default(lxd_default_protocol) }}" destination_port="{{ item.1.wan_port }}" jump=DNAT to_destination="{{ lxd_provisioning_ips[item.0.name] }}:{{ item.1.lxd_port }}" comment="Prerouting for {{ item.0.name }} (Ansible managed)"
  become: yes
  with_subelements:
    - "{{ lxd_provisioning_inventory }}"
    - nat
    - flags:
      skip_missing: true
  when: ( (main_wan_ip is defined and main_wan_ip|trim) and (main_lxd_iface is defined and main_lxd_iface|trim) and (lxd_default_protocol is defined and lxd_default_protocol|trim) )


```
<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
not seeing an error.

##### ACTUAL RESULTS
seeing an error

<!--- Paste verbatim command output between quotes below -->
```
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TypeError: can only join an iterable
failed: [localhost] (item=({u'image': u'ubuntu/xenial/amd64', u'name': u'proxy'}, {u'lxd_port': u'443', u'wan_port': u'443'})) => {"failed": true, "item": [{"image": "ubuntu/xenial/amd64", "name": "proxy"}, {"lxd_port": "443", "wan_port": "443"}], "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_CbCfun/ansible_module_iptables.py\", line 576, in <module>\n    main()\n  File \"/tmp/ansible_CbCfun/ansible_module_iptables.py\", line 532, in main\n    rule=' '.join(construct_rule(module.params)),\nTypeError: can only join an iterable\n", "module_stdout": "", "msg": "MODULE FAILURE", "rc": 0}
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TypeError: can only join an iterable
failed: [localhost] (item=({u'image': u'ubuntu/xenial/amd64', u'name': u'proxy'}, {u'lxd_port': u'80', u'wan_port': u'80'})) => {"failed": true, "item": [{"image": "ubuntu/xenial/amd64", "name": "proxy"}, {"lxd_port": "80", "wan_port": "80"}], "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_5esHLN/ansible_module_iptables.py\", line 576, in <module>\n    main()\n  File \"/tmp/ansible_5esHLN/ansible_module_iptables.py\", line 532, in main\n    rule=' '.join(construct_rule(module.params)),\nTypeError: can only join an iterable\n", "module_stdout": "", "msg": "MODULE FAILURE", "rc": 0}
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: TypeError: can only join an iterable
failed: [localhost] (item=({u'image': u'ubuntu/xenial/amd64', u'name': u'srv-1'}, {u'protocol': u'tcp', u'lxd_port': u'22', u'wan_port': u'22129'})) => {"failed": true, "item": [{"image": "ubuntu/xenial/amd64", "name": "japex"}, {"lxd_port": "22", "protocol": "tcp", "wan_port": "22129"}], "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_d989dF/ansible_module_iptables.py\", line 576, in <module>\n    main()\n  File \"/tmp/ansible_d989dF/ansible_module_iptables.py\", line 532, in main\n    rule=' '.join(construct_rule(module.params)),\nTypeError: can only join an iterable\n", "module_stdout": "", "msg": "MODULE FAILURE", "rc": 0}
```



Discussion:
cc @LinusU
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Looks like commit 96a19a452174667f47c55f4185cd1c6ec4fb6289 added a way for construct_rule() to return False, which I suspect is getting it and causing the TypeError.
(just a reformatting of the troublesome task in dict format for my reference)

``` yaml
# iptables -t nat -A PREROUTING -d "${wan_ipv4}" -p ${protocol} --dport "${wan_port}" -j DNAT --to "${container_ipv4}":"${container_port}"
- name: "Making sure prerouting set properly for all containers"
  iptables:
    table: nat
    chain: PREROUTING
    destination: "{{ item.1.wan_ip | default(main_wan_ip) }}" 
    protocol: "{{ item.1.protocol | default(lxd_default_protocol) }}"
    destination_port: "{{ item.1.wan_port }}" 
    jump: DNAT 
    to_destination: "{{ lxd_provisioning_ips[item.0.name] }}:{{ item.1.lxd_port }}" 
    comment: "Prerouting for {{ item.0.name }} (Ansible managed)"
  become: yes
  with_subelements:
    - "{{ lxd_provisioning_inventory }}"
    - nat
    - flags:
      skip_missing: true
  when: ( (main_wan_ip is defined and main_wan_ip|trim) and (main_lxd_iface is defined and main_lxd_iface|trim) and (lxd_default_protocol is defined and lxd_default_protocol|trim) )
```


Resolving patch:
diff --git a/lib/ansible/modules/system/iptables.py b/lib/ansible/modules/system/iptables.py
--- a/lib/ansible/modules/system/iptables.py
+++ b/lib/ansible/modules/system/iptables.py
@@ -406,8 +406,6 @@ def construct_rule(params):
     elif params['ctstate']:
         append_match(rule, params['ctstate'], 'conntrack')
         append_csv(rule, params['ctstate'], '--ctstate')
-    else:
-        return False
     append_match(rule, params['limit'] or params['limit_burst'], 'limit')
     append_param(rule, params['limit'], '--limit', False)
     append_param(rule, params['limit_burst'], '--limit-burst', False)


Test patch: