Repository: ansible/ansible

Issue:
easy_install name=pip state=latest is not idempotent
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
easy_install

##### ANSIBLE VERSION
```
ansible 2.2.1.0
  config file = /home/tasso/Projetos/prototipos/ansible-configs/ansible.cfg
  configured module search path = Default w/o overrides
```

##### CONFIGURATION
```
[defaults]
nocows = 1
roles_path = roles
allow_world_readable_tmpfiles = 1
```

##### OS / ENVIRONMENT
Ubuntu 16.04 (Xenial) for both host and node.

##### SUMMARY
The easy_install module always report changes in `pip` when using `state=latest`. It occurred after an upgrade of host via `apt-get dist-upgrade`, but I can't confirm it happened due an Ansible update.

##### STEPS TO REPRODUCE
Run the following at least twice:

```
$ ansible <node> -m easy_install -a 'name=pip state=latest' -b
```

##### EXPECTED RESULTS
```
<node> | SUCCESS => {
    "binary": "/usr/bin/easy_install", 
    "changed": true, 
    "name": "pip", 
    "virtualenv": null
}

<node> | SUCCESS => {
    "binary": "/usr/bin/easy_install", 
    "changed": false, 
    "name": "pip", 
    "virtualenv": null
}
```

##### ACTUAL RESULTS
```
<node> | SUCCESS => {
    "binary": "/usr/bin/easy_install", 
    "changed": true, 
    "name": "pip", 
    "virtualenv": null
}

<node> | SUCCESS => {
    "binary": "/usr/bin/easy_install", 
    "changed": true, 
    "name": "pip", 
    "virtualenv": null
}
```


Discussion:
cc @mattupstate
[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/packaging/language/easy_install.py b/lib/ansible/modules/packaging/language/easy_install.py
--- a/lib/ansible/modules/packaging/language/easy_install.py
+++ b/lib/ansible/modules/packaging/language/easy_install.py
@@ -94,17 +94,23 @@
 import os
 import os.path
 import tempfile
-
 from ansible.module_utils.basic import AnsibleModule
 
 
-def _is_package_installed(module, name, easy_install, executable_arguments):
-    executable_arguments = executable_arguments + ['--dry-run']
+def install_package(module, name, easy_install, executable_arguments):
     cmd = '%s %s %s' % (easy_install, ' '.join(executable_arguments), name)
-    rc, status_stdout, status_stderr = module.run_command(cmd)
+    rc, out, err = module.run_command(cmd)
+    return rc, out, err
+
+
+def _is_package_installed(module, name, easy_install, executable_arguments):
+    # Copy and add to the arguments
+    executable_arguments = executable_arguments[:]
+    executable_arguments.append('--dry-run')
+    rc, out, err = install_package(module, name, easy_install, executable_arguments)
     if rc:
-        module.fail_json(msg=status_stderr)
-    return not ('Reading' in status_stdout or 'Downloading' in status_stdout)
+        module.fail_json(msg=err)
+    return 'Downloading' not in out
 
 
 def _get_easy_install(module, env=None, executable=None):
@@ -138,7 +144,7 @@ def main():
         name=dict(required=True),
         state=dict(required=False,
                    default='present',
-                   choices=['present','latest'],
+                   choices=['present', 'latest'],
                    type='str'),
         virtualenv=dict(default=None, required=False),
         virtualenv_site_packages=dict(default='no', type='bool'),
@@ -186,8 +192,7 @@ def main():
     if not installed:
         if module.check_mode:
             module.exit_json(changed=True)
-        cmd = '%s %s %s' % (easy_install, ' '.join(executable_arguments), name)
-        rc_easy_inst, out_easy_inst, err_easy_inst = module.run_command(cmd)
+        rc_easy_inst, out_easy_inst, err_easy_inst = install_package(module, name, easy_install, executable_arguments)
 
         rc += rc_easy_inst
         out += out_easy_inst


Test patch:
diff --git a/test/sanity/pep8/legacy-files.txt b/test/sanity/pep8/legacy-files.txt
--- a/test/sanity/pep8/legacy-files.txt
+++ b/test/sanity/pep8/legacy-files.txt
@@ -363,7 +363,6 @@ lib/ansible/modules/notification/slack.py
 lib/ansible/modules/notification/twilio.py
 lib/ansible/modules/packaging/language/bundler.py
 lib/ansible/modules/packaging/language/cpanm.py
-lib/ansible/modules/packaging/language/easy_install.py
 lib/ansible/modules/packaging/language/gem.py
 lib/ansible/modules/packaging/language/maven_artifact.py
 lib/ansible/modules/packaging/language/pear.py