Merge branch '2017.7' into '2018.3'

Conflicts:
  - tests/integration/modules/test_state.py
  - tests/unit/templates/test_jinja.py
  - tests/unit/test_minion.py
This commit is contained in:
rallytime 2018-04-17 10:54:42 -04:00
commit 94c2a12be6
No known key found for this signature in database
GPG key ID: E8F1A4B90D0DEA19
11 changed files with 180 additions and 46 deletions

View file

@ -46,6 +46,7 @@ provisioner:
- .bundle
- .kitchen
- .kitchen.yml
- artifacts
- Gemfile
- Gemfile.lock
- README.rst

View file

@ -136,24 +136,24 @@ the following userdata example:
$SourceStoreScope = 'LocalMachine'
$SourceStorename = 'Remote Desktop'
$SourceStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $SourceStorename, $SourceStoreScope
$SourceStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $SourceStorename, $SourceStoreScope
$SourceStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
$cert = $SourceStore.Certificates | Where-Object -FilterScript {
$cert = $SourceStore.Certificates | Where-Object -FilterScript {
$_.subject -like '*'
}
$DestStoreScope = 'LocalMachine'
$DestStoreName = 'My'
$DestStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $DestStoreName, $DestStoreScope
$DestStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $DestStoreName, $DestStoreScope
$DestStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$DestStore.Add($cert)
$SourceStore.Close()
$DestStore.Close()
winrm create winrm/config/listener?Address=*+Transport=HTTPS `@`{Hostname=`"($certId)`"`;CertificateThumbprint=`"($cert.Thumbprint)`"`}
winrm create winrm/config/listener?Address=*+Transport=HTTPS `@`{CertificateThumbprint=`"($cert.Thumbprint)`"`}
Restart-Service winrm
</powershell>

View file

@ -477,6 +477,7 @@ class LocalClient(object):
sub=3,
cli=False,
progress=False,
full_return=False,
**kwargs):
'''
Execute a command on a random subset of the targeted systems
@ -525,6 +526,7 @@ class LocalClient(object):
ret=ret,
kwarg=kwarg,
progress=progress,
full_return=full_return,
**kwargs)
def cmd_batch(

View file

@ -4566,7 +4566,7 @@ def _list_nodes(full=False):
pass
vms[name]['id'] = vm.find('ID').text
if vm.find('TEMPLATE').find('TEMPLATE_ID'):
if 'TEMPLATE_ID' in vm.find('TEMPLATE'):
vms[name]['image'] = vm.find('TEMPLATE').find('TEMPLATE_ID').text
vms[name]['name'] = name
vms[name]['size'] = {'cpu': cpu_size, 'memory': memory_size}

View file

@ -10,24 +10,24 @@ winrm set winrm/config/service/auth '@{Basic="true"}'
$SourceStoreScope = 'LocalMachine'
$SourceStorename = 'Remote Desktop'
$SourceStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $SourceStorename, $SourceStoreScope
$SourceStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $SourceStorename, $SourceStoreScope
$SourceStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
$cert = $SourceStore.Certificates | Where-Object -FilterScript {
$cert = $SourceStore.Certificates | Where-Object -FilterScript {
$_.subject -like '*'
}
$DestStoreScope = 'LocalMachine'
$DestStoreName = 'My'
$DestStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $DestStoreName, $DestStoreScope
$DestStore = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $DestStoreName, $DestStoreScope
$DestStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$DestStore.Add($cert)
$SourceStore.Close()
$DestStore.Close()
winrm create winrm/config/listener?Address=*+Transport=HTTPS `@`{Hostname=`"($certId)`"`;CertificateThumbprint=`"($cert.Thumbprint)`"`}
winrm create winrm/config/listener?Address=*+Transport=HTTPS `@`{CertificateThumbprint=`"($cert.Thumbprint)`"`}
Restart-Service winrm
</powershell>

View file

@ -12,7 +12,7 @@ import time
# Import Salt Testing libs
from tests.support.case import ModuleCase
from tests.support.unit import skipIf
from tests.support.paths import TMP, TMP_PILLAR_TREE
from tests.support.paths import FILES, TMP, TMP_PILLAR_TREE
from tests.support.mixins import SaltReturnAssertsMixin
# Import Salt libs
@ -29,6 +29,37 @@ import logging
log = logging.getLogger(__name__)
DEFAULT_ENDING = salt.utils.to_bytes(os.linesep)
def trim_line_end(line):
'''
Remove CRLF or LF from the end of line.
'''
if line[-2:] == salt.utils.to_bytes('\r\n'):
return line[:-2]
elif line[-1:] == salt.utils.to_bytes('\n'):
return line[:-1]
raise Exception("Invalid line ending")
def reline(source, dest, force=False, ending=DEFAULT_ENDING):
'''
Normalize the line endings of a file.
'''
fp, tmp = tempfile.mkstemp()
os.close(fp)
with salt.utils.fopen(tmp, 'wb') as tmp_fd:
with salt.utils.fopen(source, 'rb') as fd:
lines = fd.readlines()
for line in lines:
line_noend = trim_line_end(line)
tmp_fd.write(line_noend + ending)
if os.path.exists(dest) and force:
os.remove(dest)
os.rename(tmp, dest)
class StateModuleTest(ModuleCase, SaltReturnAssertsMixin):
'''
Validate the state module
@ -36,6 +67,13 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin):
maxDiff = None
def setUp(self):
super(StateModuleTest, self).setUp()
destpath = os.path.join(FILES, 'file', 'base', 'testappend', 'firstif')
reline(destpath, destpath, force=True)
destpath = os.path.join(FILES, 'file', 'base', 'testappend', 'secondif')
reline(destpath, destpath, force=True)
def test_show_highstate(self):
'''
state.show_highstate

View file

@ -122,7 +122,7 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
self.assertIn('minion', data)
'''
arg_str = '-c {0} {1}'.format(self.get_config_dir(), arg_str)
return self.run_script('salt', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr)
return self.run_script('salt', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=timeout)
def run_ssh(self, arg_str, with_retcode=False, timeout=25,
catch_stderr=False, wipe=False, raw=False):

View file

@ -62,6 +62,14 @@ class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMix
cls.test_symlink_list_file_roots = {'base': [root_dir]}
else:
cls.test_symlink_list_file_roots = None
cls.tmp_dir = tempfile.mkdtemp(dir=TMP)
full_path_to_file = os.path.join(FILES, 'file', 'base', 'testfile')
with salt.utils.fopen(full_path_to_file, 'rb') as s_fp:
with salt.utils.fopen(os.path.join(cls.tmp_dir, 'testfile'), 'wb') as d_fp:
for line in s_fp:
d_fp.write(
line.rstrip(b'\n').rstrip(b'\r') + os.linesep.encode('utf-8')
)
@classmethod
def tearDownClass(cls):
@ -73,6 +81,7 @@ class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMix
salt.utils.files.rm_rf(cls.test_symlink_list_file_roots['base'][0])
except OSError:
pass
salt.utils.rm_rf(cls.tmp_dir)
def tearDown(self):
del self.opts
@ -92,10 +101,10 @@ class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMix
def test_serve_file(self):
with patch.dict(roots.__opts__, {'file_buffer_size': 262144}):
load = {'saltenv': 'base',
'path': os.path.join(FILES, 'file', 'base', 'testfile'),
'path': os.path.join(self.tmp_dir, 'testfile'),
'loc': 0
}
fnd = {'path': os.path.join(FILES, 'file', 'base', 'testfile'),
fnd = {'path': os.path.join(self.tmp_dir, 'testfile'),
'rel': 'testfile'}
ret = roots.serve_file(load, fnd)
@ -144,10 +153,10 @@ class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMix
def test_file_hash(self):
load = {
'saltenv': 'base',
'path': os.path.join(FILES, 'file', 'base', 'testfile'),
'path': os.path.join(self.tmp_dir, 'testfile'),
}
fnd = {
'path': os.path.join(FILES, 'file', 'base', 'testfile'),
'path': os.path.join(self.tmp_dir, 'testfile'),
'rel': 'testfile'
}
ret = roots.file_hash(load, fnd)

View file

@ -20,6 +20,7 @@ from tests.support.paths import TMP_CONF_DIR
# Import Salt libs
import salt.config
import salt.loader
import salt.utils.files
import salt.utils.yaml
from salt.exceptions import SaltRenderError
@ -49,6 +50,7 @@ except ImportError:
HAS_TIMELIB = False
TEMPLATES_DIR = os.path.dirname(os.path.abspath(__file__))
BLINESEP = salt.utils.to_bytes(os.linesep)
class MockFileClient(object):
@ -69,18 +71,48 @@ class MockFileClient(object):
})
def _setup_test_dir(src_dir, test_dir):
os.makedirs(test_dir)
salt.utils.files.recursive_copy(src_dir, test_dir)
filename = os.path.join(test_dir, 'non_ascii')
with salt.utils.fopen(filename, 'wb') as fp:
fp.write(b'Assun\xc3\xa7\xc3\xa3o' + BLINESEP)
filename = os.path.join(test_dir, 'hello_simple')
with salt.utils.fopen(filename, 'wb') as fp:
fp.write(b'world' + BLINESEP)
filename = os.path.join(test_dir, 'hello_import')
lines = [
r"{% from 'macro' import mymacro -%}",
r"{% from 'macro' import mymacro -%}",
r"{{ mymacro('Hey') ~ mymacro(a|default('a'), b|default('b')) }}",
]
with salt.utils.fopen(filename, 'wb') as fp:
for line in lines:
fp.write(line.encode('utf-8') + BLINESEP)
class TestSaltCacheLoader(TestCase):
def __init__(self, *args, **kws):
super(TestSaltCacheLoader, self).__init__(*args, **kws)
def setUp(self):
self.TEMPDIR = tempfile.mkdtemp()
self.TEMPLATES_DIR = os.path.join(self.TEMPDIR, 'files', 'test')
_setup_test_dir(
os.path.join(TEMPLATES_DIR, 'files', 'test'),
self.TEMPLATES_DIR
)
self.opts = {
'cachedir': TEMPLATES_DIR,
'cachedir': self.TEMPDIR,
'file_roots': {
'test': [os.path.join(TEMPLATES_DIR, 'files', 'test')]
'test': [self.TEMPLATES_DIR]
},
'pillar_roots': {
'test': [os.path.join(TEMPLATES_DIR, 'files', 'test')]
'test': [self.TEMPLATES_DIR]
}
}
super(TestSaltCacheLoader, self).setUp()
def tearDown(self):
salt.utils.rm_rf(self.TEMPDIR)
def test_searchpath(self):
'''
@ -102,7 +134,7 @@ class TestSaltCacheLoader(TestCase):
assert len(res) == 3
# res[0] on Windows is unicode and use os.linesep so it works cross OS
self.assertEqual(six.text_type(res[0]), 'world' + os.linesep)
tmpl_dir = os.path.join(TEMPLATES_DIR, 'files', 'test', 'hello_simple')
tmpl_dir = os.path.join(self.TEMPLATES_DIR, 'hello_simple')
self.assertEqual(res[1], tmpl_dir)
assert res[2](), 'Template up to date?'
assert len(loader._file_client.requests)
@ -161,18 +193,24 @@ class TestSaltCacheLoader(TestCase):
class TestGetTemplate(TestCase):
def __init__(self, *args, **kws):
super(TestGetTemplate, self).__init__(*args, **kws)
def setUp(self):
self.TEMPDIR = tempfile.mkdtemp()
self.TEMPLATES_DIR = os.path.join(self.TEMPDIR, 'files', 'test')
_setup_test_dir(
os.path.join(TEMPLATES_DIR, 'files', 'test'),
self.TEMPLATES_DIR
)
self.local_opts = {
'cachedir': TEMPLATES_DIR,
'cachedir': self.TEMPDIR,
'file_client': 'local',
'file_ignore_regex': None,
'file_ignore_glob': None,
'file_roots': {
'test': [os.path.join(TEMPLATES_DIR, 'files', 'test')]
'test': [self.TEMPLATES_DIR]
},
'pillar_roots': {
'test': [os.path.join(TEMPLATES_DIR, 'files', 'test')]
'test': [self.TEMPLATES_DIR]
},
'fileserver_backend': ['roots'],
'hash_type': 'md5',
@ -181,13 +219,17 @@ class TestGetTemplate(TestCase):
'extmods'),
}
self.local_salt = {}
super(TestGetTemplate, self).setUp()
def tearDown(self):
salt.utils.rm_rf(self.TEMPDIR)
def test_fallback(self):
'''
A Template with a filesystem loader is returned as fallback
if the file is not contained in the searchpath
'''
fn_ = os.path.join(TEMPLATES_DIR, 'files', 'test', 'hello_simple')
fn_ = os.path.join(self.TEMPLATES_DIR, 'hello_simple')
with salt.utils.files.fopen(fn_) as fp_:
out = render_jinja_tmpl(
salt.utils.stringutils.to_unicode(fp_.read()),
@ -200,7 +242,7 @@ class TestGetTemplate(TestCase):
A Template with a filesystem loader is returned as fallback
if the file is not contained in the searchpath
'''
filename = os.path.join(TEMPLATES_DIR, 'files', 'test', 'hello_import')
filename = os.path.join(self.TEMPLATES_DIR, 'hello_import')
with salt.utils.files.fopen(filename) as fp_:
out = render_jinja_tmpl(
salt.utils.stringutils.to_unicode(fp_.read()),
@ -217,11 +259,11 @@ class TestGetTemplate(TestCase):
'''
fc = MockFileClient()
with patch.object(SaltCacheLoader, 'file_client', MagicMock(return_value=fc)):
filename = os.path.join(TEMPLATES_DIR, 'files', 'test', 'hello_import')
filename = os.path.join(self.TEMPLATES_DIR, 'hello_import')
with salt.utils.files.fopen(filename) as fp_:
out = render_jinja_tmpl(
salt.utils.stringutils.to_unicode(fp_.read()),
dict(opts={'cachedir': TEMPLATES_DIR, 'file_client': 'remote',
dict(opts={'cachedir': self.TEMPDIR, 'file_client': 'remote',
'file_roots': self.local_opts['file_roots'],
'pillar_roots': self.local_opts['pillar_roots']},
a='Hi', b='Salt', saltenv='test', salt=self.local_salt))
@ -240,8 +282,7 @@ class TestGetTemplate(TestCase):
\{\{ 1/0 \}\} <======================
\{%- endmacro %\}
---.*'''
filename = os.path.join(TEMPLATES_DIR,
'files', 'test', 'hello_import_generalerror')
filename = os.path.join(self.TEMPLATES_DIR, 'hello_import_generalerror')
fc = MockFileClient()
with patch.object(SaltCacheLoader, 'file_client', MagicMock(return_value=fc)):
with salt.utils.files.fopen(filename) as fp_:
@ -264,8 +305,7 @@ class TestGetTemplate(TestCase):
\{\{b.greetee\}\} <-- error is here <======================
\{%- endmacro %\}
---'''
filename = os.path.join(TEMPLATES_DIR,
'files', 'test', 'hello_import_undefined')
filename = os.path.join(self.TEMPLATES_DIR, 'hello_import_undefined')
fc = MockFileClient()
with patch.object(SaltCacheLoader, 'file_client', MagicMock(return_value=fc)):
with salt.utils.files.fopen(filename) as fp_:
@ -288,8 +328,7 @@ class TestGetTemplate(TestCase):
\{\{ greeting ~ ' ' ~ greetee \}\} !
\{%- endmacro %\}
---.*'''
filename = os.path.join(TEMPLATES_DIR,
'files', 'test', 'hello_import_error')
filename = os.path.join(self.TEMPLATES_DIR, 'hello_import_error')
fc = MockFileClient()
with patch.object(SaltCacheLoader, 'file_client', MagicMock(return_value=fc)):
with salt.utils.files.fopen(filename) as fp_:
@ -303,22 +342,22 @@ class TestGetTemplate(TestCase):
def test_non_ascii_encoding(self):
fc = MockFileClient()
with patch.object(SaltCacheLoader, 'file_client', MagicMock(return_value=fc)):
filename = os.path.join(TEMPLATES_DIR, 'files', 'test', 'hello_import')
filename = os.path.join(self.TEMPLATES_DIR, 'hello_import')
with salt.utils.files.fopen(filename) as fp_:
out = render_jinja_tmpl(
salt.utils.stringutils.to_unicode(fp_.read()),
dict(opts={'cachedir': TEMPLATES_DIR, 'file_client': 'remote',
dict(opts={'cachedir': self.TEMPDIR, 'file_client': 'remote',
'file_roots': self.local_opts['file_roots'],
'pillar_roots': self.local_opts['pillar_roots']},
a='Hi', b='Sàlt', saltenv='test', salt=self.local_salt))
self.assertEqual(out, salt.utils.stringutils.to_unicode('Hey world !Hi Sàlt !' + os.linesep))
self.assertEqual(fc.requests[0]['path'], 'salt://macro')
filename = os.path.join(TEMPLATES_DIR, 'files', 'test', 'non_ascii')
filename = os.path.join(self.TEMPLATES_DIR, 'non_ascii')
with salt.utils.files.fopen(filename) as fp_:
out = render_jinja_tmpl(
salt.utils.stringutils.to_unicode(fp_.read(), 'utf-8'),
dict(opts={'cachedir': TEMPLATES_DIR, 'file_client': 'remote',
dict(opts={'cachedir': self.TEMPDIR, 'file_client': 'remote',
'file_roots': self.local_opts['file_roots'],
'pillar_roots': self.local_opts['pillar_roots']},
a='Hi', b='Sàlt', saltenv='test', salt=self.local_salt))
@ -375,7 +414,7 @@ class TestGetTemplate(TestCase):
self.assertEqual(response, '02')
def test_non_ascii(self):
fn = os.path.join(TEMPLATES_DIR, 'files', 'test', 'non_ascii')
fn = os.path.join(self.TEMPLATES_DIR, 'non_ascii')
out = JINJA(
fn,
opts=self.local_opts,
@ -386,6 +425,36 @@ class TestGetTemplate(TestCase):
result = salt.utils.stringutils.to_unicode(fp.read(), 'utf-8')
self.assertEqual(salt.utils.stringutils.to_unicode('Assunção' + os.linesep), result)
def test_get_context_has_enough_context(self):
template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf'
context = get_context(template, 8)
expected = '---\n[...]\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\n[...]\n---'
self.assertEqual(expected, context)
def test_get_context_at_top_of_file(self):
template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf'
context = get_context(template, 1)
expected = '---\n1\n2\n3\n4\n5\n6\n[...]\n---'
self.assertEqual(expected, context)
def test_get_context_at_bottom_of_file(self):
template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf'
context = get_context(template, 15)
expected = '---\n[...]\na\nb\nc\nd\ne\nf\n---'
self.assertEqual(expected, context)
def test_get_context_2_context_lines(self):
template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf'
context = get_context(template, 8, num_lines=2)
expected = '---\n[...]\n6\n7\n8\n9\na\n[...]\n---'
self.assertEqual(expected, context)
def test_get_context_with_marker(self):
template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf'
context = get_context(template, 8, num_lines=2, marker=' <---')
expected = '---\n[...]\n6\n7\n8 <---\n9\na\n[...]\n---'
self.assertEqual(expected, context)
def test_render_with_syntax_error(self):
template = 'hello\n\n{{ bad\n\nfoo'
expected = r'.*---\nhello\n\n{{ bad\n\nfoo <======================\n---'

View file

@ -54,20 +54,30 @@ class LocalClientTestCase(TestCase,
self.client.cmd_subset('*', 'first.func', sub=1, cli=True)
try:
cmd_cli_mock.assert_called_with(['minion2'], 'first.func', (), progress=False,
kwarg=None, tgt_type='list',
kwarg=None, tgt_type='list', full_return=False,
ret='')
except AssertionError:
cmd_cli_mock.assert_called_with(['minion1'], 'first.func', (), progress=False,
kwarg=None, tgt_type='list',
kwarg=None, tgt_type='list', full_return=False,
ret='')
self.client.cmd_subset('*', 'first.func', sub=10, cli=True)
try:
cmd_cli_mock.assert_called_with(['minion2', 'minion1'], 'first.func', (), progress=False,
kwarg=None, tgt_type='list',
kwarg=None, tgt_type='list', full_return=False,
ret='')
except AssertionError:
cmd_cli_mock.assert_called_with(['minion1', 'minion2'], 'first.func', (), progress=False,
kwarg=None, tgt_type='list',
kwarg=None, tgt_type='list', full_return=False,
ret='')
ret = self.client.cmd_subset('*', 'first.func', sub=1, cli=True, full_return=True)
try:
cmd_cli_mock.assert_called_with(['minion2'], 'first.func', (), progress=False,
kwarg=None, tgt_type='list', full_return=True,
ret='')
except AssertionError:
cmd_cli_mock.assert_called_with(['minion1'], 'first.func', (), progress=False,
kwarg=None, tgt_type='list', full_return=True,
ret='')
@skipIf(salt.utils.platform.is_windows(), 'Not supported on Windows')

View file

@ -18,13 +18,14 @@ import salt.utils.event as event
from salt.exceptions import SaltSystemExit
import salt.syspaths
import tornado
import tornado.testing
from salt.ext.six.moves import range
__opts__ = {}
@skipIf(NO_MOCK, NO_MOCK_REASON)
class MinionTestCase(TestCase):
class MinionTestCase(TestCase, tornado.testing.AsyncTestCase):
def test_invalid_master_address(self):
with patch.dict(__opts__, {'ipv6': False, 'master': float('127.0'), 'master_port': '4555', 'retry_dns': False}):
self.assertRaises(SaltSystemExit, salt.minion.resolve_dns, __opts__)
@ -120,6 +121,10 @@ class MinionTestCase(TestCase):
try:
event_publisher = event.AsyncEventPublisher(__opts__)
result = True
except ValueError:
# There are rare cases where we operate a closed socket, especially in containers.
# In this case, don't fail the test because we'll catch it down the road.
result = True
except SaltSystemExit:
result = False
self.assertTrue(result)