Merge branch 'develop' of https://github.com/saltstack/salt into develop

This commit is contained in:
Mike Chesnut 2012-05-10 13:28:43 -07:00
commit 9fc390ad15
32 changed files with 753 additions and 152 deletions

View file

@ -1,4 +1,4 @@
.TH "SALT-CALL" "1" "April 27, 2012" "0.9.9" "Salt"
.TH "SALT-CALL" "1" "May 11, 2012" "0.9.9" "Salt"
.SH NAME
salt-call \- salt-call Documentation
.
@ -95,6 +95,11 @@ Print the output from the salt command in json.
.B \-\-no\-color
Disable all colored output
.UNINDENT
.SH SEE ALSO
.sp
\fIsalt(1)\fP
\fIsalt\-master(1)\fP
\fIsalt\-minion(1)\fP
.SH AUTHOR
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
.SH COPYRIGHT

View file

@ -1,4 +1,4 @@
.TH "SALT-CP" "1" "April 27, 2012" "0.9.9" "Salt"
.TH "SALT-CP" "1" "May 11, 2012" "0.9.9" "Salt"
.SH NAME
salt-cp \- salt-cp Documentation
.
@ -110,6 +110,11 @@ The location of the salt master configuration file, the salt master
settings are required to know where the connections are;
default=/etc/salt/master
.UNINDENT
.SH SEE ALSO
.sp
\fIsalt(1)\fP
\fIsalt\-master(1)\fP
\fIsalt\-minion(1)\fP
.SH AUTHOR
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
.SH COPYRIGHT

View file

@ -1,4 +1,4 @@
.TH "SALT-KEY" "1" "April 27, 2012" "0.9.9" "Salt"
.TH "SALT-KEY" "1" "May 11, 2012" "0.9.9" "Salt"
.SH NAME
salt-key \- salt-key Documentation
.

View file

@ -1,4 +1,4 @@
.TH "SALT-MASTER" "1" "April 27, 2012" "0.9.9" "Salt"
.TH "SALT-MASTER" "1" "May 11, 2012" "0.9.9" "Salt"
.SH NAME
salt-master \- salt-master Documentation
.
@ -71,6 +71,11 @@ Console log level. One of \fBinfo\fP, \fBnone\fP, \fBgarbage\fP,
\fBtrace\fP, \fBwarning\fP, \fBerror\fP, \fBdebug\fP. For the logfile
settings see the config file. Default: \fBwarning\fP.
.UNINDENT
.SH SEE ALSO
.sp
\fIsalt(1)\fP
\fIsalt(7)\fP
\fIsalt\-minion(1)\fP
.SH AUTHOR
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
.SH COPYRIGHT

View file

@ -1,4 +1,4 @@
.TH "SALT-MINION" "1" "April 27, 2012" "0.9.9" "Salt"
.TH "SALT-MINION" "1" "May 11, 2012" "0.9.9" "Salt"
.SH NAME
salt-minion \- salt-minion Documentation
.
@ -72,6 +72,11 @@ Console log level. One of \fBinfo\fP, \fBnone\fP, \fBgarbage\fP,
\fBtrace\fP, \fBwarning\fP, \fBerror\fP, \fBdebug\fP. For the logfile
settings see the config file. Default: \fBwarning\fP.
.UNINDENT
.SH SEE ALSO
.sp
\fIsalt(1)\fP
\fIsalt(7)\fP
\fIsalt\-master(1)\fP
.SH AUTHOR
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
.SH COPYRIGHT

View file

@ -1,4 +1,4 @@
.TH "SALT-RUN" "1" "April 27, 2012" "0.9.9" "Salt"
.TH "SALT-RUN" "1" "May 11, 2012" "0.9.9" "Salt"
.SH NAME
salt-run \- salt-run Documentation
.
@ -57,6 +57,11 @@ The location of the salt master configuration file, the salt master
settings are required to know where the connections are;
default=/etc/salt/master
.UNINDENT
.SH SEE ALSO
.sp
\fIsalt(1)\fP
\fIsalt\-master(1)\fP
\fIsalt\-minion(1)\fP
.SH AUTHOR
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
.SH COPYRIGHT

View file

@ -1,4 +1,4 @@
.TH "SALT-SYNDIC" "1" "April 27, 2012" "0.9.9" "Salt"
.TH "SALT-SYNDIC" "1" "May 11, 2012" "0.9.9" "Salt"
.SH NAME
salt-syndic \- salt-syndic Documentation
.
@ -66,6 +66,11 @@ The master configuration file to use, the default is /etc/salt/master
.B \-\-minion\-config=MINION_CONFIG
The minion configuration file to use, the default is /etc/salt/minion
.UNINDENT
.SH SEE ALSO
.sp
\fIsalt(1)\fP
\fIsalt\-master(1)\fP
\fIsalt\-minion(1)\fP
.SH AUTHOR
Thomas S. Hatch <thatch@gmail.com> and many others, please see the Authors file
.SH COPYRIGHT

View file

@ -1,4 +1,4 @@
.TH "SALT" "1" "April 27, 2012" "0.9.9" "Salt"
.TH "SALT" "1" "May 11, 2012" "0.9.9" "Salt"
.SH NAME
salt \- salt
.

File diff suppressed because it is too large Load diff

View file

@ -60,3 +60,10 @@ Options
.. option:: --no-color
Disable all colored output
See also
========
:manpage:`salt(1)`
:manpage:`salt-master(1)`
:manpage:`salt-minion(1)`

View file

@ -78,3 +78,10 @@ Options
The location of the salt master configuration file, the salt master
settings are required to know where the connections are;
default=/etc/salt/master
See also
========
:manpage:`salt(1)`
:manpage:`salt-master(1)`
:manpage:`salt-minion(1)`

View file

@ -44,3 +44,10 @@ Options
Console log level. One of ``info``, ``none``, ``garbage``,
``trace``, ``warning``, ``error``, ``debug``. For the logfile
settings see the config file. Default: ``warning``.
See also
========
:manpage:`salt(1)`
:manpage:`salt(7)`
:manpage:`salt-minion(1)`

View file

@ -45,3 +45,10 @@ Options
Console log level. One of ``info``, ``none``, ``garbage``,
``trace``, ``warning``, ``error``, ``debug``. For the logfile
settings see the config file. Default: ``warning``.
See also
========
:manpage:`salt(1)`
:manpage:`salt(7)`
:manpage:`salt-master(1)`

View file

@ -32,3 +32,10 @@ Options
The location of the salt master configuration file, the salt master
settings are required to know where the connections are;
default=/etc/salt/master
See also
========
:manpage:`salt(1)`
:manpage:`salt-master(1)`
:manpage:`salt-minion(1)`

View file

@ -40,3 +40,10 @@ Options
.. option:: --minion-config=MINION_CONFIG
The minion configuration file to use, the default is /etc/salt/minion
See also
========
:manpage:`salt(1)`
:manpage:`salt-master(1)`
:manpage:`salt-minion(1)`

View file

@ -523,6 +523,17 @@ def saltversion():
from salt import __version__
return {'saltversion': __version__}
def shell():
'''
Return the default shell to use on this system
'''
# Provides:
# shell
ret = {'shell': '/bin/sh'}
if 'SHELL' in os.environ:
ret['shell'] = os.environ['SHELL']
return ret
# Relatively complex mini-algorithm to iterate over the various
# sections of dmidecode output and return matches for specific
# lines containing data we want, but only in the right section.

View file

@ -7,10 +7,11 @@ Routines to set up a minion
# Import python libs
import imp
import logging
import os
import imp
import salt
import logging
import tempfile
from salt.exceptions import LoaderError
log = logging.getLogger(__name__)
@ -246,7 +247,7 @@ class Loader(object):
return None
try:
if full.endswith('.pyx') and self.opts['cython_enable']:
mod = pyximport.load_module(name, full, '/tmp')
mod = pyximport.load_module(name, full, tempfile.gettempdir())
else:
fn_, path, desc = imp.find_module(name, self.module_dirs)
mod = imp.load_module(
@ -339,7 +340,7 @@ class Loader(object):
mod = pyximport.load_module(
'{0}_{1}'.format(name, self.tag),
names[name],
'/tmp')
tempfile.gettempdir())
else:
fn_, path, desc = imp.find_module(name, self.module_dirs)
mod = imp.load_module(

View file

@ -7,14 +7,15 @@ involves preparing the three listeners and the workers needed by the master.
import os
import re
import time
import errno
import signal
import shutil
import logging
import hashlib
import tempfile
import datetime
import signal
import multiprocessing
import subprocess
import multiprocessing
# Import zeromq
import zmq
@ -32,6 +33,7 @@ import salt.client
import salt.payload
import salt.pillar
import salt.state
from salt.utils.debug import enable_sigusr1_handler
log = logging.getLogger(__name__)
@ -148,6 +150,8 @@ class Master(SMaster):
'''
Turn on the master server components
'''
enable_sigusr1_handler()
log.warn('Starting the Salt Master')
clear_old_jobs_proc = multiprocessing.Process(
target=self._clear_old_jobs)
@ -219,8 +223,15 @@ class Publisher(multiprocessing.Process):
try:
while True:
package = pull_sock.recv()
pub_sock.send(package)
# Catch and handle EINTR from when this process is sent
# SIGUSR1 gracefully so we don't choke and die horribly
try:
package = pull_sock.recv()
pub_sock.send(package)
except zmq.ZMQError as exc:
if exc.errno == errno.EINTR:
continue
raise exc
except KeyboardInterrupt:
pub_sock.close()
pull_sock.close()
@ -270,7 +281,13 @@ class ReqServer(object):
self.workers.bind(self.w_uri)
zmq.device(zmq.QUEUE, self.clients, self.workers)
while True:
try:
zmq.device(zmq.QUEUE, self.clients, self.workers)
except zmq.ZMQError as exc:
if exc.errno == errno.EINTR:
continue
raise exc
def start_publisher(self):
'''
@ -320,10 +337,16 @@ class MWorker(multiprocessing.Process):
socket.connect(w_uri)
while True:
package = socket.recv()
payload = self.serial.loads(package)
ret = self.serial.dumps(self._handle_payload(payload))
socket.send(ret)
try:
package = socket.recv()
payload = self.serial.loads(package)
ret = self.serial.dumps(self._handle_payload(payload))
socket.send(ret)
# Properly handle EINTR from SIGUSR1
except zmq.ZMQError as exc:
if exc.errno == errno.EINTR:
continue
raise exc
except KeyboardInterrupt:
socket.close()
@ -976,7 +999,7 @@ class ClearFuncs(object):
# Set up the payload
payload = {'enc': 'aes'}
# Altering the contents of the publish load is serious!! Changes here
# break compatibility with minion/master versions and even tiny
# break compatibility with minion/master versions and even tiny
# additions can have serious implications on the performance of the
# publish commands.
#

View file

@ -26,6 +26,7 @@ import salt.crypt
import salt.loader
import salt.utils
import salt.payload
from salt.utils.debug import enable_sigusr1_handler
log = logging.getLogger(__name__)
@ -475,6 +476,10 @@ class Minion(object):
socket.setsockopt(zmq.SUBSCRIBE, '')
socket.setsockopt(zmq.IDENTITY, self.opts['id'])
socket.connect(self.master_pub)
# Make sure to gracefully handle SIGUSR1
enable_sigusr1_handler()
if self.opts['sub_timeout']:
last = time.time()
while True:

View file

@ -216,7 +216,7 @@ def purge(pkg):
return ret_pkgs
def upgrade(refresh=True):
def upgrade(refresh=True, **kwargs):
'''
Upgrades all packages via ``apt-get dist-upgrade``

View file

@ -56,6 +56,10 @@ def _run(cmd,
if not cwd:
cwd = os.path.expanduser('~{0}'.format('' if not runas else runas))
if not os.path.isfile(shell) or not os.access(shell, os.X_OK):
msg = 'The shell {0} is not available'.format(shell)
raise CommandExecutionError(msg)
# TODO: Figure out the proper way to do this in windows
disable_runas = [
'Windows',

View file

@ -146,7 +146,7 @@ def _interfaces_ifconfig():
ret = {}
piface = re.compile('^(\w+)')
pmac = re.compile('.*?(?:HWaddr|ether) (.*?)\s')
pmac = re.compile('.*?(?:HWaddr|ether) ([0-9a-fA-F:]+)')
pip = re.compile('.*?(?:inet addr:|inet )(.*?)\s')
pip6 = re.compile('.*?(?:inet6 addr: (.*?)/|inet6 )([0-9a-fA-F:]+)')
pmask = re.compile('.*?(?:Mask:|netmask )(?:(0x[0-9a-fA-F]{8})|([\d\.]+))')
@ -393,7 +393,7 @@ def hwaddr(interface):
salt '*' network.hwaddr eth0
'''
data = interfaces().get(interface)
if data:
if data and 'hwaddr' in data:
return data['hwaddr']
else:
return None

View file

@ -30,14 +30,6 @@ syslog if there is no disk space:
cmd:
- run
- unless: echo 'foo' > /tmp/.test
.. warning::
Please be advised that on Unix systems the shell being used by python
to run executions is /bin/sh, this requires that commands are formatted
to execute under /bin/sh. Some capabilities of newer shells such as bash,
zsh and ksh will not always be available on minions.
'''
import grp
@ -49,7 +41,8 @@ def wait(name,
unless=None,
cwd='/root',
user=None,
group=None):
group=None,
shell=None):
'''
Run the given command only if the watch statement calls it
@ -74,6 +67,9 @@ def wait(name,
group
The group context to run the command as
shell
The shell to use for execution, defaults to /bin/sh
'''
return {'name': name,
'changes': {},
@ -86,7 +82,7 @@ def run(name,
cwd='/root',
user=None,
group=None,
shell='/bin/sh',
shell=None,
env=()):
'''
Run a command if certain circumstances are met
@ -112,6 +108,9 @@ def run(name,
group
The group context to run the command as
shell
The shell to use for execution, defaults to the shell grain
'''
ret = {'name': name,
'changes': {},
@ -147,7 +146,7 @@ def run(name,
cmd_kwargs = {'cwd': cwd,
'runas': user,
'shell': shell,
'shell': shell or __grains__['shell'],
'env': env}
if onlyif:
@ -167,7 +166,7 @@ def run(name,
try:
cmd_all = __salt__['cmd.run_all'](name, **cmd_kwargs)
except CommandExecutionError as e:
ret['comment'] = e
ret['comment'] = str(e)
return ret
ret['changes'] = cmd_all

View file

@ -772,6 +772,7 @@ def managed(name,
template=None,
makedirs=False,
context=None,
replace=True,
defaults=None,
env=None,
**kwargs):
@ -821,6 +822,10 @@ def managed(name,
directories will be created to facilitate the creation of the named
file.
replace
If this file should be replaced, if false then this command will
be ignored if the file exists already. Default is true.
context
Overrides default context variables passed to the template.
@ -845,6 +850,13 @@ def managed(name,
ret['result'] = False
return ret
if not replace:
if os.path.exists(name):
ret['comment'] = 'File {0} exists. No changes made'.format(name)
return ret
if not source:
return touch(name, makedirs=makedirs)
if __opts__['test']:
ret['result'], ret['comment'] = _check_managed(
name,

View file

@ -63,7 +63,8 @@ def _changes(
change['groups'] = groups
if home:
if lusr['home'] != home:
change['home'] = home
if not home is True:
change['home'] = home
if shell:
if lusr['shell'] != shell:
change['shell'] = shell

43
salt/utils/debug.py Normal file
View file

@ -0,0 +1,43 @@
'''
Print a stacktrace when sent a SIGUSR1 for debugging
'''
import os
import sys
import time
import signal
import datetime
import tempfile
import traceback
def _makepretty(printout, stack):
'''
Pretty print the stack trace and environment information
for debugging those hard to reproduce user problems. :)
'''
printout.write('======== Salt Debug Stack Trace =========\n')
traceback.print_stack(stack, file=printout)
printout.write('=========================================\n')
def _handle_sigusr1(sig, stack):
'''
Signal handler for SIGUSR1, only available on Unix-like systems
'''
# When running in the foreground, do the right thing
# and spit out the debug info straight to the console
if sys.stderr.isatty():
output = sys.stderr
_makepretty(output, stack)
else:
filename = 'salt-debug-{0}.log'.format(int(time.time()))
destfile = os.path.join(tempfile.gettempdir(), filename)
with open(destfile, 'w') as output:
_makepretty(output, stack)
def enable_sigusr1_handler():
'''
Pretty print a stack trace to the console or a debug log under /tmp
when any of the salt daemons such as salt-master are sent a SIGUSR1
'''
signal.signal(signal.SIGUSR1, _handle_sigusr1)

View file

@ -495,6 +495,9 @@ class Finder(object):
_REQUIRES_STAT: list(),
_REQUIRES_CONTENTS: list()}
for key, value in options.iteritems():
if key.startswith('_'):
# this is a passthrough object, continue
continue
if value is None or len(value) == 0:
raise ValueError('missing value for "{0}" option'.format(key))
try:

View file

@ -36,23 +36,23 @@ class TestDaemon(object):
Start a master and minion
'''
self.master_opts = salt.config.master_config(
os.path.join(INTEGRATION_TEST_DIR, 'files/conf/master'))
os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'master'))
self.minion_opts = salt.config.minion_config(
os.path.join(INTEGRATION_TEST_DIR, 'files/conf/minion'))
os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'minion'))
self.smaster_opts = salt.config.master_config(
os.path.join(INTEGRATION_TEST_DIR, 'files/conf/syndic_master'))
os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'syndic_master'))
self.syndic_opts = salt.config.minion_config(
os.path.join(INTEGRATION_TEST_DIR, 'files/conf/syndic'))
os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'syndic'))
self.syndic_opts['_master_conf_file'] = os.path.join(
INTEGRATION_TEST_DIR,
'files/conf/master'
)
# Set up config options that require internal data
self.master_opts['pillar_roots'] = {
'base': [os.path.join(FILES, 'pillar/base')]
'base': [os.path.join(FILES, 'pillar', 'base')]
}
self.master_opts['file_roots'] = {
'base': [os.path.join(FILES, 'file/base')]
'base': [os.path.join(FILES, 'file', 'base')]
}
self.master_opts['ext_pillar'] = [
{'cmd_yaml': 'cat {0}'.format(
@ -135,7 +135,7 @@ class ModuleCase(TestCase):
self.client = salt.client.LocalClient(
os.path.join(
INTEGRATION_TEST_DIR,
'files/conf/master'
'files', 'conf', 'master'
)
)
@ -154,7 +154,7 @@ class ModuleCase(TestCase):
return salt.config.minion_config(
os.path.join(
INTEGRATION_TEST_DIR,
'files/conf/minion'
'files', 'conf', 'minion'
)
)
@ -165,7 +165,7 @@ class ModuleCase(TestCase):
return salt.config.minion_config(
os.path.join(
INTEGRATION_TEST_DIR,
'files/conf/master'
'files', 'conf', 'master'
)
)
@ -180,7 +180,7 @@ class SyndicCase(TestCase):
self.client = salt.client.LocalClient(
os.path.join(
INTEGRATION_TEST_DIR,
'files/conf/syndic_master'
'files', 'conf', 'syndic_master'
)
)
@ -216,7 +216,7 @@ class ShellCase(TestCase):
'''
Execute salt-key
'''
mconf = os.path.join(INTEGRATION_TEST_DIR, 'files/conf/master')
mconf = os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'master')
arg_str = '-c {0} {1}'.format(mconf, arg_str)
return self.run_script('salt', arg_str)
@ -224,7 +224,7 @@ class ShellCase(TestCase):
'''
Execute salt-key
'''
mconf = os.path.join(INTEGRATION_TEST_DIR, 'files/conf/master')
mconf = os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'master')
arg_str = '-c {0} {1}'.format(mconf, arg_str)
return self.run_script('salt-run', arg_str)
@ -232,6 +232,6 @@ class ShellCase(TestCase):
'''
Execute salt-key
'''
mconf = os.path.join(INTEGRATION_TEST_DIR, 'files/conf/master')
mconf = os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf', 'master')
arg_str = '-c {0} {1}'.format(mconf, arg_str)
return self.run_script('salt-key', arg_str)

View file

@ -32,26 +32,30 @@ class TestModulesGrains(integration.ModuleCase):
'''
grains.ls
'''
check_for = (
'cpuarch',
'cpu_flags',
'cpu_model',
'domain',
'fqdn',
'host',
'kernel',
'kernelrelease',
'localhost',
'mem_total',
'num_cpus',
'os',
'path',
'ps',
'pythonpath',
'pythonversion',
'saltpath',
'saltversion',
'virtual',
)
lsgrains = self.run_function('grains.ls')
self.assertTrue('cpu_model' in lsgrains)
self.assertTrue('cpu_flags' in lsgrains)
self.assertTrue('cpuarch' in lsgrains)
self.assertTrue('domain' in lsgrains)
self.assertTrue('fqdn' in lsgrains)
self.assertTrue('host' in lsgrains)
self.assertTrue('kernel' in lsgrains)
self.assertTrue('kernelrelease' in lsgrains)
self.assertTrue('localhost' in lsgrains)
self.assertTrue('mem_total' in lsgrains)
self.assertTrue('num_cpus' in lsgrains)
self.assertTrue('os' in lsgrains)
self.assertTrue('path' in lsgrains)
self.assertTrue('ps' in lsgrains)
self.assertTrue('pythonpath' in lsgrains)
self.assertTrue('pythonversion' in lsgrains)
self.assertTrue('saltpath' in lsgrains)
self.assertTrue('saltversion' in lsgrains)
self.assertTrue('virtual' in lsgrains)
for grain_name in check_for:
self.assertTrue(grain_name in lsgrains)
if __name__ == "__main__":
loader = TestLoader()

View file

@ -44,17 +44,22 @@ class PublishModuleTest(integration.ModuleCase):
'cheese=spam',
]
)['minion']['ret']
self.assertTrue('__pub_arg' in ret)
self.assertTrue('__pub_id' in ret)
self.assertTrue('__pub_fun' in ret)
self.assertTrue('__pub_jid' in ret)
self.assertTrue('__pub_tgt' in ret)
self.assertTrue('__pub_tgt_type' in ret)
self.assertTrue('__pub_ret' in ret)
self.assertTrue('cheese' in ret)
self.assertEqual(ret['cheese'], 'spam')
check_true = (
'cheese',
'__pub_arg',
'__pub_fun',
'__pub_id',
'__pub_jid',
'__pub_ret',
'__pub_tgt',
'__pub_tgt_type',
)
for name in check_true:
self.assertTrue(name in ret)
self.assertEqual(ret['cheese'], 'spam')
self.assertEqual(ret['__pub_arg'], ['cheese=spam'])
self.assertEqual(ret['__pub_id'], 'minion')
self.assertEqual(ret['__pub_id'], 'minion')
self.assertEqual(ret['__pub_fun'], 'test.kwarg')
def test_reject_minion(self):

View file

@ -1,10 +1,11 @@
from os import path
from salt.utils.jinja import SaltCacheLoader, get_template
import os
import tempfile
from jinja2 import Environment
from salt.utils.jinja import SaltCacheLoader, get_template
from saltunittest import TestCase
TEMPLATES_DIR = path.dirname(path.abspath(__file__))
TEMPLATES_DIR = os.path.dirname(os.path.abspath(__file__))
class MockFileClient(object):
'''
@ -15,7 +16,7 @@ class MockFileClient(object):
self.requests = []
def get_file(self, template, dest='', makedirs=False, env='base'):
self.requests.append({
'path': template,
'path': template,
'dest': dest,
'makedirs': makedirs,
'env': env
@ -26,8 +27,9 @@ class TestSaltCacheLoader(TestCase):
'''
The searchpath is based on the cachedir option and the env parameter
'''
loader = SaltCacheLoader({'cachedir': '/tmp'}, env='test')
assert loader.searchpath == '/tmp/files/test'
tmp = tempfile.gettempdir()
loader = SaltCacheLoader({'cachedir': tmp}, env='test')
assert loader.searchpath == os.path.join(tmp, 'files', 'test')
def test_mockclient(self):
'''
A MockFileClient is used that records all file request normally send to the master.
@ -37,7 +39,8 @@ class TestSaltCacheLoader(TestCase):
res = loader.get_source(None, 'hello_simple')
assert len(res) == 3
self.assertEqual(res[0], 'world\n')
self.assertEqual(res[1], '%s/files/test/hello_simple' % TEMPLATES_DIR)
tmpl_dir = os.path.join(TEMPLATES_DIR, 'files', 'test', 'hello_simple')
self.assertEqual(res[1], tmpl_dir)
assert res[2](), "Template up to date?"
assert len(fc.requests)
self.assertEqual(fc.requests[0]['path'], 'salt://hello_simple')
@ -81,10 +84,10 @@ class TestSaltCacheLoader(TestCase):
class TestGetTemplate(TestCase):
def test_fallback(self):
'''
A Template without loader is returned as fallback
A Template without loader is returned as fallback
if the file is not contained in the searchpath
'''
filename = '%s/files/test/hello_simple' % TEMPLATES_DIR
filename = os.path.join(TEMPLATES_DIR, 'files', 'test', 'hello_simple')
tmpl = get_template(filename, {'cachedir': TEMPLATES_DIR}, env='other')
self.assertEqual(tmpl.render(), 'world')
def test_fallback_noloader(self):
@ -92,7 +95,7 @@ class TestGetTemplate(TestCase):
If the fallback is used any attempt to load other templates
will raise a TypeError.
'''
filename = '%s/files/test/hello_import' % TEMPLATES_DIR
filename = os.path.join(TEMPLATES_DIR, 'files', 'test', 'hello_import')
tmpl = get_template(filename, {'cachedir': TEMPLATES_DIR}, env='other')
self.assertRaises(TypeError, tmpl.render)
def test_env(self):
@ -105,8 +108,8 @@ class TestGetTemplate(TestCase):
fc = MockFileClient()
# monkey patch file client
_fc = SaltCacheLoader.file_client
SaltCacheLoader.file_client = lambda loader: fc
filename = '%s/files/test/hello_import' % TEMPLATES_DIR
SaltCacheLoader.file_client = lambda loader: fc
filename = os.path.join(TEMPLATES_DIR, 'files', 'test', 'hello_import')
tmpl = get_template(filename, {'cachedir': TEMPLATES_DIR}, env='test')
self.assertEqual(tmpl.render(a='Hi', b='Salt'), 'Hey world !Hi Salt !')
self.assertEqual(fc.requests[0]['path'], 'salt://macro')