2016-09-13 16:52:15 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
'''
|
|
|
|
unit tests for the localfs cache
|
|
|
|
'''
|
|
|
|
|
|
|
|
# Import Python libs
|
|
|
|
from __future__ import absolute_import
|
2017-02-22 13:51:22 +00:00
|
|
|
import shutil
|
2016-09-13 16:52:15 +00:00
|
|
|
import tempfile
|
|
|
|
|
|
|
|
# Import Salt Testing libs
|
2017-02-19 15:59:50 +00:00
|
|
|
from tests.support.mixins import LoaderModuleMockMixin
|
2017-04-04 13:46:17 +00:00
|
|
|
from tests.support.paths import TMP
|
2017-02-27 13:58:07 +00:00
|
|
|
from tests.support.unit import skipIf, TestCase
|
|
|
|
from tests.support.mock import (
|
2016-09-13 16:52:15 +00:00
|
|
|
MagicMock,
|
|
|
|
NO_MOCK,
|
|
|
|
NO_MOCK_REASON,
|
|
|
|
patch
|
|
|
|
)
|
|
|
|
|
|
|
|
# Import Salt libs
|
|
|
|
import salt.payload
|
2017-07-18 16:31:01 +00:00
|
|
|
import salt.utils.files
|
2017-02-19 15:59:50 +00:00
|
|
|
import salt.cache.localfs as localfs
|
2016-09-13 16:52:15 +00:00
|
|
|
from salt.exceptions import SaltCacheError
|
|
|
|
|
2017-02-22 13:51:22 +00:00
|
|
|
# Import 3rd-party libs
|
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
|
2017-02-22 13:51:22 +00:00
|
|
|
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
2017-02-19 15:59:50 +00:00
|
|
|
class LocalFSTest(TestCase, LoaderModuleMockMixin):
|
2016-09-13 16:52:15 +00:00
|
|
|
'''
|
|
|
|
Validate the functions in the localfs cache
|
|
|
|
'''
|
|
|
|
|
2017-03-22 12:12:36 +00:00
|
|
|
def setup_loader_modules(self):
|
|
|
|
return {localfs: {}}
|
2017-02-19 15:59:50 +00:00
|
|
|
|
2016-09-13 16:52:15 +00:00
|
|
|
def _create_tmp_cache_file(self, tmp_dir, serializer):
|
|
|
|
'''
|
|
|
|
Helper function that creates a temporary cache file using localfs.store. This
|
|
|
|
is to used to create DRY unit tests for the localfs cache.
|
|
|
|
'''
|
2017-02-22 13:51:22 +00:00
|
|
|
self.addCleanup(shutil.rmtree, tmp_dir)
|
2016-09-13 16:52:15 +00:00
|
|
|
with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}):
|
|
|
|
with patch.dict(localfs.__context__, {'serial': serializer}):
|
2017-01-10 20:04:39 +00:00
|
|
|
localfs.store(bank='bank', key='key', data='payload data', cachedir=tmp_dir)
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
# 'store' function tests: 4
|
|
|
|
|
|
|
|
def test_store_no_base_cache_dir(self):
|
|
|
|
'''
|
|
|
|
Tests that a SaltCacheError is raised when the base directory doesn't exist and
|
|
|
|
cannot be created.
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isdir', MagicMock(return_value=None)):
|
|
|
|
with patch('os.makedirs', MagicMock(side_effect=OSError)):
|
|
|
|
self.assertRaises(SaltCacheError, localfs.store, bank='', key='', data='', cachedir='')
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
def test_store_close_mkstemp_file_handle(self):
|
|
|
|
'''
|
|
|
|
Tests that the file descriptor that is opened by os.open during the mkstemp call
|
2017-07-18 16:31:01 +00:00
|
|
|
in localfs.store is closed before calling salt.utils.files.fopen on the filename.
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
This test mocks the call to mkstemp, but forces an OSError to be raised when the
|
|
|
|
close() function is called on a file descriptor that doesn't exist.
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isdir', MagicMock(return_value=True)):
|
|
|
|
with patch('tempfile.mkstemp', MagicMock(return_value=(12345, 'foo'))):
|
|
|
|
self.assertRaises(OSError, localfs.store, bank='', key='', data='', cachedir='')
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
def test_store_error_writing_cache(self):
|
|
|
|
'''
|
|
|
|
Tests that a SaltCacheError is raised when there is a problem writing to the
|
|
|
|
cache file.
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isdir', MagicMock(return_value=True)):
|
|
|
|
with patch('tempfile.mkstemp', MagicMock(return_value=('one', 'two'))):
|
|
|
|
with patch('os.close', MagicMock(return_value=None)):
|
2017-07-18 16:31:01 +00:00
|
|
|
with patch('salt.utils.files.fopen', MagicMock(side_effect=IOError)):
|
2017-04-10 13:00:57 +00:00
|
|
|
self.assertRaises(SaltCacheError, localfs.store, bank='', key='', data='', cachedir='')
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
def test_store_success(self):
|
|
|
|
'''
|
|
|
|
Tests that the store function writes the data to the serializer for storage.
|
|
|
|
'''
|
|
|
|
# Create a temporary cache dir
|
2017-04-04 13:46:17 +00:00
|
|
|
tmp_dir = tempfile.mkdtemp(dir=TMP)
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
# Use the helper function to create the cache file using localfs.store()
|
|
|
|
self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self))
|
|
|
|
|
|
|
|
# Read in the contents of the key.p file and assert "payload data" was written
|
2017-07-18 16:31:01 +00:00
|
|
|
with salt.utils.files.fopen(tmp_dir + '/bank/key.p', 'rb') as fh_:
|
2016-09-13 16:52:15 +00:00
|
|
|
for line in fh_:
|
2017-02-22 13:51:22 +00:00
|
|
|
self.assertIn(six.b('payload data'), line)
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
# 'fetch' function tests: 3
|
|
|
|
|
|
|
|
def test_fetch_return_when_cache_file_does_not_exist(self):
|
|
|
|
'''
|
2017-02-24 19:57:45 +00:00
|
|
|
Tests that the fetch function returns an empty dic when the cache key file
|
|
|
|
doesn't exist.
|
2016-09-13 16:52:15 +00:00
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isfile', MagicMock(return_value=False)):
|
|
|
|
self.assertEqual(localfs.fetch(bank='', key='', cachedir=''), {})
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
def test_fetch_error_reading_cache(self):
|
|
|
|
'''
|
|
|
|
Tests that a SaltCacheError is raised when there is a problem reading the cache
|
|
|
|
file.
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isfile', MagicMock(return_value=True)):
|
2017-07-18 16:31:01 +00:00
|
|
|
with patch('salt.utils.files.fopen', MagicMock(side_effect=IOError)):
|
2017-04-10 13:00:57 +00:00
|
|
|
self.assertRaises(SaltCacheError, localfs.fetch, bank='', key='', cachedir='')
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
def test_fetch_success(self):
|
|
|
|
'''
|
|
|
|
Tests that the fetch function is able to read the cache file and return its data.
|
|
|
|
'''
|
|
|
|
# Create a temporary cache dir
|
2017-04-04 13:46:17 +00:00
|
|
|
tmp_dir = tempfile.mkdtemp(dir=TMP)
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
# Create a new serializer object to use in function patches
|
|
|
|
serializer = salt.payload.Serial(self)
|
|
|
|
|
|
|
|
# Use the helper function to create the cache file using localfs.store()
|
|
|
|
self._create_tmp_cache_file(tmp_dir, serializer)
|
|
|
|
|
|
|
|
# Now fetch the data from the new cache key file
|
|
|
|
with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}):
|
|
|
|
with patch.dict(localfs.__context__, {'serial': serializer}):
|
2017-01-10 20:04:39 +00:00
|
|
|
self.assertIn('payload data', localfs.fetch(bank='bank', key='key', cachedir=tmp_dir))
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
# 'updated' function tests: 3
|
|
|
|
|
|
|
|
def test_updated_return_when_cache_file_does_not_exist(self):
|
|
|
|
'''
|
|
|
|
Tests that the updated function returns None when the cache key file doesn't
|
|
|
|
exist.
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isfile', MagicMock(return_value=False)):
|
|
|
|
self.assertIsNone(localfs.updated(bank='', key='', cachedir=''))
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
def test_updated_error_when_reading_mtime(self):
|
|
|
|
'''
|
|
|
|
Tests that a SaltCacheError is raised when there is a problem reading the mtime
|
|
|
|
of the cache file.
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isfile', MagicMock(return_value=True)):
|
|
|
|
with patch('os.path.getmtime', MagicMock(side_effect=IOError)):
|
|
|
|
self.assertRaises(SaltCacheError, localfs.updated, bank='', key='', cachedir='')
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
def test_updated_success(self):
|
|
|
|
'''
|
|
|
|
Test that the updated function returns the modification time of the cache file
|
|
|
|
'''
|
|
|
|
# Create a temporary cache dir
|
2017-04-04 13:46:17 +00:00
|
|
|
tmp_dir = tempfile.mkdtemp(dir=TMP)
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
# Use the helper function to create the cache file using localfs.store()
|
|
|
|
self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self))
|
|
|
|
|
|
|
|
with patch('os.path.join', MagicMock(return_value=tmp_dir + '/bank/key.p')):
|
2017-01-10 20:04:39 +00:00
|
|
|
self.assertIsInstance(localfs.updated(bank='bank', key='key', cachedir=tmp_dir), int)
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
# 'flush' function tests: 4
|
|
|
|
|
|
|
|
def test_flush_key_is_none_and_no_target_dir(self):
|
|
|
|
'''
|
|
|
|
Tests that the flush function returns False when no key is passed in and the
|
|
|
|
target directory doesn't exist.
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isdir', MagicMock(return_value=False)):
|
|
|
|
self.assertFalse(localfs.flush(bank='', key=None, cachedir=''))
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
def test_flush_key_provided_and_no_key_file_false(self):
|
|
|
|
'''
|
|
|
|
Tests that the flush function returns False when a key file is provided but
|
|
|
|
the target key file doesn't exist in the cache bank.
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isfile', MagicMock(return_value=False)):
|
|
|
|
self.assertFalse(localfs.flush(bank='', key='key', cachedir=''))
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
def test_flush_success(self):
|
|
|
|
'''
|
|
|
|
Tests that the flush function returns True when a key file is provided and
|
|
|
|
the target key exists in the cache bank.
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isfile', MagicMock(return_value=True)):
|
|
|
|
# Create a temporary cache dir
|
|
|
|
tmp_dir = tempfile.mkdtemp(dir=TMP)
|
2016-09-13 16:52:15 +00:00
|
|
|
|
2017-04-10 13:00:57 +00:00
|
|
|
# Use the helper function to create the cache file using localfs.store()
|
|
|
|
self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self))
|
2016-09-13 16:52:15 +00:00
|
|
|
|
2017-04-10 13:00:57 +00:00
|
|
|
# Now test the return of the flush function
|
|
|
|
with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}):
|
|
|
|
self.assertTrue(localfs.flush(bank='bank', key='key', cachedir=tmp_dir))
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
def test_flush_error_raised(self):
|
|
|
|
'''
|
|
|
|
Tests that a SaltCacheError is raised when there is a problem removing the
|
|
|
|
key file from the cache bank
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isfile', MagicMock(return_value=True)):
|
|
|
|
with patch('os.remove', MagicMock(side_effect=OSError)):
|
|
|
|
self.assertRaises(SaltCacheError, localfs.flush, bank='', key='key', cachedir='/var/cache/salt')
|
2016-09-13 16:52:15 +00:00
|
|
|
|
2017-08-17 22:19:34 +00:00
|
|
|
# 'list' function tests: 3
|
2016-09-13 16:52:15 +00:00
|
|
|
|
2017-08-17 22:19:34 +00:00
|
|
|
def test_list_no_base_dir(self):
|
2016-09-13 16:52:15 +00:00
|
|
|
'''
|
2017-03-28 16:21:58 +00:00
|
|
|
Tests that the ls function returns an empty list if the bank directory
|
2016-09-13 16:52:15 +00:00
|
|
|
doesn't exist.
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isdir', MagicMock(return_value=False)):
|
2017-08-17 22:19:34 +00:00
|
|
|
self.assertEqual(localfs.list_(bank='', cachedir=''), [])
|
2016-09-13 16:52:15 +00:00
|
|
|
|
2017-08-17 22:19:34 +00:00
|
|
|
def test_list_error_raised_no_bank_directory_access(self):
|
2016-09-13 16:52:15 +00:00
|
|
|
'''
|
|
|
|
Tests that a SaltCacheError is raised when there is a problem accessing the
|
|
|
|
cache bank directory.
|
|
|
|
'''
|
2017-04-10 13:00:57 +00:00
|
|
|
with patch('os.path.isdir', MagicMock(return_value=True)):
|
|
|
|
with patch('os.listdir', MagicMock(side_effect=OSError)):
|
2017-08-17 22:19:34 +00:00
|
|
|
self.assertRaises(SaltCacheError, localfs.list_, bank='', cachedir='')
|
2016-09-13 16:52:15 +00:00
|
|
|
|
2017-08-17 22:19:34 +00:00
|
|
|
def test_list_success(self):
|
2016-09-13 16:52:15 +00:00
|
|
|
'''
|
2017-03-28 16:21:58 +00:00
|
|
|
Tests the return of the ls function containing bank entries.
|
2016-09-13 16:52:15 +00:00
|
|
|
'''
|
|
|
|
# Create a temporary cache dir
|
2017-04-04 13:46:17 +00:00
|
|
|
tmp_dir = tempfile.mkdtemp(dir=TMP)
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
# Use the helper function to create the cache file using localfs.store()
|
|
|
|
self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self))
|
|
|
|
|
2017-03-28 16:21:58 +00:00
|
|
|
# Now test the return of the ls function
|
2016-09-13 16:52:15 +00:00
|
|
|
with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}):
|
2017-08-17 22:19:34 +00:00
|
|
|
self.assertEqual(localfs.list_(bank='bank', cachedir=tmp_dir), ['key'])
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
# 'contains' function tests: 1
|
|
|
|
|
|
|
|
def test_contains(self):
|
|
|
|
'''
|
|
|
|
Test the return of the contains function when key=None and when a key
|
|
|
|
is provided.
|
|
|
|
'''
|
|
|
|
# Create a temporary cache dir
|
2017-04-04 13:46:17 +00:00
|
|
|
tmp_dir = tempfile.mkdtemp(dir=TMP)
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
# Use the helper function to create the cache file using localfs.store()
|
|
|
|
self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self))
|
|
|
|
|
|
|
|
# Now test the return of the contains function when key=None
|
|
|
|
with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}):
|
2017-01-10 20:04:39 +00:00
|
|
|
self.assertTrue(localfs.contains(bank='bank', key=None, cachedir=tmp_dir))
|
2016-09-13 16:52:15 +00:00
|
|
|
|
|
|
|
# Now test the return of the contains function when key='key'
|
|
|
|
with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}):
|
2017-01-10 20:04:39 +00:00
|
|
|
self.assertTrue(localfs.contains(bank='bank', key='key', cachedir=tmp_dir))
|