fixed lookup search path (#16630)

* fixed lookup search path

added ansible_search_path var that contains the proper list and in order
removed roledir var which was only used by first_found, rest used role_path
added needle function for lookups that mirrors the action plugin one, now
both types of plugins use same pathing.

* added missing os import

* renamed as per feedback

* fixed missing rename in first_found

* also fixed first_found

* fixed import to match new error class

* fixed getattr ref
This commit is contained in:
Brian Coca 2016-07-13 10:06:34 -04:00 committed by GitHub
parent 221520cbad
commit 3c39bb5633
11 changed files with 87 additions and 84 deletions

View file

@ -83,12 +83,8 @@ class TaskExecutor:
display.debug("in run()") display.debug("in run()")
try: try:
# lookup plugins need to know if this task is executing from # get search path for this task to pass to lookup plugins
# a role, so that it can properly find files/templates/etc. self._job_vars['ansible_search_path'] = self._task.get_search_path()
roledir = None
if self._task._role:
roledir = self._task._role._role_path
self._job_vars['roledir'] = roledir
items = self._get_loop_items() items = self._get_loop_items()
if items is not None: if items is not None:
@ -192,7 +188,18 @@ class TaskExecutor:
except AnsibleUndefinedVariable as e: except AnsibleUndefinedVariable as e:
display.deprecated("Skipping task due to undefined Error, in the future this will be a fatal error.: %s" % to_bytes(e)) display.deprecated("Skipping task due to undefined Error, in the future this will be a fatal error.: %s" % to_bytes(e))
return None return None
items = self._shared_loader_obj.lookup_loader.get(self._task.loop, loader=self._loader, templar=templar).run(terms=loop_terms, variables=self._job_vars, wantlist=True)
# get lookup
mylookup = self._shared_loader_obj.lookup_loader.get(self._task.loop, loader=self._loader, templar=templar)
# give lookup task 'context' for subdir (mostly needed for first_found)
for subdir in ['tempalte', 'var', 'file']: #TODO: move this to constants?
if subdir in self._task.name:
break
setattr(mylookup,'_subdir', subdir + 's')
# run lookup
items = mylookup.run(terms=loop_terms, variables=self._job_vars, wantlist=True)
else: else:
raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % self._task.loop) raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % self._task.loop)

View file

@ -19,6 +19,8 @@
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
import os
from ansible.compat.six import iteritems, string_types from ansible.compat.six import iteritems, string_types
from ansible.errors import AnsibleError, AnsibleParserError from ansible.errors import AnsibleError, AnsibleParserError
@ -454,3 +456,24 @@ class Task(Base, Conditional, Taggable, Become):
def _get_attr_loop_control(self): def _get_attr_loop_control(self):
return self._attributes['loop_control'] return self._attributes['loop_control']
def get_search_path(self):
'''
Return the list of paths you should search for files, in order.
This follows role/playbook dependency chain.
'''
path_stack = []
dep_chain = self._block.get_dep_chain()
# inside role: add the dependency chain from current to dependant
if dep_chain:
path_stack.extend(reversed([x._role_path for x in dep_chain]))
# add path of task itself, unless it is already in the list
task_dir = os.path.dirname(self.get_path())
if task_dir not in path_stack:
path_stack.append(task_dir)
return path_stack

View file

@ -831,18 +831,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
to get back the first existing file found. to get back the first existing file found.
''' '''
path_stack = [] path_stack = self._task.get_search_path()
dep_chain = self._task._block.get_dep_chain()
# inside role: add the dependency chain
if dep_chain:
path_stack.extend(reversed([x._role_path for x in dep_chain]))
task_dir = os.path.dirname(self._task.get_path())
# include from diff directory: add it to file path
if not task_dir.endswith('tasks') and task_dir != self._loader.get_basedir():
path_stack.append(task_dir)
result = self._loader.path_dwim_relative_stack(path_stack, dirname, needle) result = self._loader.path_dwim_relative_stack(path_stack, dirname, needle)

View file

@ -22,6 +22,7 @@ __metaclass__ = type
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
from ansible.compat.six import with_metaclass from ansible.compat.six import with_metaclass
from ansible.errors import AnsibleFileNotFound
try: try:
from __main__ import display from __main__ import display
@ -101,3 +102,19 @@ class LookupBase(with_metaclass(ABCMeta, object)):
result_string = to_unicode(result_string) result_string = to_unicode(result_string)
""" """
pass pass
def find_file_in_search_path(self, myvars, subdir, needle):
'''
Return a file (needle) in the task's expected search path.
'''
if 'ansible_search_path' in myvars:
paths = myvars['ansible_search_path']
else:
paths = self.get_basedir(myvars)
result = self._loader.path_dwim_relative_stack(paths, subdir, needle)
if result is None:
raise AnsibleFileNotFound("Unable to find '%s' in expected paths." % needle)
return result

View file

@ -72,8 +72,6 @@ class LookupModule(LookupBase):
def run(self, terms, variables=None, **kwargs): def run(self, terms, variables=None, **kwargs):
basedir = self.get_basedir(variables)
ret = [] ret = []
for term in terms: for term in terms:
@ -100,7 +98,7 @@ class LookupModule(LookupBase):
if paramvals['delimiter'] == 'TAB': if paramvals['delimiter'] == 'TAB':
paramvals['delimiter'] = "\t" paramvals['delimiter'] = "\t"
lookupfile = self._loader.path_dwim_relative(basedir, 'files', paramvals['file']) lookupfile = self.find_file_in_search_path(variables, 'files', paramvals['file'])
var = self.read_csv(lookupfile, key, paramvals['delimiter'], paramvals['encoding'], paramvals['default'], paramvals['col']) var = self.read_csv(lookupfile, key, paramvals['delimiter'], paramvals['encoding'], paramvals['default'], paramvals['col'])
if var is not None: if var is not None:
if type(var) is list: if type(var) is list:

View file

@ -33,18 +33,11 @@ class LookupModule(LookupBase):
ret = [] ret = []
basedir = self.get_basedir(variables)
for term in terms: for term in terms:
display.debug("File lookup term: %s" % term) display.debug("File lookup term: %s" % term)
# Special handling of the file lookup, used primarily when the # Find the file in the expected search path
# lookup is done from a role. If the file isn't found in the lookupfile = self.find_file_in_search_path(variables, 'files', term)
# basedir of the current file, use dwim_relative to look in the
# role/files/ directory, and finally the playbook directory
# itself (which will be relative to the current working dir)
lookupfile = self._loader.path_dwim_relative(basedir, 'files', term)
display.vvvv("File lookup using %s as file" % lookupfile) display.vvvv("File lookup using %s as file" % lookupfile)
try: try:
if lookupfile: if lookupfile:

View file

@ -26,12 +26,10 @@ class LookupModule(LookupBase):
def run(self, terms, variables=None, **kwargs): def run(self, terms, variables=None, **kwargs):
basedir = self.get_basedir(variables)
ret = [] ret = []
for term in terms: for term in terms:
term_file = os.path.basename(term) term_file = os.path.basename(term)
dwimmed_path = self._loader.path_dwim_relative(basedir, 'files', os.path.dirname(term)) dwimmed_path = self.find_file_in_search_path(variables, 'files', os.path.dirname(term))
globbed = glob.glob(os.path.join(dwimmed_path, term_file)) globbed = glob.glob(os.path.join(dwimmed_path, term_file))
ret.extend(g for g in globbed if os.path.isfile(g)) ret.extend(g for g in globbed if os.path.isfile(g))
return ret return ret

View file

@ -118,12 +118,11 @@ __metaclass__ = type
# - ../files/baz # - ../files/baz
# ignore_errors: true # ignore_errors: true
import os import os
from jinja2.exceptions import UndefinedError from jinja2.exceptions import UndefinedError
from ansible.errors import AnsibleLookupError, AnsibleUndefinedVariable from ansible.errors import AnsibleFileNotFound, AnsibleLookupError, AnsibleUndefinedVariable
from ansible.plugins.lookup import LookupBase from ansible.plugins.lookup import LookupBase
from ansible.utils.boolean import boolean from ansible.utils.boolean import boolean
@ -131,7 +130,6 @@ class LookupModule(LookupBase):
def run(self, terms, variables, **kwargs): def run(self, terms, variables, **kwargs):
result = None
anydict = False anydict = False
skip = False skip = False
@ -173,28 +171,20 @@ class LookupModule(LookupBase):
else: else:
total_search = self._flatten(terms) total_search = self._flatten(terms)
roledir = variables.get('roledir')
for fn in total_search: for fn in total_search:
try: try:
fn = self._templar.template(fn) fn = self._templar.template(fn)
except (AnsibleUndefinedVariable, UndefinedError) as e: except (AnsibleUndefinedVariable, UndefinedError):
continue continue
if os.path.isabs(fn) and os.path.exists(fn): # get subdir if set by task executor, default to files otherwise
return [fn] subdir = getattr(self, '_subdir', 'files')
else: path = None
if roledir is not None: try:
# check the templates and vars directories too,if they exist path = self.find_file_in_search_path(variables, subdir, fn)
for subdir in ('templates', 'vars', 'files'): return [path]
path = self._loader.path_dwim_relative(roledir, subdir, fn) except AnsibleFileNotFound:
if os.path.exists(path): continue
return [path]
# if none of the above were found, just check the
# current filename against the current dir
path = self._loader.path_dwim(fn)
if os.path.exists(path):
return [path]
else: else:
if skip: if skip:
return [] return []

