123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- # -*- coding: utf-8 -*-
- """
- :codeauthor: Mike Place <mp@saltstack.com>
- """
- from __future__ import absolute_import, print_function, unicode_literals
- import salt.utils.platform
- from salt import client
- from salt.exceptions import (
- EauthAuthenticationError,
- SaltClientError,
- SaltInvocationError,
- SaltReqTimeoutError,
- )
- from salt.ext.tornado.concurrent import Future
- from tests.support.helpers import slowTest
- from tests.support.mixins import SaltClientTestCaseMixin
- from tests.support.mock import MagicMock, patch
- from tests.support.unit import TestCase, skipIf
- class LocalClientTestCase(TestCase, SaltClientTestCaseMixin):
- def test_job_result_return_success(self):
- """
- Should return the `expected_return`, since there is a job with the right jid.
- """
- minions = ()
- jid = "0815"
- raw_return = {"id": "fake-id", "jid": jid, "data": "", "return": "fake-return"}
- expected_return = {"fake-id": {"ret": "fake-return"}}
- local_client = client.LocalClient(mopts=self.get_temp_config("master"))
- local_client.event.get_event = MagicMock(return_value=raw_return)
- local_client.returners = MagicMock()
- ret = local_client.get_event_iter_returns(jid, minions)
- val = next(ret)
- self.assertEqual(val, expected_return)
- def test_job_result_return_failure(self):
- """
- We are _not_ getting a job return, because the jid is different. Instead we should
- get a StopIteration exception.
- """
- minions = ()
- jid = "0815"
- raw_return = {
- "id": "fake-id",
- "jid": "0816",
- "data": "",
- "return": "fake-return",
- }
- local_client = client.LocalClient(mopts=self.get_temp_config("master"))
- local_client.event.get_event = MagicMock()
- local_client.event.get_event.side_effect = [raw_return, None]
- local_client.returners = MagicMock()
- ret = local_client.get_event_iter_returns(jid, minions)
- with self.assertRaises(StopIteration):
- next(ret)
- def test_create_local_client(self):
- local_client = client.LocalClient(mopts=self.get_temp_config("master"))
- self.assertIsInstance(
- local_client,
- client.LocalClient,
- "LocalClient did not create a LocalClient instance",
- )
- def test_check_pub_data(self):
- just_minions = {"minions": ["m1", "m2"]}
- jid_no_minions = {"jid": "1234", "minions": []}
- valid_pub_data = {"minions": ["m1", "m2"], "jid": "1234"}
- self.assertRaises(EauthAuthenticationError, self.client._check_pub_data, "")
- self.assertDictEqual(
- {},
- self.client._check_pub_data(just_minions),
- "Did not handle lack of jid correctly",
- )
- self.assertDictEqual(
- {},
- self.client._check_pub_data({"jid": "0"}),
- "Passing JID of zero is not handled gracefully",
- )
- with patch.dict(self.client.opts, {}):
- self.client._check_pub_data(jid_no_minions)
- self.assertDictEqual(
- valid_pub_data, self.client._check_pub_data(valid_pub_data)
- )
- def test_cmd_subset(self):
- with patch(
- "salt.client.LocalClient.cmd",
- return_value={
- "minion1": ["first.func", "second.func"],
- "minion2": ["first.func", "second.func"],
- },
- ):
- with patch("salt.client.LocalClient.cmd_cli") as cmd_cli_mock:
- 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",
- full_return=False,
- ret="",
- )
- except AssertionError:
- cmd_cli_mock.assert_called_with(
- ["minion1"],
- "first.func",
- (),
- progress=False,
- 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",
- full_return=False,
- ret="",
- )
- except AssertionError:
- cmd_cli_mock.assert_called_with(
- ["minion1", "minion2"],
- "first.func",
- (),
- progress=False,
- 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")
- def test_pub(self):
- """
- Tests that the client cleanly returns when the publisher is not running
- Note: Requires ZeroMQ's IPC transport which is not supported on windows.
- """
- if self.get_config("minion")["transport"] != "zeromq":
- self.skipTest("This test only works with ZeroMQ")
- # Make sure we cleanly return if the publisher isn't running
- with patch("os.path.exists", return_value=False):
- self.assertRaises(
- SaltClientError, lambda: self.client.pub("*", "test.ping")
- )
- # Check nodegroups behavior
- with patch("os.path.exists", return_value=True):
- with patch.dict(
- self.client.opts,
- {
- "nodegroups": {
- "group1": "L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com"
- }
- },
- ):
- # Do we raise an exception if the nodegroup can't be matched?
- self.assertRaises(
- SaltInvocationError,
- self.client.pub,
- "non_existent_group",
- "test.ping",
- tgt_type="nodegroup",
- )
- @skipIf(not salt.utils.platform.is_windows(), "Windows only test")
- @slowTest
- def test_pub_win32(self):
- """
- Tests that the client raises a timeout error when using ZeroMQ's TCP
- transport and publisher is not running.
- Note: Requires ZeroMQ's TCP transport, this is only the default on Windows.
- """
- if self.get_config("minion")["transport"] != "zeromq":
- self.skipTest("This test only works with ZeroMQ")
- # Make sure we cleanly return if the publisher isn't running
- with patch("os.path.exists", return_value=False):
- self.assertRaises(
- SaltReqTimeoutError, lambda: self.client.pub("*", "test.ping")
- )
- # Check nodegroups behavior
- with patch("os.path.exists", return_value=True):
- with patch.dict(
- self.client.opts,
- {
- "nodegroups": {
- "group1": "L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com"
- }
- },
- ):
- # Do we raise an exception if the nodegroup can't be matched?
- self.assertRaises(
- SaltInvocationError,
- self.client.pub,
- "non_existent_group",
- "test.ping",
- tgt_type="nodegroup",
- )
- # all of these parse_input test wrapper tests can be replaced by
- # parameterize if/when we switch to pytest runner
- # @pytest.mark.parametrize('method', [('run_job', 'cmd', ...)])
- def _test_parse_input(self, method, asynchronous=False):
- if asynchronous:
- target = "salt.client.LocalClient.pub_async"
- pub_ret = Future()
- pub_ret.set_result({"jid": "123456789", "minions": ["m1"]})
- else:
- target = "salt.client.LocalClient.pub"
- pub_ret = {"jid": "123456789", "minions": ["m1"]}
- with patch(target, return_value=pub_ret) as pub_mock:
- with patch(
- "salt.client.LocalClient.get_cli_event_returns",
- return_value=[{"m1": {"ret": ["test.arg"]}}],
- ):
- with patch(
- "salt.client.LocalClient.get_iter_returns",
- return_value=[{"m1": {"ret": True}}],
- ):
- ret = getattr(self.client, method)(
- "*",
- "test.arg",
- arg=[
- "a",
- 5,
- "yaml_arg={qux: Qux}",
- "another_yaml={bax: 12345}",
- ],
- jid="123456789",
- )
- # iterate generator if needed
- if asynchronous:
- pass
- else:
- ret = list(ret)
- # main test here is that yaml_arg is getting deserialized properly
- parsed_args = [
- "a",
- 5,
- {
- "yaml_arg": {"qux": "Qux"},
- "another_yaml": {"bax": 12345},
- "__kwarg__": True,
- },
- ]
- self.assertTrue(
- any(parsed_args in call[0] for call in pub_mock.call_args_list)
- )
- def test_parse_input_is_called(self):
- self._test_parse_input("run_job")
- self._test_parse_input("cmd")
- self._test_parse_input("cmd_subset")
- self._test_parse_input("cmd_batch")
- self._test_parse_input("cmd_cli")
- self._test_parse_input("cmd_full_return")
- self._test_parse_input("cmd_iter")
- self._test_parse_input("cmd_iter_no_block")
- self._test_parse_input("cmd_async")
- self._test_parse_input("run_job_async", asynchronous=True)
|