Ensure plugin_loader.all() only loads one instance

by ensuring all basedirs, plugin paths and extra
paths are handled as absolute paths and are checked
to not add any doubles.

This fixes the corner case where e.g. the user has
an additional plugin path configured to a dir
relative to his playbooks or inventory location,
which also matches the  _plugin subdir relative to
one of the basedirs in the play.

For most plugins this doesn't show as an obvious issue
except for callback_plugins, which might fire more
than once. Other plugins (inventory and template
plugins) might unnecessarily be ran twice.

e.g. ansible.cfg has
callback_plugins   = ./plays/callback_plugins
and plays/ contains a playbook file:
.
├── ansible.cfg
├── inventory
└── plays
    ├── callback_plugins
    │   └── timestamp.py
    └── site.yml

modified:   lib/ansible/utils/plugins.py
parent 3e32654f
...@@ -29,6 +29,8 @@ PLUGIN_PATH_CACHE = {} ...@@ -29,6 +29,8 @@ PLUGIN_PATH_CACHE = {}
_basedirs = [] _basedirs = []
def push_basedir(basedir): def push_basedir(basedir):
# avoid pushing the same absolute dir more than once
basedir = os.path.abspath(basedir)
if basedir not in _basedirs: if basedir not in _basedirs:
_basedirs.insert(0, basedir) _basedirs.insert(0, basedir)
...@@ -97,7 +99,7 @@ class PluginLoader(object): ...@@ -97,7 +99,7 @@ class PluginLoader(object):
ret = [] ret = []
ret += self._extra_dirs ret += self._extra_dirs
for basedir in _basedirs: for basedir in _basedirs:
fullpath = os.path.join(basedir, self.subdir) fullpath = os.path.abspath(os.path.join(basedir, self.subdir))
if os.path.isdir(fullpath): if os.path.isdir(fullpath):
files = glob.glob("%s/*" % fullpath) files = glob.glob("%s/*" % fullpath)
for file in files: for file in files:
...@@ -109,12 +111,13 @@ class PluginLoader(object): ...@@ -109,12 +111,13 @@ class PluginLoader(object):
# look in any configured plugin paths, allow one level deep for subcategories # look in any configured plugin paths, allow one level deep for subcategories
configured_paths = self.config.split(os.pathsep) configured_paths = self.config.split(os.pathsep)
for path in configured_paths: for path in configured_paths:
path = os.path.expanduser(path) path = os.path.abspath(os.path.expanduser(path))
contents = glob.glob("%s/*" % path) contents = glob.glob("%s/*" % path)
for c in contents: for c in contents:
if os.path.isdir(c): if os.path.isdir(c) and c not in ret:
ret.append(c) ret.append(c)
ret.append(path) if path not in ret:
ret.append(path)
# look for any plugins installed in the package subtree # look for any plugins installed in the package subtree
ret.extend(self._get_package_paths()) ret.extend(self._get_package_paths())
...@@ -128,6 +131,7 @@ class PluginLoader(object): ...@@ -128,6 +131,7 @@ class PluginLoader(object):
''' Adds an additional directory to the search path ''' ''' Adds an additional directory to the search path '''
self._paths = None self._paths = None
directory = os.path.abspath(directory)
if directory is not None: if directory is not None:
if with_subdir: if with_subdir:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment