Repository: ansible/ansible

Issue:
#31425 affects variables assigned to implicit localhost in a backwards-incompatible way
<!---
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 -->

inventory

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.0 (detached HEAD 7623c2fbda) last updated 2017/10/17 20:12:26 (GMT -400)
  config file = /home/ec2-user/ansible-aws-dev/ansible/ansible.cfg
  configured module search path = [u'/home/ec2-user/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/ec2-user/ansible-aws-dev/git/ansible.git/lib/ansible
  executable location = /home/ec2-user/ansible-aws-dev/git/ansible.git/bin/ansible
  python version = 2.7.5 (default, Aug  2 2016, 04:20:16) [GCC 4.8.5 20150623 (Red Hat 4.8.5-4)]
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"

Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).

-->
```
ANSIBLE_PIPELINING(/home/ec2-user/ansible-aws-dev/ansible/ansible.cfg) = True
ANSIBLE_SSH_CONTROL_PATH(/home/ec2-user/ansible-aws-dev/ansible/ansible.cfg) = %(directory)s/%%h-%%p-%%r
DEFAULT_BECOME(/home/ec2-user/ansible-aws-dev/ansible/ansible.cfg) = True
DEFAULT_BECOME_METHOD(/home/ec2-user/ansible-aws-dev/ansible/ansible.cfg) = sudo
DEFAULT_HOST_LIST(env: ANSIBLE_INVENTORY) = [u'/home/ec2-user/ansible-aws-dev/inventory']
DEFAULT_PRIVATE_KEY_FILE(env: ANSIBLE_PRIVATE_KEY_FILE) = /home/ec2-user/ansible-aws-dev/id_rsa
DEFAULT_REMOTE_USER(/home/ec2-user/ansible-aws-dev/ansible/ansible.cfg) = ec2-user
HOST_KEY_CHECKING(/home/ec2-user/ansible-aws-dev/ansible/ansible.cfg) = False
RETRY_FILES_ENABLED(/home/ec2-user/ansible-aws-dev/ansible/ansible.cfg) = False
```

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
N/A

##### SUMMARY
<!--- Explain the problem briefly -->
Before #31425 was merged, and going back at least as far as ansible 2.0, the implicit localhost entry was assigned group vars from 'all'. Now it is not.

##### 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 -->
test.yml:
```yaml
- hosts: localhost
  gather_facts: false
  tasks:
    - debug:
        var: flowerysong
```

group_vars/all.yml:
```yaml
flowerysong: foo
```

Run ```ansible-playbook -i /dev/null test.yml```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
ansible 2.0.2.0 and all other versions tested before this pull request show the variable as defined:
```
 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] ***************************************************************

TASK [debug] *******************************************************************
ok: [localhost] => {
    "flowerysong": "foo"
}

PLAY RECAP *********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0
```

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
The variable is now shown as undefined.

<!--- Paste verbatim command output between quotes below -->
```
 [WARNING]: Could not match supplied host pattern, ignoring: all

 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] *****************************************************************************************************

TASK [debug] *********************************************************************************************************
ok: [localhost] => {
    "flowerysong": "VARIABLE IS NOT DEFINED!"
}

PLAY RECAP ***********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0
```



Discussion:
cc @dagnello @emonty @j2sol @juliakreger @rcarrillocruz @shrews @thingee
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I wonder if this is something that needs covering in https://github.com/ansible/ansible/pull/31840

Resolving patch:
diff --git a/lib/ansible/inventory/data.py b/lib/ansible/inventory/data.py
--- a/lib/ansible/inventory/data.py
+++ b/lib/ansible/inventory/data.py
@@ -79,6 +79,9 @@ def _create_implicit_localhost(self, pattern):
         else:
             new_host = Host(pattern)
 
+            # use 'all' vars but not part of all group
+            new_host.vars = self.groups['all'].get_vars()
+
             new_host.address = "127.0.0.1"
             new_host.implicit = True
 
diff --git a/lib/ansible/vars/manager.py b/lib/ansible/vars/manager.py
--- a/lib/ansible/vars/manager.py
+++ b/lib/ansible/vars/manager.py
@@ -334,15 +334,14 @@ def plugins_by_groups():
                     data[group] = combine_vars(data[group], _plugins_play(group))
                 return data
 
-            # Merge groups as per precedence config, if not implicit localhost
+            # Merge groups as per precedence config
             # only allow to call the functions we want exposed
-            if not host.implicit:
-                for entry in C.VARIABLE_PRECEDENCE:
-                    if entry in self._ALLOWED:
-                        display.debug('Calling %s to load vars for %s' % (entry, host.name))
-                        all_vars = combine_vars(all_vars, locals()[entry]())
-                    else:
-                        display.warning('Ignoring unknown variable precedence entry: %s' % (entry))
+            for entry in C.VARIABLE_PRECEDENCE:
+                if entry in self._ALLOWED:
+                    display.debug('Calling %s to load vars for %s' % (entry, host.name))
+                    all_vars = combine_vars(all_vars, locals()[entry]())
+                else:
+                    display.warning('Ignoring unknown variable precedence entry: %s' % (entry))
 
             # host vars, from inventory, inventory adjacent and play adjacent via plugins
             all_vars = combine_vars(all_vars, host.get_vars())


Test patch: