Merge pull request #28226 from plastikos/improvement-module_package_collision

Improvement module package collision
This commit is contained in:
Mike Place 2015-10-26 07:04:47 -06:00
commit 05ed7d0bdc
2 changed files with 109 additions and 16 deletions

View File

@ -985,7 +985,8 @@ class LazyLoader(salt.utils.lazy.LazyDict):
'''
# map of suffix to description for imp
self.suffix_map = {}
suffix_order = [] # local list to determine precedence of extensions
suffix_order = [''] # local list to determine precedence of extensions
# Prefer packages (directories) over modules (single files)!
for (suffix, mode, kind) in SUFFIXES:
self.suffix_map[suffix] = (suffix, mode, kind)
suffix_order.append(suffix)
@ -1014,47 +1015,51 @@ class LazyLoader(salt.utils.lazy.LazyDict):
try:
files = os.listdir(mod_dir)
except OSError:
continue
continue # Next mod_dir
for filename in files:
try:
if filename.startswith('_'):
# skip private modules
# log messages omitted for obviousness
continue
continue # Next filename
f_noext, ext = os.path.splitext(filename)
# make sure it is a suffix we support
if ext not in self.suffix_map:
continue
continue # Next filename
if f_noext in self.disabled:
log.trace(
'Skipping {0}, it is disabled by configuration'.format(
filename
)
)
continue
continue # Next filename
fpath = os.path.join(mod_dir, filename)
# if its a directory, lets allow us to load that
if ext == '':
# is there something __init__?
subfiles = os.listdir(fpath)
sub_path = None
for suffix in suffix_order:
if '' == suffix:
continue # Next suffix (__init__ must have a suffix)
init_file = '__init__{0}'.format(suffix)
if init_file in subfiles:
sub_path = os.path.join(fpath, init_file)
break
if sub_path is not None:
self.file_mapping[f_noext] = (fpath, ext)
else:
continue # Next filename
# if we don't have it, we want it
elif f_noext not in self.file_mapping:
self.file_mapping[f_noext] = (fpath, ext)
# if we do, we want it if we have a higher precidence ext
else:
if f_noext in self.file_mapping:
curr_ext = self.file_mapping[f_noext][1]
#log.debug("****** curr_ext={0} ext={1} suffix_order={2}".format(curr_ext, ext, suffix_order))
if curr_ext and suffix_order.index(ext) < suffix_order.index(curr_ext):
self.file_mapping[f_noext] = (fpath, ext)
if '' in (curr_ext, ext) and curr_ext != ext:
log.error('Module/package collision: {0!r} and {1!r}'.format(
fpath, self.file_mapping[f_noext][0]
))
if suffix_order.index(ext) >= suffix_order.index(curr_ext):
continue # Next filename
# Made it this far - add it
self.file_mapping[f_noext] = (fpath, ext)
except OSError:
continue
for smod in self.static_modules:

View File

@ -435,6 +435,94 @@ class LazyLoaderSubmodReloadingTest(TestCase):
self.assertNotIn(self.module_key, self.loader)
mod_template = '''
def test():
return ({val})
'''
class LazyLoaderModulePackageTest(TestCase):
'''
Test the loader of salt with changing modules
'''
module_name = 'loadertestmodpkg'
module_key = 'loadertestmodpkg.test'
def setUp(self):
self.opts = _config = minion_config(None)
self.opts['grains'] = grains(self.opts)
self.tmp_dir = tempfile.mkdtemp(dir=tests.integration.TMP)
dirs = _module_dirs(self.opts, 'modules', 'module')
dirs.append(self.tmp_dir)
self.loader = LazyLoader(dirs,
self.opts,
tag='module')
def tearDown(self):
shutil.rmtree(self.tmp_dir)
def update_pyfile(self, pyfile, contents):
dirname = os.path.dirname(pyfile)
if not os.path.exists(dirname):
os.makedirs(dirname)
with open(pyfile, 'wb') as fh:
fh.write(contents)
fh.flush()
os.fsync(fh.fileno()) # flush to disk
# pyc files don't like it when we change the original quickly
# since the header bytes only contain the timestamp (granularity of seconds)
# TODO: don't write them? Is *much* slower on re-load (~3x)
# https://docs.python.org/2/library/sys.html#sys.dont_write_bytecode
try:
os.unlink(pyfile + 'c')
except OSError:
pass
def rm_pyfile(self, pyfile):
os.unlink(pyfile)
os.unlink(pyfile + 'c')
def update_module(self, relative_path, contents):
self.update_pyfile(os.path.join(self.tmp_dir, relative_path), contents)
def rm_module(self, relative_path):
self.rm_pyfile(os.path.join(self.tmp_dir, relative_path))
def test_module(self):
# ensure it doesn't exist
self.assertNotIn('foo', self.loader)
self.assertNotIn('foo.test', self.loader)
self.update_module('foo.py', mod_template.format(val=1))
self.loader.clear()
self.assertIn('foo.test', self.loader)
self.assertEqual(self.loader['foo.test'](), 1)
def test_package(self):
# ensure it doesn't exist
self.assertNotIn('foo', self.loader)
self.assertNotIn('foo.test', self.loader)
self.update_module('foo/__init__.py', mod_template.format(val=2))
self.loader.clear()
self.assertIn('foo.test', self.loader)
self.assertEqual(self.loader['foo.test'](), 2)
def test_module_package_collision(self):
# ensure it doesn't exist
self.assertNotIn('foo', self.loader)
self.assertNotIn('foo.test', self.loader)
self.update_module('foo.py', mod_template.format(val=3))
self.loader.clear()
self.assertIn('foo.test', self.loader)
self.assertEqual(self.loader['foo.test'](), 3)
self.update_module('foo/__init__.py', mod_template.format(val=4))
self.loader.clear()
self.assertIn('foo.test', self.loader)
self.assertEqual(self.loader['foo.test'](), 4)
deep_init_base = '''
import top_lib
import top_lib.mid_lib