View file

@ -107,7 +107,7 @@ class LookupModule(LookupBase):
except (ValueError, AssertionError) as e: except (ValueError, AssertionError) as e:
raise AnsibleError(e) raise AnsibleError(e)
path = self._loader.path_dwim_relative(basedir, 'files', paramvals['file']) path = self.find_file_in_search_path(variables, 'files', paramvals['file'])
if paramvals['type'] == "properties": if paramvals['type'] == "properties":
var = self.read_properties(path, key, paramvals['default'], paramvals['re']) var = self.read_properties(path, key, paramvals['default'], paramvals['re'])
else: else:

View file

@ -18,7 +18,6 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
import shelve import shelve
import os
from ansible.errors import AnsibleError from ansible.errors import AnsibleError
from ansible.plugins.lookup import LookupBase from ansible.plugins.lookup import LookupBase
@ -43,9 +42,6 @@ class LookupModule(LookupBase):
ret = [] ret = []
for term in terms: for term in terms:
playbook_path = None
relative_path = None
paramvals = {"file": None, "key": None} paramvals = {"file": None, "key": None}
params = term.split() params = term.split()
@ -59,24 +55,18 @@ class LookupModule(LookupBase):
# In case "file" or "key" are not present # In case "file" or "key" are not present
raise AnsibleError(e) raise AnsibleError(e)
file = paramvals['file']
key = paramvals['key'] key = paramvals['key']
basedir_path = self._loader.path_dwim(file)
# Search also in the role/files directory and in the playbook directory # Search also in the role/files directory and in the playbook directory
if 'role_path' in variables: shelvefile = self.find_file_in_search_path(variables, 'files', paramvals['file'])
relative_path = self._loader.path_dwim_relative(variables['role_path'], 'files', file)
if 'playbook_dir' in variables:
playbook_path = self._loader.path_dwim_relative(variables['playbook_dir'],'files', file)
for path in (basedir_path, relative_path, playbook_path): if shelvefile:
if path and os.path.exists(path): res = self.read_shelve(shelvefile, key)
res = self.read_shelve(path, key) if res is None:
if res is None: raise AnsibleError("Key %s not found in shelve file %s" % (key, file))
raise AnsibleError("Key %s not found in shelve file %s" % (key, file)) # Convert the value read to string
# Convert the value read to string ret.append(str(res))
ret.append(str(res)) break
break
else: else:
raise AnsibleError("Could not locate shelve file in lookup: %s" % file) raise AnsibleError("Could not locate shelve file in lookup: %s" % file)

View file

@ -19,7 +19,6 @@ __metaclass__ = type
import os import os
from ansible import constants as C
from ansible.errors import AnsibleError from ansible.errors import AnsibleError
from ansible.plugins.lookup import LookupBase from ansible.plugins.lookup import LookupBase
from ansible.utils.unicode import to_unicode from ansible.utils.unicode import to_unicode
@ -36,26 +35,25 @@ class LookupModule(LookupBase):
def run(self, terms, variables, **kwargs): def run(self, terms, variables, **kwargs):
convert_data_p = kwargs.get('convert_data', True) convert_data_p = kwargs.get('convert_data', True)
basedir = self.get_basedir(variables)
ret = [] ret = []
for term in terms: for term in terms:
display.debug("File lookup term: %s" % term) display.debug("File lookup term: %s" % term)
lookupfile = self._loader.path_dwim_relative(basedir, 'templates', term) lookupfile = self.find_file_in_search_path(variables, 'templates', term)
display.vvvv("File lookup using %s as file" % lookupfile) display.vvvv("File lookup using %s as file" % lookupfile)
if lookupfile and os.path.exists(lookupfile): if lookupfile:
with open(lookupfile, 'r') as f: with open(lookupfile, 'r') as f:
template_data = to_unicode(f.read()) template_data = to_unicode(f.read())
searchpath = [self._loader._basedir, os.path.dirname(lookupfile)] # set jinja2 internal search path for includes
if 'role_path' in variables: if 'ansible_search_path' in variables:
if C.DEFAULT_ROLES_PATH: searchpath = variables['ansible_search_path']
searchpath[:0] = C.DEFAULT_ROLES_PATH else:
searchpath.insert(1, variables['role_path']) searchpath = [self._loader._basedir, os.path.dirname(lookupfile)]
self._templar.environment.loader.searchpath = searchpath self._templar.environment.loader.searchpath = searchpath
# do the templating
res = self._templar.template(template_data, preserve_trailing_newlines=True,convert_data=convert_data_p) res = self._templar.template(template_data, preserve_trailing_newlines=True,convert_data=convert_data_p)
ret.append(res) ret.append(res)
else: else: