123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- # -*- coding: utf-8 -*-
- from __future__ import absolute_import, print_function, unicode_literals
- import codecs
- import glob
- import logging
- import os
- import textwrap
- import salt.loader
- import salt.utils.data
- import salt.utils.files
- import salt.utils.reactor as reactor
- import salt.utils.yaml
- from tests.support.unit import TestCase
- from tests.support.mixins import AdaptedConfigurationTestCaseMixin
- from tests.support.mock import (
- patch,
- MagicMock,
- Mock,
- mock_open,
- )
- REACTOR_CONFIG = '''\
- reactor:
- - old_runner:
- - /srv/reactor/old_runner.sls
- - old_wheel:
- - /srv/reactor/old_wheel.sls
- - old_local:
- - /srv/reactor/old_local.sls
- - old_cmd:
- - /srv/reactor/old_cmd.sls
- - old_caller:
- - /srv/reactor/old_caller.sls
- - new_runner:
- - /srv/reactor/new_runner.sls
- - new_wheel:
- - /srv/reactor/new_wheel.sls
- - new_local:
- - /srv/reactor/new_local.sls
- - new_cmd:
- - /srv/reactor/new_cmd.sls
- - new_caller:
- - /srv/reactor/new_caller.sls
- '''
- REACTOR_DATA = {
- 'runner': {'data': {'message': 'This is an error'}},
- 'wheel': {'data': {'id': 'foo'}},
- 'local': {'data': {'pkg': 'zsh', 'repo': 'updates'}},
- 'cmd': {'data': {'pkg': 'zsh', 'repo': 'updates'}},
- 'caller': {'data': {'path': '/tmp/foo'}},
- }
- SLS = {
- '/srv/reactor/old_runner.sls': textwrap.dedent('''\
- raise_error:
- runner.error.error:
- - name: Exception
- - message: {{ data['data']['message'] }}
- '''),
- '/srv/reactor/old_wheel.sls': textwrap.dedent('''\
- remove_key:
- wheel.key.delete:
- - match: {{ data['data']['id'] }}
- '''),
- '/srv/reactor/old_local.sls': textwrap.dedent('''\
- install_zsh:
- local.state.single:
- - tgt: test
- - arg:
- - pkg.installed
- - {{ data['data']['pkg'] }}
- - kwarg:
- fromrepo: {{ data['data']['repo'] }}
- '''),
- '/srv/reactor/old_cmd.sls': textwrap.dedent('''\
- install_zsh:
- cmd.state.single:
- - tgt: test
- - arg:
- - pkg.installed
- - {{ data['data']['pkg'] }}
- - kwarg:
- fromrepo: {{ data['data']['repo'] }}
- '''),
- '/srv/reactor/old_caller.sls': textwrap.dedent('''\
- touch_file:
- caller.file.touch:
- - args:
- - {{ data['data']['path'] }}
- '''),
- '/srv/reactor/new_runner.sls': textwrap.dedent('''\
- raise_error:
- runner.error.error:
- - args:
- - name: Exception
- - message: {{ data['data']['message'] }}
- '''),
- '/srv/reactor/new_wheel.sls': textwrap.dedent('''\
- remove_key:
- wheel.key.delete:
- - args:
- - match: {{ data['data']['id'] }}
- '''),
- '/srv/reactor/new_local.sls': textwrap.dedent('''\
- install_zsh:
- local.state.single:
- - tgt: test
- - args:
- - fun: pkg.installed
- - name: {{ data['data']['pkg'] }}
- - fromrepo: {{ data['data']['repo'] }}
- '''),
- '/srv/reactor/new_cmd.sls': textwrap.dedent('''\
- install_zsh:
- cmd.state.single:
- - tgt: test
- - args:
- - fun: pkg.installed
- - name: {{ data['data']['pkg'] }}
- - fromrepo: {{ data['data']['repo'] }}
- '''),
- '/srv/reactor/new_caller.sls': textwrap.dedent('''\
- touch_file:
- caller.file.touch:
- - args:
- - name: {{ data['data']['path'] }}
- '''),
- }
- LOW_CHUNKS = {
- # Note that the "name" value in the chunk has been overwritten by the
- # "name" argument in the SLS. This is one reason why the new schema was
- # needed.
- 'old_runner': [{
- 'state': 'runner',
- '__id__': 'raise_error',
- '__sls__': '/srv/reactor/old_runner.sls',
- 'order': 1,
- 'fun': 'error.error',
- 'name': 'Exception',
- 'message': 'This is an error',
- }],
- 'old_wheel': [{
- 'state': 'wheel',
- '__id__': 'remove_key',
- 'name': 'remove_key',
- '__sls__': '/srv/reactor/old_wheel.sls',
- 'order': 1,
- 'fun': 'key.delete',
- 'match': 'foo',
- }],
- 'old_local': [{
- 'state': 'local',
- '__id__': 'install_zsh',
- 'name': 'install_zsh',
- '__sls__': '/srv/reactor/old_local.sls',
- 'order': 1,
- 'tgt': 'test',
- 'fun': 'state.single',
- 'arg': ['pkg.installed', 'zsh'],
- 'kwarg': {'fromrepo': 'updates'},
- }],
- 'old_cmd': [{
- 'state': 'local', # 'cmd' should be aliased to 'local'
- '__id__': 'install_zsh',
- 'name': 'install_zsh',
- '__sls__': '/srv/reactor/old_cmd.sls',
- 'order': 1,
- 'tgt': 'test',
- 'fun': 'state.single',
- 'arg': ['pkg.installed', 'zsh'],
- 'kwarg': {'fromrepo': 'updates'},
- }],
- 'old_caller': [{
- 'state': 'caller',
- '__id__': 'touch_file',
- 'name': 'touch_file',
- '__sls__': '/srv/reactor/old_caller.sls',
- 'order': 1,
- 'fun': 'file.touch',
- 'args': ['/tmp/foo'],
- }],
- 'new_runner': [{
- 'state': 'runner',
- '__id__': 'raise_error',
- 'name': 'raise_error',
- '__sls__': '/srv/reactor/new_runner.sls',
- 'order': 1,
- 'fun': 'error.error',
- 'args': [
- {'name': 'Exception'},
- {'message': 'This is an error'},
- ],
- }],
- 'new_wheel': [{
- 'state': 'wheel',
- '__id__': 'remove_key',
- 'name': 'remove_key',
- '__sls__': '/srv/reactor/new_wheel.sls',
- 'order': 1,
- 'fun': 'key.delete',
- 'args': [
- {'match': 'foo'},
- ],
- }],
- 'new_local': [{
- 'state': 'local',
- '__id__': 'install_zsh',
- 'name': 'install_zsh',
- '__sls__': '/srv/reactor/new_local.sls',
- 'order': 1,
- 'tgt': 'test',
- 'fun': 'state.single',
- 'args': [
- {'fun': 'pkg.installed'},
- {'name': 'zsh'},
- {'fromrepo': 'updates'},
- ],
- }],
- 'new_cmd': [{
- 'state': 'local',
- '__id__': 'install_zsh',
- 'name': 'install_zsh',
- '__sls__': '/srv/reactor/new_cmd.sls',
- 'order': 1,
- 'tgt': 'test',
- 'fun': 'state.single',
- 'args': [
- {'fun': 'pkg.installed'},
- {'name': 'zsh'},
- {'fromrepo': 'updates'},
- ],
- }],
- 'new_caller': [{
- 'state': 'caller',
- '__id__': 'touch_file',
- 'name': 'touch_file',
- '__sls__': '/srv/reactor/new_caller.sls',
- 'order': 1,
- 'fun': 'file.touch',
- 'args': [
- {'name': '/tmp/foo'},
- ],
- }],
- }
- WRAPPER_CALLS = {
- 'old_runner': (
- 'error.error',
- {
- '__state__': 'runner',
- '__id__': 'raise_error',
- '__sls__': '/srv/reactor/old_runner.sls',
- '__user__': 'Reactor',
- 'order': 1,
- 'arg': [],
- 'kwarg': {
- 'name': 'Exception',
- 'message': 'This is an error',
- },
- 'name': 'Exception',
- 'message': 'This is an error',
- },
- ),
- 'old_wheel': (
- 'key.delete',
- {
- '__state__': 'wheel',
- '__id__': 'remove_key',
- 'name': 'remove_key',
- '__sls__': '/srv/reactor/old_wheel.sls',
- 'order': 1,
- '__user__': 'Reactor',
- 'arg': ['foo'],
- 'kwarg': {},
- 'match': 'foo',
- },
- ),
- 'old_local': {
- 'args': ('test', 'state.single'),
- 'kwargs': {
- 'state': 'local',
- '__id__': 'install_zsh',
- 'name': 'install_zsh',
- '__sls__': '/srv/reactor/old_local.sls',
- 'order': 1,
- 'arg': ['pkg.installed', 'zsh'],
- 'kwarg': {'fromrepo': 'updates'},
- },
- },
- 'old_cmd': {
- 'args': ('test', 'state.single'),
- 'kwargs': {
- 'state': 'local',
- '__id__': 'install_zsh',
- 'name': 'install_zsh',
- '__sls__': '/srv/reactor/old_cmd.sls',
- 'order': 1,
- 'arg': ['pkg.installed', 'zsh'],
- 'kwarg': {'fromrepo': 'updates'},
- },
- },
- 'old_caller': {
- 'args': ('file.touch', '/tmp/foo'),
- 'kwargs': {},
- },
- 'new_runner': (
- 'error.error',
- {
- '__state__': 'runner',
- '__id__': 'raise_error',
- 'name': 'raise_error',
- '__sls__': '/srv/reactor/new_runner.sls',
- '__user__': 'Reactor',
- 'order': 1,
- 'arg': (),
- 'kwarg': {
- 'name': 'Exception',
- 'message': 'This is an error',
- },
- },
- ),
- 'new_wheel': (
- 'key.delete',
- {
- '__state__': 'wheel',
- '__id__': 'remove_key',
- 'name': 'remove_key',
- '__sls__': '/srv/reactor/new_wheel.sls',
- 'order': 1,
- '__user__': 'Reactor',
- 'arg': (),
- 'kwarg': {'match': 'foo'},
- },
- ),
- 'new_local': {
- 'args': ('test', 'state.single'),
- 'kwargs': {
- 'state': 'local',
- '__id__': 'install_zsh',
- 'name': 'install_zsh',
- '__sls__': '/srv/reactor/new_local.sls',
- 'order': 1,
- 'arg': (),
- 'kwarg': {
- 'fun': 'pkg.installed',
- 'name': 'zsh',
- 'fromrepo': 'updates',
- },
- },
- },
- 'new_cmd': {
- 'args': ('test', 'state.single'),
- 'kwargs': {
- 'state': 'local',
- '__id__': 'install_zsh',
- 'name': 'install_zsh',
- '__sls__': '/srv/reactor/new_cmd.sls',
- 'order': 1,
- 'arg': (),
- 'kwarg': {
- 'fun': 'pkg.installed',
- 'name': 'zsh',
- 'fromrepo': 'updates',
- },
- },
- },
- 'new_caller': {
- 'args': ('file.touch',),
- 'kwargs': {'name': '/tmp/foo'},
- },
- }
- log = logging.getLogger(__name__)
- class TestReactor(TestCase, AdaptedConfigurationTestCaseMixin):
- '''
- Tests for constructing the low chunks to be executed via the Reactor
- '''
- @classmethod
- def setUpClass(cls):
- '''
- Load the reactor config for mocking
- '''
- cls.opts = cls.get_temp_config('master')
- reactor_config = salt.utils.yaml.safe_load(REACTOR_CONFIG)
- cls.opts.update(reactor_config)
- cls.reactor = reactor.Reactor(cls.opts)
- cls.reaction_map = salt.utils.data.repack_dictlist(reactor_config['reactor'])
- renderers = salt.loader.render(cls.opts, {})
- cls.render_pipe = [(renderers[x], '') for x in ('jinja', 'yaml')]
- @classmethod
- def tearDownClass(cls):
- del cls.opts
- del cls.reactor
- del cls.render_pipe
- def test_list_reactors(self):
- '''
- Ensure that list_reactors() returns the correct list of reactor SLS
- files for each tag.
- '''
- for schema in ('old', 'new'):
- for rtype in REACTOR_DATA:
- tag = '_'.join((schema, rtype))
- self.assertEqual(
- self.reactor.list_reactors(tag),
- self.reaction_map[tag]
- )
- def test_reactions(self):
- '''
- Ensure that the correct reactions are built from the configured SLS
- files and tag data.
- '''
- for schema in ('old', 'new'):
- for rtype in REACTOR_DATA:
- tag = '_'.join((schema, rtype))
- log.debug('test_reactions: processing %s', tag)
- reactors = self.reactor.list_reactors(tag)
- log.debug('test_reactions: %s reactors: %s', tag, reactors)
- # No globbing in our example SLS, and the files don't actually
- # exist, so mock glob.glob to just return back the path passed
- # to it.
- with patch.object(
- glob,
- 'glob',
- MagicMock(side_effect=lambda x: [x])):
- # The below four mocks are all so that
- # salt.template.compile_template() will read the templates
- # we've mocked up in the SLS global variable above.
- with patch.object(
- os.path, 'isfile',
- MagicMock(return_value=True)):
- with patch.object(
- salt.utils.files, 'is_empty',
- MagicMock(return_value=False)):
- with patch.object(
- codecs, 'open',
- mock_open(read_data=SLS[reactors[0]])):
- with patch.object(
- salt.template, 'template_shebang',
- MagicMock(return_value=self.render_pipe)):
- reactions = self.reactor.reactions(
- tag,
- REACTOR_DATA[rtype],
- reactors,
- )
- log.debug(
- 'test_reactions: %s reactions: %s',
- tag, reactions
- )
- self.assertEqual(reactions, LOW_CHUNKS[tag])
- class TestReactWrap(TestCase, AdaptedConfigurationTestCaseMixin):
- '''
- Tests that we are formulating the wrapper calls properly
- '''
- @classmethod
- def setUpClass(cls):
- cls.wrap = reactor.ReactWrap(cls.get_temp_config('master'))
- @classmethod
- def tearDownClass(cls):
- del cls.wrap
- def test_runner(self):
- '''
- Test runner reactions using both the old and new config schema
- '''
- for schema in ('old', 'new'):
- tag = '_'.join((schema, 'runner'))
- chunk = LOW_CHUNKS[tag][0]
- thread_pool = Mock()
- thread_pool.fire_async = Mock()
- with patch.object(self.wrap, 'pool', thread_pool):
- self.wrap.run(chunk)
- thread_pool.fire_async.assert_called_with(
- self.wrap.client_cache['runner'].low,
- args=WRAPPER_CALLS[tag]
- )
- def test_wheel(self):
- '''
- Test wheel reactions using both the old and new config schema
- '''
- for schema in ('old', 'new'):
- tag = '_'.join((schema, 'wheel'))
- chunk = LOW_CHUNKS[tag][0]
- thread_pool = Mock()
- thread_pool.fire_async = Mock()
- with patch.object(self.wrap, 'pool', thread_pool):
- self.wrap.run(chunk)
- thread_pool.fire_async.assert_called_with(
- self.wrap.client_cache['wheel'].low,
- args=WRAPPER_CALLS[tag]
- )
- def test_local(self):
- '''
- Test local reactions using both the old and new config schema
- '''
- for schema in ('old', 'new'):
- tag = '_'.join((schema, 'local'))
- chunk = LOW_CHUNKS[tag][0]
- client_cache = {'local': Mock()}
- client_cache['local'].cmd_async = Mock()
- with patch.object(self.wrap, 'client_cache', client_cache):
- self.wrap.run(chunk)
- client_cache['local'].cmd_async.assert_called_with(
- *WRAPPER_CALLS[tag]['args'],
- **WRAPPER_CALLS[tag]['kwargs']
- )
- def test_cmd(self):
- '''
- Test cmd reactions (alias for 'local') using both the old and new
- config schema
- '''
- for schema in ('old', 'new'):
- tag = '_'.join((schema, 'cmd'))
- chunk = LOW_CHUNKS[tag][0]
- client_cache = {'local': Mock()}
- client_cache['local'].cmd_async = Mock()
- with patch.object(self.wrap, 'client_cache', client_cache):
- self.wrap.run(chunk)
- client_cache['local'].cmd_async.assert_called_with(
- *WRAPPER_CALLS[tag]['args'],
- **WRAPPER_CALLS[tag]['kwargs']
- )
- def test_caller(self):
- '''
- Test caller reactions using both the old and new config schema
- '''
- for schema in ('old', 'new'):
- tag = '_'.join((schema, 'caller'))
- chunk = LOW_CHUNKS[tag][0]
- client_cache = {'caller': Mock()}
- client_cache['caller'].cmd = Mock()
- with patch.object(self.wrap, 'client_cache', client_cache):
- self.wrap.run(chunk)
- client_cache['caller'].cmd.assert_called_with(
- *WRAPPER_CALLS[tag]['args'],
- **WRAPPER_CALLS[tag]['kwargs']
- )
|