2015-02-05 03:29:29 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
'''
|
|
|
|
unit.loader
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Test Salt's loader
|
|
|
|
'''
|
|
|
|
|
|
|
|
# Import Python libs
|
2017-12-15 18:14:18 +00:00
|
|
|
from __future__ import absolute_import, print_function, unicode_literals
|
2015-02-05 03:29:29 +00:00
|
|
|
import inspect
|
2017-02-10 01:57:40 +00:00
|
|
|
import logging
|
2015-02-05 03:29:29 +00:00
|
|
|
import tempfile
|
|
|
|
import shutil
|
|
|
|
import os
|
2015-02-07 19:19:22 +00:00
|
|
|
import collections
|
2017-03-15 01:50:07 +00:00
|
|
|
import sys
|
|
|
|
import imp
|
2017-02-19 15:35:30 +00:00
|
|
|
import copy
|
2017-02-10 01:57:40 +00:00
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
# Import Salt Testing libs
|
2017-02-27 13:58:07 +00:00
|
|
|
from tests.support.unit import TestCase
|
2017-02-19 15:35:30 +00:00
|
|
|
from tests.support.mock import patch
|
2017-03-15 01:50:07 +00:00
|
|
|
from tests.support.paths import TMP
|
2015-02-06 19:49:28 +00:00
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
# Import Salt libs
|
2017-02-19 15:35:30 +00:00
|
|
|
import salt.config
|
2017-07-18 16:31:01 +00:00
|
|
|
import salt.utils.files
|
Use explicit unicode strings + break up salt.utils
This PR is part of what will be an ongoing effort to use explicit
unicode strings in Salt. Because Python 3 does not suport Python 2's raw
unicode string syntax (i.e. `ur'\d+'`), we must use
`salt.utils.locales.sdecode()` to ensure that the raw string is unicode.
However, because of how `salt/utils/__init__.py` has evolved into the
hulking monstrosity it is today, this means importing a large module in
places where it is not needed, which could negatively impact
performance. For this reason, this PR also breaks out some of the
functions from `salt/utils/__init__.py` into new/existing modules under
`salt/utils/`. The long term goal will be that the modules within this
directory do not depend on importing `salt.utils`.
A summary of the changes in this PR is as follows:
* Moves the following functions from `salt.utils` to new locations
(including a deprecation warning if invoked from `salt.utils`):
`to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`,
`dequote`, `is_hex`, `is_bin_str`, `rand_string`,
`contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`,
`which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`,
`is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`,
`is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`,
`is_openbsd`, `is_aix`
* Moves the functions already deprecated by @rallytime to the bottom of
`salt/utils/__init__.py` for better organization, so we can keep the
deprecated ones separate from the ones yet to be deprecated as we
continue to break up `salt.utils`
* Updates `salt/*.py` and all files under `salt/client/` to use explicit
unicode string literals.
* Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils
import foo` becomes `import salt.utils.foo as foo`).
* Renames the `test.rand_str` function to `test.random_hash` to more
accurately reflect what it does
* Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`)
such that it returns a string matching the passed size. Previously
this function would get `size` bytes from `os.urandom()`,
base64-encode it, and return the result, which would in most cases not
be equal to the passed size.
2017-07-25 01:47:15 +00:00
|
|
|
import salt.utils.stringutils
|
2015-02-06 17:04:52 +00:00
|
|
|
# pylint: disable=import-error,no-name-in-module,redefined-builtin
|
Use explicit unicode strings + break up salt.utils
This PR is part of what will be an ongoing effort to use explicit
unicode strings in Salt. Because Python 3 does not suport Python 2's raw
unicode string syntax (i.e. `ur'\d+'`), we must use
`salt.utils.locales.sdecode()` to ensure that the raw string is unicode.
However, because of how `salt/utils/__init__.py` has evolved into the
hulking monstrosity it is today, this means importing a large module in
places where it is not needed, which could negatively impact
performance. For this reason, this PR also breaks out some of the
functions from `salt/utils/__init__.py` into new/existing modules under
`salt/utils/`. The long term goal will be that the modules within this
directory do not depend on importing `salt.utils`.
A summary of the changes in this PR is as follows:
* Moves the following functions from `salt.utils` to new locations
(including a deprecation warning if invoked from `salt.utils`):
`to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`,
`dequote`, `is_hex`, `is_bin_str`, `rand_string`,
`contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`,
`which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`,
`is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`,
`is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`,
`is_openbsd`, `is_aix`
* Moves the functions already deprecated by @rallytime to the bottom of
`salt/utils/__init__.py` for better organization, so we can keep the
deprecated ones separate from the ones yet to be deprecated as we
continue to break up `salt.utils`
* Updates `salt/*.py` and all files under `salt/client/` to use explicit
unicode string literals.
* Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils
import foo` becomes `import salt.utils.foo as foo`).
* Renames the `test.rand_str` function to `test.random_hash` to more
accurately reflect what it does
* Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`)
such that it returns a string matching the passed size. Previously
this function would get `size` bytes from `os.urandom()`,
base64-encode it, and return the result, which would in most cases not
be equal to the passed size.
2017-07-25 01:47:15 +00:00
|
|
|
from salt.ext import six
|
2015-02-06 17:04:52 +00:00
|
|
|
from salt.ext.six.moves import range
|
|
|
|
# pylint: enable=no-name-in-module,redefined-builtin
|
2015-02-05 03:29:29 +00:00
|
|
|
|
2016-07-07 21:03:45 +00:00
|
|
|
from salt.loader import LazyLoader, _module_dirs, grains, utils, proxy, minion_mods
|
2015-02-05 03:29:29 +00:00
|
|
|
|
2017-02-19 15:35:30 +00:00
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
|
|
|
|
def remove_bytecode(module_path):
|
|
|
|
paths = [module_path + 'c']
|
|
|
|
if hasattr(imp, 'get_tag'):
|
|
|
|
modname, ext = os.path.splitext(module_path.split(os.sep)[-1])
|
|
|
|
paths.append(
|
|
|
|
os.path.join(os.path.dirname(module_path),
|
|
|
|
'__pycache__',
|
|
|
|
'{}.{}.pyc'.format(modname, imp.get_tag())))
|
|
|
|
for path in paths:
|
|
|
|
if os.path.exists(path):
|
|
|
|
os.unlink(path)
|
|
|
|
|
|
|
|
|
2016-03-15 03:14:50 +00:00
|
|
|
loader_template = '''
|
|
|
|
import os
|
|
|
|
from salt.utils.decorators import depends
|
|
|
|
|
|
|
|
@depends('os')
|
|
|
|
def loaded():
|
|
|
|
return True
|
|
|
|
|
|
|
|
@depends('non_existantmodulename')
|
|
|
|
def not_loaded():
|
|
|
|
return True
|
|
|
|
'''
|
|
|
|
|
2016-03-15 20:05:09 +00:00
|
|
|
|
2016-03-15 03:14:50 +00:00
|
|
|
class LazyLoaderTest(TestCase):
|
|
|
|
'''
|
|
|
|
Test the loader
|
|
|
|
'''
|
|
|
|
module_name = 'lazyloadertest'
|
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
2017-02-19 15:35:30 +00:00
|
|
|
cls.opts = salt.config.minion_config(None)
|
2017-03-15 01:50:07 +00:00
|
|
|
cls.opts['grains'] = grains(cls.opts)
|
|
|
|
if not os.path.isdir(TMP):
|
|
|
|
os.makedirs(TMP)
|
2016-03-15 03:14:50 +00:00
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
def setUp(self):
|
2016-03-15 03:14:50 +00:00
|
|
|
# Setup the module
|
2017-03-15 01:50:07 +00:00
|
|
|
self.module_dir = tempfile.mkdtemp(dir=TMP)
|
2016-03-15 03:14:50 +00:00
|
|
|
self.module_file = os.path.join(self.module_dir,
|
|
|
|
'{0}.py'.format(self.module_name))
|
2017-07-18 16:31:01 +00:00
|
|
|
with salt.utils.files.fopen(self.module_file, 'w') as fh:
|
2017-12-14 06:26:39 +00:00
|
|
|
fh.write(salt.utils.stringutils.to_str(loader_template))
|
2016-03-15 03:14:50 +00:00
|
|
|
fh.flush()
|
|
|
|
os.fsync(fh.fileno())
|
|
|
|
|
|
|
|
# Invoke the loader
|
2017-02-19 15:35:30 +00:00
|
|
|
self.loader = LazyLoader([self.module_dir], copy.deepcopy(self.opts), tag='module')
|
2016-03-15 03:14:50 +00:00
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
shutil.rmtree(self.module_dir)
|
2017-03-06 16:45:59 +00:00
|
|
|
if os.path.isdir(self.module_dir):
|
|
|
|
shutil.rmtree(self.module_dir)
|
|
|
|
del self.module_dir
|
|
|
|
del self.module_file
|
|
|
|
del self.loader
|
2016-03-15 03:14:50 +00:00
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def tearDownClass(cls):
|
|
|
|
del cls.opts
|
|
|
|
|
2016-03-15 03:14:50 +00:00
|
|
|
def test_depends(self):
|
|
|
|
'''
|
|
|
|
Test that the depends decorator works properly
|
|
|
|
'''
|
|
|
|
# Make sure depends correctly allowed a function to load. If this
|
|
|
|
# results in a KeyError, the decorator is broken.
|
|
|
|
self.assertTrue(
|
|
|
|
inspect.isfunction(
|
|
|
|
self.loader[self.module_name + '.loaded']
|
|
|
|
)
|
|
|
|
)
|
|
|
|
# Make sure depends correctly kept a function from loading
|
|
|
|
self.assertTrue(self.module_name + '.not_loaded' not in self.loader)
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
|
|
|
|
class LazyLoaderVirtualEnabledTest(TestCase):
|
|
|
|
'''
|
|
|
|
Test the base loader of salt.
|
|
|
|
'''
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
2017-02-19 15:35:30 +00:00
|
|
|
cls.opts = salt.config.minion_config(None)
|
2017-03-15 01:50:07 +00:00
|
|
|
cls.opts['disable_modules'] = ['pillar']
|
|
|
|
cls.opts['grains'] = grains(cls.opts)
|
2015-02-06 16:52:33 +00:00
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
def setUp(self):
|
2017-02-19 15:35:30 +00:00
|
|
|
self.loader = LazyLoader(_module_dirs(copy.deepcopy(self.opts), 'modules', 'module'),
|
|
|
|
copy.deepcopy(self.opts),
|
2015-02-06 18:50:03 +00:00
|
|
|
tag='module')
|
2015-02-05 17:32:30 +00:00
|
|
|
|
2017-03-06 16:45:59 +00:00
|
|
|
def tearDown(self):
|
|
|
|
del self.loader
|
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def tearDownClass(cls):
|
|
|
|
del cls.opts
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
def test_basic(self):
|
|
|
|
'''
|
|
|
|
Ensure that it only loads stuff when needed
|
|
|
|
'''
|
|
|
|
# make sure it starts empty
|
|
|
|
self.assertEqual(self.loader._dict, {})
|
|
|
|
# get something, and make sure its a func
|
|
|
|
self.assertTrue(inspect.isfunction(self.loader['test.ping']))
|
|
|
|
|
|
|
|
# make sure we only loaded "test" functions
|
2015-02-06 14:28:43 +00:00
|
|
|
for key, val in six.iteritems(self.loader._dict):
|
2015-02-05 03:29:29 +00:00
|
|
|
self.assertEqual(key.split('.', 1)[0], 'test')
|
|
|
|
|
|
|
|
# make sure the depends thing worked (double check of the depends testing,
|
|
|
|
# since the loader does the calling magically
|
|
|
|
self.assertFalse('test.missing_func' in self.loader._dict)
|
|
|
|
|
2015-02-12 21:48:04 +00:00
|
|
|
def test_badkey(self):
|
|
|
|
with self.assertRaises(KeyError):
|
2015-02-12 23:53:19 +00:00
|
|
|
self.loader[None] # pylint: disable=W0104
|
2015-02-12 21:48:04 +00:00
|
|
|
|
|
|
|
with self.assertRaises(KeyError):
|
2015-02-12 23:53:19 +00:00
|
|
|
self.loader[1] # pylint: disable=W0104
|
2015-02-12 21:48:04 +00:00
|
|
|
|
2015-02-06 16:52:33 +00:00
|
|
|
def test_disable(self):
|
|
|
|
self.assertNotIn('pillar.items', self.loader)
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
def test_len_load(self):
|
|
|
|
'''
|
|
|
|
Since LazyLoader is a MutableMapping, if someone asks for len() we have
|
|
|
|
to load all
|
|
|
|
'''
|
|
|
|
self.assertEqual(self.loader._dict, {})
|
|
|
|
len(self.loader) # force a load all
|
|
|
|
self.assertNotEqual(self.loader._dict, {})
|
|
|
|
|
|
|
|
def test_iter_load(self):
|
|
|
|
'''
|
|
|
|
Since LazyLoader is a MutableMapping, if someone asks to iterate we have
|
|
|
|
to load all
|
|
|
|
'''
|
|
|
|
self.assertEqual(self.loader._dict, {})
|
|
|
|
# force a load all
|
2015-02-06 14:28:43 +00:00
|
|
|
for key, func in six.iteritems(self.loader):
|
2015-02-05 03:29:29 +00:00
|
|
|
break
|
|
|
|
self.assertNotEqual(self.loader._dict, {})
|
|
|
|
|
|
|
|
def test_context(self):
|
|
|
|
'''
|
|
|
|
Make sure context is shared across modules
|
|
|
|
'''
|
|
|
|
# make sure it starts empty
|
|
|
|
self.assertEqual(self.loader._dict, {})
|
|
|
|
# get something, and make sure its a func
|
|
|
|
func = self.loader['test.ping']
|
2017-02-19 15:35:30 +00:00
|
|
|
with patch.dict(func.__globals__['__context__'], {'foo': 'bar'}):
|
|
|
|
self.assertEqual(self.loader['test.echo'].__globals__['__context__']['foo'], 'bar')
|
|
|
|
self.assertEqual(self.loader['grains.get'].__globals__['__context__']['foo'], 'bar')
|
2015-02-05 03:29:29 +00:00
|
|
|
|
|
|
|
def test_globals(self):
|
|
|
|
func_globals = self.loader['test.ping'].__globals__
|
|
|
|
self.assertEqual(func_globals['__grains__'], self.opts.get('grains', {}))
|
|
|
|
self.assertEqual(func_globals['__pillar__'], self.opts.get('pillar', {}))
|
|
|
|
# the opts passed into modules is at least a subset of the whole opts
|
2015-02-06 14:28:43 +00:00
|
|
|
for key, val in six.iteritems(func_globals['__opts__']):
|
2017-02-19 15:35:30 +00:00
|
|
|
if key in salt.config.DEFAULT_MASTER_OPTS and key not in salt.config.DEFAULT_MINION_OPTS:
|
|
|
|
# We loaded the minion opts, but somewhere in the code, the master options got pulled in
|
|
|
|
# Let's just not check for equality since the option won't even exist in the loaded
|
|
|
|
# minion options
|
|
|
|
continue
|
|
|
|
if key not in salt.config.DEFAULT_MASTER_OPTS and key not in salt.config.DEFAULT_MINION_OPTS:
|
|
|
|
# This isn't even a default configuration setting, lets carry on
|
|
|
|
continue
|
2015-04-22 05:39:17 +00:00
|
|
|
self.assertEqual(self.opts[key], val)
|
2015-02-05 03:29:29 +00:00
|
|
|
|
|
|
|
def test_pack(self):
|
|
|
|
self.loader.pack['__foo__'] = 'bar'
|
|
|
|
func_globals = self.loader['test.ping'].__globals__
|
|
|
|
self.assertEqual(func_globals['__foo__'], 'bar')
|
|
|
|
|
|
|
|
def test_virtual(self):
|
2015-02-05 17:32:30 +00:00
|
|
|
self.assertNotIn('test_virtual.ping', self.loader)
|
2015-02-05 03:29:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
class LazyLoaderVirtualDisabledTest(TestCase):
|
|
|
|
'''
|
|
|
|
Test the loader of salt without __virtual__
|
|
|
|
'''
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
2017-02-19 15:35:30 +00:00
|
|
|
cls.opts = salt.config.minion_config(None)
|
2017-03-15 01:50:07 +00:00
|
|
|
cls.opts['grains'] = grains(cls.opts)
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
def setUp(self):
|
2017-02-19 15:35:30 +00:00
|
|
|
self.loader = LazyLoader(_module_dirs(copy.deepcopy(self.opts), 'modules', 'module'),
|
|
|
|
copy.deepcopy(self.opts),
|
2015-02-06 18:50:03 +00:00
|
|
|
tag='module',
|
2015-02-06 14:28:43 +00:00
|
|
|
virtual_enable=False)
|
2015-02-05 17:32:30 +00:00
|
|
|
|
2017-03-06 16:45:59 +00:00
|
|
|
def tearDown(self):
|
|
|
|
del self.loader
|
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def tearDownClass(cls):
|
|
|
|
del cls.opts
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
def test_virtual(self):
|
|
|
|
self.assertTrue(inspect.isfunction(self.loader['test_virtual.ping']))
|
|
|
|
|
|
|
|
|
|
|
|
class LazyLoaderWhitelistTest(TestCase):
|
|
|
|
'''
|
|
|
|
Test the loader of salt with a whitelist
|
|
|
|
'''
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
2017-02-19 15:35:30 +00:00
|
|
|
cls.opts = salt.config.minion_config(None)
|
2017-03-15 01:50:07 +00:00
|
|
|
cls.opts['grains'] = grains(cls.opts)
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
def setUp(self):
|
2017-02-19 15:35:30 +00:00
|
|
|
self.loader = LazyLoader(_module_dirs(copy.deepcopy(self.opts), 'modules', 'module'),
|
|
|
|
copy.deepcopy(self.opts),
|
2015-02-06 18:50:03 +00:00
|
|
|
tag='module',
|
2015-02-06 14:28:43 +00:00
|
|
|
whitelist=['test', 'pillar'])
|
2015-02-05 17:32:30 +00:00
|
|
|
|
2017-03-06 16:45:59 +00:00
|
|
|
def tearDown(self):
|
|
|
|
del self.loader
|
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def tearDownClass(cls):
|
|
|
|
del cls.opts
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
def test_whitelist(self):
|
|
|
|
self.assertTrue(inspect.isfunction(self.loader['test.ping']))
|
|
|
|
self.assertTrue(inspect.isfunction(self.loader['pillar.get']))
|
|
|
|
|
2015-02-05 17:32:30 +00:00
|
|
|
self.assertNotIn('grains.get', self.loader)
|
2015-02-05 03:29:29 +00:00
|
|
|
|
2015-02-05 16:05:47 +00:00
|
|
|
|
2017-11-10 18:41:45 +00:00
|
|
|
class LazyLoaderSingleItem(TestCase):
|
|
|
|
'''
|
|
|
|
Test loading a single item via the _load() function
|
|
|
|
'''
|
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
|
|
|
cls.opts = salt.config.minion_config(None)
|
|
|
|
cls.opts['grains'] = grains(cls.opts)
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.loader = LazyLoader(_module_dirs(copy.deepcopy(self.opts), 'modules', 'module'),
|
|
|
|
copy.deepcopy(self.opts),
|
|
|
|
tag='module')
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
del self.loader
|
|
|
|
|
|
|
|
def test_single_item_no_dot(self):
|
|
|
|
'''
|
|
|
|
Checks that a KeyError is raised when the function key does not contain a '.'
|
|
|
|
'''
|
|
|
|
with self.assertRaises(KeyError) as err:
|
|
|
|
inspect.isfunction(self.loader['testing_no_dot'])
|
|
|
|
|
|
|
|
if six.PY2:
|
|
|
|
self.assertEqual(err.exception[0],
|
|
|
|
'The key \'%s\' should contain a \'.\'')
|
|
|
|
else:
|
2017-12-14 06:26:39 +00:00
|
|
|
self.assertEqual(
|
|
|
|
six.text_type(err.exception),
|
|
|
|
six.text_type(("The key '%s' should contain a '.'", 'testing_no_dot'))
|
|
|
|
)
|
2017-11-10 18:41:45 +00:00
|
|
|
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
module_template = '''
|
2015-02-05 16:29:02 +00:00
|
|
|
__load__ = ['test', 'test_alias']
|
|
|
|
__func_alias__ = dict(test_alias='working_alias')
|
2015-02-05 16:05:47 +00:00
|
|
|
from salt.utils.decorators import depends
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
def test():
|
|
|
|
return {count}
|
2015-02-05 16:05:47 +00:00
|
|
|
|
2015-02-05 16:29:02 +00:00
|
|
|
def test_alias():
|
|
|
|
return True
|
|
|
|
|
2015-02-05 16:05:47 +00:00
|
|
|
def test2():
|
|
|
|
return True
|
|
|
|
|
|
|
|
@depends('non_existantmodulename')
|
|
|
|
def test3():
|
|
|
|
return True
|
|
|
|
|
|
|
|
@depends('non_existantmodulename', fallback_function=test)
|
|
|
|
def test4():
|
|
|
|
return True
|
2015-02-05 03:29:29 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
class LazyLoaderReloadingTest(TestCase):
|
|
|
|
'''
|
|
|
|
Test the loader of salt with changing modules
|
|
|
|
'''
|
|
|
|
module_name = 'loadertest'
|
|
|
|
module_key = 'loadertest.test'
|
2015-02-05 17:32:30 +00:00
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
2017-02-19 15:35:30 +00:00
|
|
|
cls.opts = salt.config.minion_config(None)
|
2017-03-15 01:50:07 +00:00
|
|
|
cls.opts['grains'] = grains(cls.opts)
|
|
|
|
if not os.path.isdir(TMP):
|
|
|
|
os.makedirs(TMP)
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
def setUp(self):
|
2017-03-15 01:50:07 +00:00
|
|
|
self.tmp_dir = tempfile.mkdtemp(dir=TMP)
|
2015-02-05 03:29:29 +00:00
|
|
|
|
|
|
|
self.count = 0
|
2017-02-19 15:35:30 +00:00
|
|
|
opts = copy.deepcopy(self.opts)
|
|
|
|
dirs = _module_dirs(opts, 'modules', 'module')
|
2015-02-05 03:29:29 +00:00
|
|
|
dirs.append(self.tmp_dir)
|
2017-02-19 15:35:30 +00:00
|
|
|
self.utils = utils(opts)
|
|
|
|
self.proxy = proxy(opts)
|
|
|
|
self.minion_mods = minion_mods(opts)
|
2015-02-05 03:29:29 +00:00
|
|
|
self.loader = LazyLoader(dirs,
|
2017-02-19 15:35:30 +00:00
|
|
|
opts,
|
2016-07-07 21:03:45 +00:00
|
|
|
tag='module',
|
|
|
|
pack={'__utils__': self.utils,
|
|
|
|
'__proxy__': self.proxy,
|
|
|
|
'__salt__': self.minion_mods})
|
2015-02-05 03:29:29 +00:00
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
shutil.rmtree(self.tmp_dir)
|
2017-03-15 01:50:07 +00:00
|
|
|
for attrname in ('tmp_dir', 'utils', 'proxy', 'loader', 'minion_mods', 'utils'):
|
2017-03-06 16:45:59 +00:00
|
|
|
try:
|
|
|
|
delattr(self, attrname)
|
|
|
|
except AttributeError:
|
|
|
|
continue
|
2015-02-05 03:29:29 +00:00
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def tearDownClass(cls):
|
|
|
|
del cls.opts
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
def update_module(self):
|
|
|
|
self.count += 1
|
2017-07-18 16:31:01 +00:00
|
|
|
with salt.utils.files.fopen(self.module_path, 'wb') as fh:
|
2016-08-03 22:21:10 +00:00
|
|
|
fh.write(
|
Use explicit unicode strings + break up salt.utils
This PR is part of what will be an ongoing effort to use explicit
unicode strings in Salt. Because Python 3 does not suport Python 2's raw
unicode string syntax (i.e. `ur'\d+'`), we must use
`salt.utils.locales.sdecode()` to ensure that the raw string is unicode.
However, because of how `salt/utils/__init__.py` has evolved into the
hulking monstrosity it is today, this means importing a large module in
places where it is not needed, which could negatively impact
performance. For this reason, this PR also breaks out some of the
functions from `salt/utils/__init__.py` into new/existing modules under
`salt/utils/`. The long term goal will be that the modules within this
directory do not depend on importing `salt.utils`.
A summary of the changes in this PR is as follows:
* Moves the following functions from `salt.utils` to new locations
(including a deprecation warning if invoked from `salt.utils`):
`to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`,
`dequote`, `is_hex`, `is_bin_str`, `rand_string`,
`contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`,
`which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`,
`is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`,
`is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`,
`is_openbsd`, `is_aix`
* Moves the functions already deprecated by @rallytime to the bottom of
`salt/utils/__init__.py` for better organization, so we can keep the
deprecated ones separate from the ones yet to be deprecated as we
continue to break up `salt.utils`
* Updates `salt/*.py` and all files under `salt/client/` to use explicit
unicode string literals.
* Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils
import foo` becomes `import salt.utils.foo as foo`).
* Renames the `test.rand_str` function to `test.random_hash` to more
accurately reflect what it does
* Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`)
such that it returns a string matching the passed size. Previously
this function would get `size` bytes from `os.urandom()`,
base64-encode it, and return the result, which would in most cases not
be equal to the passed size.
2017-07-25 01:47:15 +00:00
|
|
|
salt.utils.stringutils.to_bytes(
|
2016-08-03 22:21:10 +00:00
|
|
|
module_template.format(count=self.count)
|
|
|
|
)
|
|
|
|
)
|
2015-02-05 03:29:29 +00:00
|
|
|
fh.flush()
|
|
|
|
os.fsync(fh.fileno()) # flush to disk
|
|
|
|
|
|
|
|
# pyc files don't like it when we change the original quickly
|
2015-02-05 16:05:47 +00:00
|
|
|
# since the header bytes only contain the timestamp (granularity of seconds)
|
|
|
|
# TODO: don't write them? Is *much* slower on re-load (~3x)
|
2015-02-05 03:29:29 +00:00
|
|
|
# https://docs.python.org/2/library/sys.html#sys.dont_write_bytecode
|
2017-03-15 01:50:07 +00:00
|
|
|
remove_bytecode(self.module_path)
|
2015-02-05 03:29:29 +00:00
|
|
|
|
|
|
|
def rm_module(self):
|
|
|
|
os.unlink(self.module_path)
|
2017-03-15 01:50:07 +00:00
|
|
|
remove_bytecode(self.module_path)
|
2015-02-05 03:29:29 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def module_path(self):
|
|
|
|
return os.path.join(self.tmp_dir, '{0}.py'.format(self.module_name))
|
|
|
|
|
2015-02-05 16:29:02 +00:00
|
|
|
def test_alias(self):
|
|
|
|
'''
|
|
|
|
Make sure that you can access alias-d modules
|
|
|
|
'''
|
|
|
|
# ensure it doesn't exist
|
2015-02-05 17:32:30 +00:00
|
|
|
self.assertNotIn(self.module_key, self.loader)
|
2015-02-05 16:29:02 +00:00
|
|
|
|
|
|
|
self.update_module()
|
2015-02-05 17:32:30 +00:00
|
|
|
self.assertNotIn('{0}.test_alias'.format(self.module_name), self.loader)
|
2015-02-05 16:29:02 +00:00
|
|
|
self.assertTrue(inspect.isfunction(self.loader['{0}.working_alias'.format(self.module_name)]))
|
|
|
|
|
2015-02-05 16:05:47 +00:00
|
|
|
def test_clear(self):
|
|
|
|
self.assertTrue(inspect.isfunction(self.loader['test.ping']))
|
|
|
|
self.update_module() # write out out custom module
|
|
|
|
self.loader.clear() # clear the loader dict
|
|
|
|
|
|
|
|
# force a load of our module
|
|
|
|
self.assertTrue(inspect.isfunction(self.loader[self.module_key]))
|
|
|
|
|
|
|
|
# make sure we only loaded our custom module
|
|
|
|
# which means that we did correctly refresh the file mapping
|
2015-02-06 14:28:43 +00:00
|
|
|
for k, v in six.iteritems(self.loader._dict):
|
2015-02-05 16:05:47 +00:00
|
|
|
self.assertTrue(k.startswith(self.module_name))
|
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
def test_load(self):
|
|
|
|
# ensure it doesn't exist
|
2015-02-05 17:32:30 +00:00
|
|
|
self.assertNotIn(self.module_key, self.loader)
|
2015-02-05 03:29:29 +00:00
|
|
|
|
|
|
|
self.update_module()
|
|
|
|
self.assertTrue(inspect.isfunction(self.loader[self.module_key]))
|
|
|
|
|
2015-02-05 16:05:47 +00:00
|
|
|
def test__load__(self):
|
|
|
|
'''
|
|
|
|
If a module specifies __load__ we should only load/expose those modules
|
|
|
|
'''
|
|
|
|
self.update_module()
|
2015-02-05 17:32:30 +00:00
|
|
|
|
2015-02-05 16:05:47 +00:00
|
|
|
# ensure it doesn't exist
|
2015-02-05 17:32:30 +00:00
|
|
|
self.assertNotIn(self.module_key + '2', self.loader)
|
2015-02-05 16:05:47 +00:00
|
|
|
|
|
|
|
def test__load__and_depends(self):
|
|
|
|
'''
|
|
|
|
If a module specifies __load__ we should only load/expose those modules
|
|
|
|
'''
|
|
|
|
self.update_module()
|
|
|
|
# ensure it doesn't exist
|
2015-02-05 17:32:30 +00:00
|
|
|
self.assertNotIn(self.module_key + '3', self.loader)
|
|
|
|
self.assertNotIn(self.module_key + '4', self.loader)
|
2015-02-05 16:05:47 +00:00
|
|
|
|
2015-02-05 03:29:29 +00:00
|
|
|
def test_reload(self):
|
|
|
|
# ensure it doesn't exist
|
2015-02-05 17:32:30 +00:00
|
|
|
self.assertNotIn(self.module_key, self.loader)
|
2015-02-05 03:29:29 +00:00
|
|
|
|
|
|
|
# make sure it updates correctly
|
2015-02-06 14:28:43 +00:00
|
|
|
for x in range(1, 3):
|
2015-02-05 03:29:29 +00:00
|
|
|
self.update_module()
|
|
|
|
self.loader.clear()
|
|
|
|
self.assertEqual(self.loader[self.module_key](), self.count)
|
|
|
|
|
|
|
|
self.rm_module()
|
|
|
|
# make sure that even if we remove the module, its still loaded until a clear
|
|
|
|
self.assertEqual(self.loader[self.module_key](), self.count)
|
|
|
|
self.loader.clear()
|
2015-02-05 17:32:30 +00:00
|
|
|
self.assertNotIn(self.module_key, self.loader)
|
2015-02-06 22:24:55 +00:00
|
|
|
|
2017-02-10 01:57:40 +00:00
|
|
|
|
|
|
|
virtual_aliases = ('loadertest2', 'loadertest3')
|
|
|
|
virtual_alias_module_template = '''
|
|
|
|
__virtual_aliases__ = {0}
|
|
|
|
|
|
|
|
def test():
|
|
|
|
return True
|
|
|
|
'''.format(virtual_aliases)
|
|
|
|
|
|
|
|
|
|
|
|
class LazyLoaderVirtualAliasTest(TestCase):
|
|
|
|
'''
|
|
|
|
Test the loader of salt with changing modules
|
|
|
|
'''
|
|
|
|
module_name = 'loadertest'
|
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
2017-02-19 15:35:30 +00:00
|
|
|
cls.opts = salt.config.minion_config(None)
|
2017-03-15 01:50:07 +00:00
|
|
|
cls.opts['grains'] = grains(cls.opts)
|
|
|
|
if not os.path.isdir(TMP):
|
|
|
|
os.makedirs(TMP)
|
|
|
|
|
2017-02-10 01:57:40 +00:00
|
|
|
def setUp(self):
|
2017-03-15 01:50:07 +00:00
|
|
|
self.tmp_dir = tempfile.mkdtemp(dir=TMP)
|
2017-02-19 15:35:30 +00:00
|
|
|
opts = copy.deepcopy(self.opts)
|
|
|
|
dirs = _module_dirs(opts, 'modules', 'module')
|
2017-02-10 01:57:40 +00:00
|
|
|
dirs.append(self.tmp_dir)
|
2017-02-19 15:35:30 +00:00
|
|
|
self.utils = utils(opts)
|
|
|
|
self.proxy = proxy(opts)
|
|
|
|
self.minion_mods = minion_mods(opts)
|
2017-02-10 01:57:40 +00:00
|
|
|
self.loader = LazyLoader(dirs,
|
2017-02-19 15:35:30 +00:00
|
|
|
opts,
|
2017-02-10 01:57:40 +00:00
|
|
|
tag='module',
|
|
|
|
pack={'__utils__': self.utils,
|
|
|
|
'__proxy__': self.proxy,
|
|
|
|
'__salt__': self.minion_mods})
|
|
|
|
|
2017-03-06 16:45:59 +00:00
|
|
|
def tearDown(self):
|
|
|
|
del self.tmp_dir
|
|
|
|
del self.utils
|
|
|
|
del self.proxy
|
|
|
|
del self.minion_mods
|
|
|
|
del self.loader
|
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def tearDownClass(cls):
|
|
|
|
del cls.opts
|
|
|
|
|
2017-02-10 01:57:40 +00:00
|
|
|
def update_module(self):
|
2017-07-18 16:31:01 +00:00
|
|
|
with salt.utils.files.fopen(self.module_path, 'wb') as fh:
|
Use explicit unicode strings + break up salt.utils
This PR is part of what will be an ongoing effort to use explicit
unicode strings in Salt. Because Python 3 does not suport Python 2's raw
unicode string syntax (i.e. `ur'\d+'`), we must use
`salt.utils.locales.sdecode()` to ensure that the raw string is unicode.
However, because of how `salt/utils/__init__.py` has evolved into the
hulking monstrosity it is today, this means importing a large module in
places where it is not needed, which could negatively impact
performance. For this reason, this PR also breaks out some of the
functions from `salt/utils/__init__.py` into new/existing modules under
`salt/utils/`. The long term goal will be that the modules within this
directory do not depend on importing `salt.utils`.
A summary of the changes in this PR is as follows:
* Moves the following functions from `salt.utils` to new locations
(including a deprecation warning if invoked from `salt.utils`):
`to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`,
`dequote`, `is_hex`, `is_bin_str`, `rand_string`,
`contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`,
`which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`,
`is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`,
`is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`,
`is_openbsd`, `is_aix`
* Moves the functions already deprecated by @rallytime to the bottom of
`salt/utils/__init__.py` for better organization, so we can keep the
deprecated ones separate from the ones yet to be deprecated as we
continue to break up `salt.utils`
* Updates `salt/*.py` and all files under `salt/client/` to use explicit
unicode string literals.
* Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils
import foo` becomes `import salt.utils.foo as foo`).
* Renames the `test.rand_str` function to `test.random_hash` to more
accurately reflect what it does
* Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`)
such that it returns a string matching the passed size. Previously
this function would get `size` bytes from `os.urandom()`,
base64-encode it, and return the result, which would in most cases not
be equal to the passed size.
2017-07-25 01:47:15 +00:00
|
|
|
fh.write(salt.utils.stringutils.to_bytes(virtual_alias_module_template))
|
2017-02-10 01:57:40 +00:00
|
|
|
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
|
2017-03-15 01:50:07 +00:00
|
|
|
remove_bytecode(self.module_path)
|
2017-02-10 01:57:40 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def module_path(self):
|
|
|
|
return os.path.join(self.tmp_dir, '{0}.py'.format(self.module_name))
|
|
|
|
|
|
|
|
def test_virtual_alias(self):
|
|
|
|
'''
|
|
|
|
Test the __virtual_alias__ feature
|
|
|
|
'''
|
|
|
|
self.update_module()
|
|
|
|
|
|
|
|
mod_names = [self.module_name] + list(virtual_aliases)
|
|
|
|
for mod_name in mod_names:
|
|
|
|
func_name = '.'.join((mod_name, 'test'))
|
|
|
|
log.debug('Running %s (dict attribute)', func_name)
|
|
|
|
self.assertTrue(self.loader[func_name]())
|
|
|
|
log.debug('Running %s (loader attribute)', func_name)
|
|
|
|
self.assertTrue(getattr(self.loader, mod_name).test())
|
|
|
|
|
|
|
|
|
2015-02-06 22:24:55 +00:00
|
|
|
submodule_template = '''
|
2017-03-15 01:50:07 +00:00
|
|
|
from __future__ import absolute_import
|
|
|
|
|
|
|
|
import {0}.lib
|
2015-02-06 22:24:55 +00:00
|
|
|
|
|
|
|
def test():
|
2017-03-15 01:50:07 +00:00
|
|
|
return ({count}, {0}.lib.test())
|
2015-02-06 22:24:55 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
submodule_lib_template = '''
|
|
|
|
def test():
|
|
|
|
return {count}
|
|
|
|
'''
|
|
|
|
|
2015-02-06 23:14:38 +00:00
|
|
|
|
2015-02-06 22:24:55 +00:00
|
|
|
class LazyLoaderSubmodReloadingTest(TestCase):
|
|
|
|
'''
|
|
|
|
Test the loader of salt with changing modules
|
|
|
|
'''
|
2015-02-07 18:39:54 +00:00
|
|
|
module_name = 'loadertestsubmod'
|
|
|
|
module_key = 'loadertestsubmod.test'
|
2015-02-06 22:24:55 +00:00
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
2017-02-19 15:35:30 +00:00
|
|
|
cls.opts = salt.config.minion_config(None)
|
2017-03-15 01:50:07 +00:00
|
|
|
cls.opts['grains'] = grains(cls.opts)
|
|
|
|
if not os.path.isdir(TMP):
|
|
|
|
os.makedirs(TMP)
|
|
|
|
|
2015-02-06 22:24:55 +00:00
|
|
|
def setUp(self):
|
2017-03-15 01:50:07 +00:00
|
|
|
self.tmp_dir = tempfile.mkdtemp(dir=TMP)
|
2015-02-06 22:24:55 +00:00
|
|
|
os.makedirs(self.module_dir)
|
|
|
|
|
|
|
|
self.count = 0
|
|
|
|
self.lib_count = 0
|
|
|
|
|
2017-02-19 15:35:30 +00:00
|
|
|
opts = copy.deepcopy(self.opts)
|
|
|
|
dirs = _module_dirs(opts, 'modules', 'module')
|
2015-02-06 22:24:55 +00:00
|
|
|
dirs.append(self.tmp_dir)
|
2017-02-19 15:35:30 +00:00
|
|
|
self.utils = utils(opts)
|
|
|
|
self.proxy = proxy(opts)
|
|
|
|
self.minion_mods = minion_mods(opts)
|
2015-02-06 22:24:55 +00:00
|
|
|
self.loader = LazyLoader(dirs,
|
2017-02-19 15:35:30 +00:00
|
|
|
opts,
|
2016-07-07 21:03:45 +00:00
|
|
|
tag='module',
|
|
|
|
pack={'__utils__': self.utils,
|
2017-03-15 01:50:07 +00:00
|
|
|
'__proxy__': self.proxy,
|
|
|
|
'__salt__': self.minion_mods}
|
2016-07-07 21:03:45 +00:00
|
|
|
)
|
2015-02-06 22:24:55 +00:00
|
|
|
|
|
|
|
def tearDown(self):
|
2017-03-06 16:45:59 +00:00
|
|
|
shutil.rmtree(self.tmp_dir)
|
|
|
|
del self.tmp_dir
|
|
|
|
del self.utils
|
|
|
|
del self.proxy
|
|
|
|
del self.minion_mods
|
|
|
|
del self.loader
|
2015-02-06 22:24:55 +00:00
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def tearDownClass(cls):
|
|
|
|
del cls.opts
|
|
|
|
|
2015-02-06 22:24:55 +00:00
|
|
|
def update_module(self):
|
|
|
|
self.count += 1
|
2017-07-18 16:31:01 +00:00
|
|
|
with salt.utils.files.fopen(self.module_path, 'wb') as fh:
|
2016-08-03 22:21:10 +00:00
|
|
|
fh.write(
|
Use explicit unicode strings + break up salt.utils
This PR is part of what will be an ongoing effort to use explicit
unicode strings in Salt. Because Python 3 does not suport Python 2's raw
unicode string syntax (i.e. `ur'\d+'`), we must use
`salt.utils.locales.sdecode()` to ensure that the raw string is unicode.
However, because of how `salt/utils/__init__.py` has evolved into the
hulking monstrosity it is today, this means importing a large module in
places where it is not needed, which could negatively impact
performance. For this reason, this PR also breaks out some of the
functions from `salt/utils/__init__.py` into new/existing modules under
`salt/utils/`. The long term goal will be that the modules within this
directory do not depend on importing `salt.utils`.
A summary of the changes in this PR is as follows:
* Moves the following functions from `salt.utils` to new locations
(including a deprecation warning if invoked from `salt.utils`):
`to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`,
`dequote`, `is_hex`, `is_bin_str`, `rand_string`,
`contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`,
`which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`,
`is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`,
`is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`,
`is_openbsd`, `is_aix`
* Moves the functions already deprecated by @rallytime to the bottom of
`salt/utils/__init__.py` for better organization, so we can keep the
deprecated ones separate from the ones yet to be deprecated as we
continue to break up `salt.utils`
* Updates `salt/*.py` and all files under `salt/client/` to use explicit
unicode string literals.
* Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils
import foo` becomes `import salt.utils.foo as foo`).
* Renames the `test.rand_str` function to `test.random_hash` to more
accurately reflect what it does
* Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`)
such that it returns a string matching the passed size. Previously
this function would get `size` bytes from `os.urandom()`,
base64-encode it, and return the result, which would in most cases not
be equal to the passed size.
2017-07-25 01:47:15 +00:00
|
|
|
salt.utils.stringutils.to_bytes(
|
2017-03-15 01:50:07 +00:00
|
|
|
submodule_template.format(self.module_name, count=self.count)
|
2016-08-03 22:21:10 +00:00
|
|
|
)
|
|
|
|
)
|
2015-02-06 22:24:55 +00:00
|
|
|
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
|
2017-03-15 01:50:07 +00:00
|
|
|
remove_bytecode(self.module_path)
|
2015-02-06 22:24:55 +00:00
|
|
|
|
|
|
|
def rm_module(self):
|
|
|
|
os.unlink(self.module_path)
|
2017-03-15 01:50:07 +00:00
|
|
|
remove_bytecode(self.module_path)
|
2015-02-06 22:24:55 +00:00
|
|
|
|
|
|
|
def update_lib(self):
|
|
|
|
self.lib_count += 1
|
2017-03-15 01:50:07 +00:00
|
|
|
for modname in list(sys.modules):
|
|
|
|
if modname.startswith(self.module_name):
|
|
|
|
del sys.modules[modname]
|
2017-07-18 16:31:01 +00:00
|
|
|
with salt.utils.files.fopen(self.lib_path, 'wb') as fh:
|
2016-08-03 22:21:10 +00:00
|
|
|
fh.write(
|
Use explicit unicode strings + break up salt.utils
This PR is part of what will be an ongoing effort to use explicit
unicode strings in Salt. Because Python 3 does not suport Python 2's raw
unicode string syntax (i.e. `ur'\d+'`), we must use
`salt.utils.locales.sdecode()` to ensure that the raw string is unicode.
However, because of how `salt/utils/__init__.py` has evolved into the
hulking monstrosity it is today, this means importing a large module in
places where it is not needed, which could negatively impact
performance. For this reason, this PR also breaks out some of the
functions from `salt/utils/__init__.py` into new/existing modules under
`salt/utils/`. The long term goal will be that the modules within this
directory do not depend on importing `salt.utils`.
A summary of the changes in this PR is as follows:
* Moves the following functions from `salt.utils` to new locations
(including a deprecation warning if invoked from `salt.utils`):
`to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`,
`dequote`, `is_hex`, `is_bin_str`, `rand_string`,
`contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`,
`which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`,
`is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`,
`is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`,
`is_openbsd`, `is_aix`
* Moves the functions already deprecated by @rallytime to the bottom of
`salt/utils/__init__.py` for better organization, so we can keep the
deprecated ones separate from the ones yet to be deprecated as we
continue to break up `salt.utils`
* Updates `salt/*.py` and all files under `salt/client/` to use explicit
unicode string literals.
* Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils
import foo` becomes `import salt.utils.foo as foo`).
* Renames the `test.rand_str` function to `test.random_hash` to more
accurately reflect what it does
* Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`)
such that it returns a string matching the passed size. Previously
this function would get `size` bytes from `os.urandom()`,
base64-encode it, and return the result, which would in most cases not
be equal to the passed size.
2017-07-25 01:47:15 +00:00
|
|
|
salt.utils.stringutils.to_bytes(
|
2016-08-03 22:21:10 +00:00
|
|
|
submodule_lib_template.format(count=self.lib_count)
|
|
|
|
)
|
|
|
|
)
|
2015-02-06 22:24:55 +00:00
|
|
|
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
|
2017-03-15 01:50:07 +00:00
|
|
|
remove_bytecode(self.lib_path)
|
2015-02-06 22:24:55 +00:00
|
|
|
|
|
|
|
def rm_lib(self):
|
2017-03-15 01:50:07 +00:00
|
|
|
for modname in list(sys.modules):
|
|
|
|
if modname.startswith(self.module_name):
|
|
|
|
del sys.modules[modname]
|
2015-02-06 22:24:55 +00:00
|
|
|
os.unlink(self.lib_path)
|
2017-03-15 01:50:07 +00:00
|
|
|
remove_bytecode(self.lib_path)
|
2015-02-06 22:24:55 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def module_dir(self):
|
|
|
|
return os.path.join(self.tmp_dir, self.module_name)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def module_path(self):
|
|
|
|
return os.path.join(self.module_dir, '__init__.py')
|
|
|
|
|
|
|
|
@property
|
|
|
|
def lib_path(self):
|
|
|
|
return os.path.join(self.module_dir, 'lib.py')
|
|
|
|
|
|
|
|
def test_basic(self):
|
|
|
|
# ensure it doesn't exist
|
|
|
|
self.assertNotIn(self.module_key, self.loader)
|
|
|
|
|
|
|
|
self.update_module()
|
|
|
|
self.update_lib()
|
|
|
|
self.loader.clear()
|
|
|
|
self.assertIn(self.module_key, self.loader)
|
|
|
|
|
|
|
|
def test_reload(self):
|
|
|
|
# ensure it doesn't exist
|
|
|
|
self.assertNotIn(self.module_key, self.loader)
|
|
|
|
|
|
|
|
# update both the module and the lib
|
|
|
|
for x in range(1, 3):
|
|
|
|
self.update_lib()
|
2017-03-15 01:50:07 +00:00
|
|
|
self.update_module()
|
2015-02-06 22:24:55 +00:00
|
|
|
self.loader.clear()
|
2017-03-15 01:50:07 +00:00
|
|
|
self.assertNotIn(self.module_key, self.loader._dict)
|
|
|
|
self.assertIn(self.module_key, self.loader)
|
2015-02-06 22:24:55 +00:00
|
|
|
self.assertEqual(self.loader[self.module_key](), (self.count, self.lib_count))
|
|
|
|
|
|
|
|
# update just the module
|
|
|
|
for x in range(1, 3):
|
|
|
|
self.update_module()
|
|
|
|
self.loader.clear()
|
2017-03-15 01:50:07 +00:00
|
|
|
self.assertNotIn(self.module_key, self.loader._dict)
|
|
|
|
self.assertIn(self.module_key, self.loader)
|
2015-02-06 22:24:55 +00:00
|
|
|
self.assertEqual(self.loader[self.module_key](), (self.count, self.lib_count))
|
|
|
|
|
|
|
|
# update just the lib
|
|
|
|
for x in range(1, 3):
|
|
|
|
self.update_lib()
|
|
|
|
self.loader.clear()
|
2017-03-15 01:50:07 +00:00
|
|
|
self.assertNotIn(self.module_key, self.loader._dict)
|
|
|
|
self.assertIn(self.module_key, self.loader)
|
2015-02-06 22:24:55 +00:00
|
|
|
self.assertEqual(self.loader[self.module_key](), (self.count, self.lib_count))
|
|
|
|
|
|
|
|
self.rm_module()
|
|
|
|
# make sure that even if we remove the module, its still loaded until a clear
|
|
|
|
self.assertEqual(self.loader[self.module_key](), (self.count, self.lib_count))
|
|
|
|
self.loader.clear()
|
|
|
|
self.assertNotIn(self.module_key, self.loader)
|
|
|
|
|
|
|
|
def test_reload_missing_lib(self):
|
|
|
|
# ensure it doesn't exist
|
|
|
|
self.assertNotIn(self.module_key, self.loader)
|
|
|
|
|
|
|
|
# update both the module and the lib
|
|
|
|
self.update_module()
|
|
|
|
self.update_lib()
|
|
|
|
self.loader.clear()
|
|
|
|
self.assertEqual(self.loader[self.module_key](), (self.count, self.lib_count))
|
|
|
|
|
|
|
|
# remove the lib, this means we should fail to load the module next time
|
|
|
|
self.rm_lib()
|
|
|
|
self.loader.clear()
|
|
|
|
self.assertNotIn(self.module_key, self.loader)
|
2015-02-07 19:19:22 +00:00
|
|
|
|
|
|
|
|
2015-10-22 19:49:09 +00:00
|
|
|
mod_template = '''
|
|
|
|
def test():
|
|
|
|
return ({val})
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
class LazyLoaderModulePackageTest(TestCase):
|
|
|
|
'''
|
|
|
|
Test the loader of salt with changing modules
|
|
|
|
'''
|
|
|
|
module_name = 'loadertestmodpkg'
|
|
|
|
module_key = 'loadertestmodpkg.test'
|
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
2017-02-19 15:35:30 +00:00
|
|
|
cls.opts = salt.config.minion_config(None)
|
2017-03-15 01:50:07 +00:00
|
|
|
cls.opts['grains'] = grains(cls.opts)
|
|
|
|
if not os.path.isdir(TMP):
|
|
|
|
os.makedirs(TMP)
|
|
|
|
|
2015-10-22 19:49:09 +00:00
|
|
|
def setUp(self):
|
2017-03-15 01:50:07 +00:00
|
|
|
self.tmp_dir = tempfile.mkdtemp(dir=TMP)
|
2015-10-22 19:49:09 +00:00
|
|
|
|
2017-02-19 15:35:30 +00:00
|
|
|
dirs = _module_dirs(copy.deepcopy(self.opts), 'modules', 'module')
|
2015-10-22 19:49:09 +00:00
|
|
|
dirs.append(self.tmp_dir)
|
|
|
|
self.loader = LazyLoader(dirs,
|
2017-02-19 15:35:30 +00:00
|
|
|
copy.deepcopy(self.opts),
|
2015-10-22 19:49:09 +00:00
|
|
|
tag='module')
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
shutil.rmtree(self.tmp_dir)
|
2017-03-06 16:45:59 +00:00
|
|
|
del self.tmp_dir
|
|
|
|
del self.loader
|
2015-10-22 19:49:09 +00:00
|
|
|
|
2017-03-15 01:50:07 +00:00
|
|
|
@classmethod
|
|
|
|
def tearDownClass(cls):
|
|
|
|
del cls.opts
|
|
|
|
|
2015-10-22 19:49:09 +00:00
|
|
|
def update_pyfile(self, pyfile, contents):
|
|
|
|
dirname = os.path.dirname(pyfile)
|
|
|
|
if not os.path.exists(dirname):
|
|
|
|
os.makedirs(dirname)
|
2017-07-18 16:31:01 +00:00
|
|
|
with salt.utils.files.fopen(pyfile, 'wb') as fh:
|
Use explicit unicode strings + break up salt.utils
This PR is part of what will be an ongoing effort to use explicit
unicode strings in Salt. Because Python 3 does not suport Python 2's raw
unicode string syntax (i.e. `ur'\d+'`), we must use
`salt.utils.locales.sdecode()` to ensure that the raw string is unicode.
However, because of how `salt/utils/__init__.py` has evolved into the
hulking monstrosity it is today, this means importing a large module in
places where it is not needed, which could negatively impact
performance. For this reason, this PR also breaks out some of the
functions from `salt/utils/__init__.py` into new/existing modules under
`salt/utils/`. The long term goal will be that the modules within this
directory do not depend on importing `salt.utils`.
A summary of the changes in this PR is as follows:
* Moves the following functions from `salt.utils` to new locations
(including a deprecation warning if invoked from `salt.utils`):
`to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`,
`dequote`, `is_hex`, `is_bin_str`, `rand_string`,
`contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`,
`which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`,
`is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`,
`is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`,
`is_openbsd`, `is_aix`
* Moves the functions already deprecated by @rallytime to the bottom of
`salt/utils/__init__.py` for better organization, so we can keep the
deprecated ones separate from the ones yet to be deprecated as we
continue to break up `salt.utils`
* Updates `salt/*.py` and all files under `salt/client/` to use explicit
unicode string literals.
* Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils
import foo` becomes `import salt.utils.foo as foo`).
* Renames the `test.rand_str` function to `test.random_hash` to more
accurately reflect what it does
* Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`)
such that it returns a string matching the passed size. Previously
this function would get `size` bytes from `os.urandom()`,
base64-encode it, and return the result, which would in most cases not
be equal to the passed size.
2017-07-25 01:47:15 +00:00
|
|
|
fh.write(salt.utils.stringutils.to_bytes(contents))
|
2015-10-22 19:49:09 +00:00
|
|
|
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
|
2017-03-15 01:50:07 +00:00
|
|
|
remove_bytecode(pyfile)
|
2015-10-22 19:49:09 +00:00
|
|
|
|
|
|
|
def rm_pyfile(self, pyfile):
|
|
|
|
os.unlink(pyfile)
|
2017-03-15 01:50:07 +00:00
|
|
|
remove_bytecode(pyfile)
|
2015-10-22 19:49:09 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
2015-02-07 19:19:22 +00:00
|
|
|
deep_init_base = '''
|
2017-03-15 01:50:07 +00:00
|
|
|
from __future__ import absolute_import
|
|
|
|
import {0}.top_lib
|
|
|
|
import {0}.top_lib.mid_lib
|
|
|
|
import {0}.top_lib.mid_lib.bot_lib
|
2015-02-07 19:19:22 +00:00
|
|
|
|
|
|
|
def top():
|
2017-03-15 01:50:07 +00:00
|
|
|
return {0}.top_lib.test()
|
2015-02-07 19:19:22 +00:00
|
|
|
|
|
|
|
def mid():
|
2017-03-15 01:50:07 +00:00
|
|
|
return {0}.top_lib.mid_lib.test()
|
2015-02-07 19:19:22 +00:00
|
|
|
|
|
|
|
def bot():
|
2017-03-15 01:50:07 +00:00
|
|
|
return {0}.top_lib.mid_lib.bot_lib.test()
|
2015-02-07 19:19:22 +00:00
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
class LazyLoaderDeepSubmodReloadingTest(TestCase):
|
|
|
|
module_name = 'loadertestsubmoddeep'
|
|
|
|
libs = ('top_lib', 'mid_lib', 'bot_lib')
|
|
|
|
|
2017-02-19 15:35:30 +00:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
|
|
|
cls.opts = salt.config.minion_config(None)
|
|
|
|
cls.opts['grains'] = grains(cls.opts)
|
2017-03-15 01:50:07 +00:00
|
|
|
if not os.path.isdir(TMP):
|
|
|
|
os.makedirs(TMP)
|
2017-02-19 15:35:30 +00:00
|
|
|
|
|
|
|
def setUp(self):
|
2017-03-15 01:50:07 +00:00
|
|
|
self.tmp_dir = tempfile.mkdtemp(dir=TMP)
|
2015-02-07 19:19:22 +00:00
|
|
|
os.makedirs(self.module_dir)
|
|
|
|
|
|
|
|
self.lib_count = collections.defaultdict(int) # mapping of path -> count
|
|
|
|
|
|
|
|
# bootstrap libs
|
2017-07-18 16:31:01 +00:00
|
|
|
with salt.utils.files.fopen(os.path.join(self.module_dir, '__init__.py'), 'w') as fh:
|
2016-08-03 22:21:10 +00:00
|
|
|
# No .decode() needed here as deep_init_base is defined as str and
|
|
|
|
# not bytes.
|
2017-12-14 06:26:39 +00:00
|
|
|
fh.write(
|
|
|
|
salt.utils.stringutils.to_str(
|
|
|
|
deep_init_base.format(self.module_name)
|
|
|
|
)
|
|
|
|
)
|
2015-02-07 19:19:22 +00:00
|
|
|
fh.flush()
|
|
|
|
os.fsync(fh.fileno()) # flush to disk
|
|
|
|
|
|
|
|
self.lib_paths = {}
|
|
|
|
dir_path = self.module_dir
|
|
|
|
for lib_name in self.libs:
|
|
|
|
dir_path = os.path.join(dir_path, lib_name)
|
|
|
|
self.lib_paths[lib_name] = dir_path
|
|
|
|
os.makedirs(dir_path)
|
|
|
|
self.update_lib(lib_name)
|
|
|
|
|
2017-02-19 15:35:30 +00:00
|
|
|
opts = copy.deepcopy(self.opts)
|
|
|
|
dirs = _module_dirs(opts, 'modules', 'module')
|
2015-02-07 19:19:22 +00:00
|
|
|
dirs.append(self.tmp_dir)
|
2017-02-19 15:35:30 +00:00
|
|
|
self.utils = utils(opts)
|
|
|
|
self.proxy = proxy(opts)
|
|
|
|
self.minion_mods = minion_mods(opts)
|
2015-02-07 19:19:22 +00:00
|
|
|
self.loader = LazyLoader(dirs,
|
2017-02-19 15:35:30 +00:00
|
|
|
copy.deepcopy(opts),
|
2016-07-07 21:03:45 +00:00
|
|
|
tag='module',
|
|
|
|
pack={'__utils__': self.utils,
|
|
|
|
'__proxy__': self.proxy,
|
|
|
|
'__salt__': self.minion_mods}
|
|
|
|
)
|
2017-03-15 01:50:07 +00:00
|
|
|
self.assertIn('{0}.top'.format(self.module_name), self.loader)
|
2015-02-07 19:19:22 +00:00
|
|
|
|
2017-03-06 16:45:59 +00:00
|
|
|
def tearDown(self):
|
|
|
|
shutil.rmtree(self.tmp_dir)
|
|
|
|
del self.tmp_dir
|
|
|
|
del self.lib_paths
|
|
|
|
del self.utils
|
|
|
|
del self.proxy
|
|
|
|
del self.minion_mods
|
|
|
|
del self.loader
|
2017-03-15 01:50:07 +00:00
|
|
|
del self.lib_count
|
2017-03-06 16:45:59 +00:00
|
|
|
|
2017-02-19 15:35:30 +00:00
|
|
|
@classmethod
|
|
|
|
def tearDownClass(cls):
|
|
|
|
del cls.opts
|
|
|
|
|
2015-02-07 19:19:22 +00:00
|
|
|
@property
|
|
|
|
def module_dir(self):
|
|
|
|
return os.path.join(self.tmp_dir, self.module_name)
|
|
|
|
|
|
|
|
def update_lib(self, lib_name):
|
2017-03-15 01:50:07 +00:00
|
|
|
for modname in list(sys.modules):
|
|
|
|
if modname.startswith(self.module_name):
|
|
|
|
del sys.modules[modname]
|
2015-02-07 19:19:22 +00:00
|
|
|
path = os.path.join(self.lib_paths[lib_name], '__init__.py')
|
|
|
|
self.lib_count[lib_name] += 1
|
2017-07-18 16:31:01 +00:00
|
|
|
with salt.utils.files.fopen(path, 'wb') as fh:
|
2016-08-03 22:21:10 +00:00
|
|
|
fh.write(
|
Use explicit unicode strings + break up salt.utils
This PR is part of what will be an ongoing effort to use explicit
unicode strings in Salt. Because Python 3 does not suport Python 2's raw
unicode string syntax (i.e. `ur'\d+'`), we must use
`salt.utils.locales.sdecode()` to ensure that the raw string is unicode.
However, because of how `salt/utils/__init__.py` has evolved into the
hulking monstrosity it is today, this means importing a large module in
places where it is not needed, which could negatively impact
performance. For this reason, this PR also breaks out some of the
functions from `salt/utils/__init__.py` into new/existing modules under
`salt/utils/`. The long term goal will be that the modules within this
directory do not depend on importing `salt.utils`.
A summary of the changes in this PR is as follows:
* Moves the following functions from `salt.utils` to new locations
(including a deprecation warning if invoked from `salt.utils`):
`to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`,
`dequote`, `is_hex`, `is_bin_str`, `rand_string`,
`contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`,
`which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`,
`is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`,
`is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`,
`is_openbsd`, `is_aix`
* Moves the functions already deprecated by @rallytime to the bottom of
`salt/utils/__init__.py` for better organization, so we can keep the
deprecated ones separate from the ones yet to be deprecated as we
continue to break up `salt.utils`
* Updates `salt/*.py` and all files under `salt/client/` to use explicit
unicode string literals.
* Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils
import foo` becomes `import salt.utils.foo as foo`).
* Renames the `test.rand_str` function to `test.random_hash` to more
accurately reflect what it does
* Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`)
such that it returns a string matching the passed size. Previously
this function would get `size` bytes from `os.urandom()`,
base64-encode it, and return the result, which would in most cases not
be equal to the passed size.
2017-07-25 01:47:15 +00:00
|
|
|
salt.utils.stringutils.to_bytes(
|
2016-08-03 22:21:10 +00:00
|
|
|
submodule_lib_template.format(count=self.lib_count[lib_name])
|
|
|
|
)
|
|
|
|
)
|
2015-02-07 19:19:22 +00:00
|
|
|
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
|
2017-03-15 01:50:07 +00:00
|
|
|
remove_bytecode(path)
|
2015-02-07 19:19:22 +00:00
|
|
|
|
|
|
|
def test_basic(self):
|
|
|
|
self.assertIn('{0}.top'.format(self.module_name), self.loader)
|
|
|
|
|
|
|
|
def _verify_libs(self):
|
|
|
|
for lib in self.libs:
|
|
|
|
self.assertEqual(self.loader['{0}.{1}'.format(self.module_name, lib.replace('_lib', ''))](),
|
|
|
|
self.lib_count[lib])
|
|
|
|
|
|
|
|
def test_reload(self):
|
|
|
|
'''
|
|
|
|
Make sure that we can reload all libraries of arbitrary depth
|
|
|
|
'''
|
|
|
|
self._verify_libs()
|
|
|
|
|
|
|
|
# update them all
|
|
|
|
for lib in self.libs:
|
2017-03-15 01:50:07 +00:00
|
|
|
for x in range(5):
|
2015-02-07 19:19:22 +00:00
|
|
|
self.update_lib(lib)
|
|
|
|
self.loader.clear()
|
|
|
|
self._verify_libs()
|