Repository: ansible/ansible

Issue:
Dynamic inventory errors in 2.5.1
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
lib/ansible/plugins/inventory/script.py

##### ANSIBLE VERSION
```
ansible 2.5.1
  config file = None
  configured module search path = [u'/Users/BR0kEN/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/Cellar/ansible/2.5.1/libexec/lib/python2.7/site-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.14 (default, Mar 10 2018, 00:01:04) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]
```

##### CONFIGURATION
Standard.

##### OS / ENVIRONMENT
```
Darwin Macintosh.local 17.5.0 Darwin Kernel Version 17.5.0: Mon Mar  5 22:24:32 PST 2018; root:xnu-4570.51.1~1/RELEASE_X86_64 x86_64 i386 MacBookPro11,3 Darwin
```

##### SUMMARY
The dynamic inventory (Python) script fails with an unknown error right after starting the process.

##### STEPS TO REPRODUCE

```bash
ANSIBLE_VERBOSITY=4 ansible-playbook 'test.yml' -c 'local' -i 'localhost,' -i 'inventory'
```

`test.yml`

```yaml
---
- hosts: all
  gather_facts: no

  tasks:
    - debug: msg=12
```

`inventory`

```python
#!/usr/bin/env python
import sys
import json
INVENTORY = {}

print(json.dumps(INVENTORY))
sys.exit(0)
```

##### EXPECTED RESULTS

```
ansible-playbook 2.5.1
  config file = None
  configured module search path = [u'/Users/BR0kEN/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/Cellar/ansible/2.5.1/libexec/lib/python2.7/site-packages/ansible
  executable location = /usr/local/bin/ansible-playbook
  python version = 2.7.14 (default, Mar 10 2018, 00:01:04) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]
No config file found; using defaults
setting up inventory plugins
Set default localhost to localhost
Parsed localhost, inventory source with host_list plugin
Parsed /usr/local/share/cikit/lib/inventory inventory source with script plugin
Loading callback plugin default of type stdout, v2.0 from /usr/local/Cellar/ansible/2.5.1/libexec/lib/python2.7/site-packages/ansible/plugins/callback/default.pyc

PLAYBOOK: test.yml *****************************************************************************************************************************************************************************************
1 plays in /usr/local/share/cikit/scripts/test.yml
^C
PLAY [all] *************************************************************************************************************************************************************************************************
META: ran handlers

TASK [debug] ***********************************************************************************************************************************************************************************************
task path: /usr/local/share/cikit/scripts/test.yml:7
ok: [localhost] => {
    "msg": "12"
}
META: ran handlers
META: ran handlers

PLAY RECAP *************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0
```

##### ACTUAL RESULTS

```
ansible-playbook 2.5.1
  config file = None
  configured module search path = [u'/Users/BR0kEN/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/Cellar/ansible/2.5.1/libexec/lib/python2.7/site-packages/ansible
  executable location = /usr/local/bin/ansible-playbook
  python version = 2.7.14 (default, Mar 10 2018, 00:01:04) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)]
No config file found; using defaults
setting up inventory plugins
Set default localhost to localhost
Parsed localhost, inventory source with host_list plugin
 [ERROR]:

Parsed /usr/local/share/cikit/lib/inventory inventory source with script plugin
Loading callback plugin default of type stdout, v2.0 from /usr/local/Cellar/ansible/2.5.1/libexec/lib/python2.7/site-packages/ansible/plugins/callback/default.pyc

PLAYBOOK: test.yml *****************************************************************************************************************************************************************************************
1 plays in /usr/local/share/cikit/scripts/test.yml
^C
PLAY [all] *************************************************************************************************************************************************************************************************
META: ran handlers

TASK [debug] ***********************************************************************************************************************************************************************************************
task path: /usr/local/share/cikit/scripts/test.yml:7
ok: [localhost] => {
    "msg": "12"
}
META: ran handlers
META: ran handlers

PLAY RECAP *************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0
```
<img width="1208" alt="1 bash 2018-04-19 15-40-22" src="https://user-images.githubusercontent.com/2760616/38989450-ffb4f068-43e7-11e8-878c-8abe0e0a1492.png">

However, the `ANSIBLE_VERBOSITY=4 ansible-playbook 'test.yml' -c 'local' -i 'localhost,'` command (without dynamic invetory) works as expected.


Discussion:
Files identified in the description:
* [lib/ansible/inventory](https://github.com/ansible/ansible/blob/devel/lib/ansible/inventory)

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 --->
During debug I've discovered that these lines introduced this behavior: https://github.com/ansible/ansible/compare/v2.5.0...v2.5.1#diff-d01d986987d9cd0f682890b51b98b04dR124

The problem is that `stderr` is an empty string but `err` is not since `\n` has been appended.
!component=lib/ansible/plugins/inventory/script.py
Files identified in the description:
* [lib/ansible/plugins/inventory/script.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/inventory/script.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 --->

Resolving patch:
diff --git a/lib/ansible/plugins/inventory/script.py b/lib/ansible/plugins/inventory/script.py
--- a/lib/ansible/plugins/inventory/script.py
+++ b/lib/ansible/plugins/inventory/script.py
@@ -104,7 +104,10 @@ def parse(self, inventory, loader, path, cache=None):
                 (stdout, stderr) = sp.communicate()
 
                 path = to_native(path)
-                err = to_native(stderr or "") + "\n"
+                err = to_native(stderr or "")
+
+                if err and not err.endswith('\n'):
+                    err += '\n'
 
                 if sp.returncode != 0:
                     raise AnsibleError("Inventory script (%s) had an execution error: %s " % (path, err))


Test patch: