"Fossies" - the Fresh Open Source Software Archive

Member "salt-3002.2/tests/unit/test_minion.py" (18 Nov 2020, 29876 Bytes) of package /linux/misc/salt-3002.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_minion.py": 3002.1_vs_3002.2.

    1 """
    2     :codeauthor: Mike Place <mp@saltstack.com>
    3 """
    4 
    5 import copy
    6 import logging
    7 import os
    8 
    9 import salt.ext.tornado
   10 import salt.ext.tornado.testing
   11 import salt.minion
   12 import salt.syspaths
   13 import salt.utils.crypt
   14 import salt.utils.event as event
   15 import salt.utils.platform
   16 import salt.utils.process
   17 from salt._compat import ipaddress
   18 from salt.exceptions import SaltClientError, SaltMasterUnresolvableError, SaltSystemExit
   19 from salt.ext.six.moves import range
   20 from tests.support.helpers import skip_if_not_root, slowTest
   21 from tests.support.mixins import AdaptedConfigurationTestCaseMixin
   22 from tests.support.mock import MagicMock, patch
   23 from tests.support.unit import TestCase, skipIf
   24 
   25 log = logging.getLogger(__name__)
   26 
   27 
   28 class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
   29     def setUp(self):
   30         self.opts = {}
   31         self.addCleanup(delattr, self, "opts")
   32 
   33     def test_invalid_master_address(self):
   34         with patch.dict(
   35             self.opts,
   36             {
   37                 "ipv6": False,
   38                 "master": float("127.0"),
   39                 "master_port": "4555",
   40                 "retry_dns": False,
   41             },
   42         ):
   43             self.assertRaises(SaltSystemExit, salt.minion.resolve_dns, self.opts)
   44 
   45     def test_source_int_name_local(self):
   46         """
   47         test when file_client local and
   48         source_interface_name is set
   49         """
   50         interfaces = {
   51             "bond0.1234": {
   52                 "hwaddr": "01:01:01:d0:d0:d0",
   53                 "up": True,
   54                 "inet": [
   55                     {
   56                         "broadcast": "111.1.111.255",
   57                         "netmask": "111.1.0.0",
   58                         "label": "bond0",
   59                         "address": "111.1.0.1",
   60                     }
   61                 ],
   62             }
   63         }
   64         with patch.dict(
   65             self.opts,
   66             {
   67                 "ipv6": False,
   68                 "master": "127.0.0.1",
   69                 "master_port": "4555",
   70                 "file_client": "local",
   71                 "source_interface_name": "bond0.1234",
   72                 "source_ret_port": 49017,
   73                 "source_publish_port": 49018,
   74             },
   75         ), patch("salt.utils.network.interfaces", MagicMock(return_value=interfaces)):
   76             assert salt.minion.resolve_dns(self.opts) == {
   77                 "master_ip": "127.0.0.1",
   78                 "source_ip": "111.1.0.1",
   79                 "source_ret_port": 49017,
   80                 "source_publish_port": 49018,
   81                 "master_uri": "tcp://127.0.0.1:4555",
   82             }
   83 
   84     @slowTest
   85     def test_source_int_name_remote(self):
   86         """
   87         test when file_client remote and
   88         source_interface_name is set and
   89         interface is down
   90         """
   91         interfaces = {
   92             "bond0.1234": {
   93                 "hwaddr": "01:01:01:d0:d0:d0",
   94                 "up": False,
   95                 "inet": [
   96                     {
   97                         "broadcast": "111.1.111.255",
   98                         "netmask": "111.1.0.0",
   99                         "label": "bond0",
  100                         "address": "111.1.0.1",
  101                     }
  102                 ],
  103             }
  104         }
  105         with patch.dict(
  106             self.opts,
  107             {
  108                 "ipv6": False,
  109                 "master": "127.0.0.1",
  110                 "master_port": "4555",
  111                 "file_client": "remote",
  112                 "source_interface_name": "bond0.1234",
  113                 "source_ret_port": 49017,
  114                 "source_publish_port": 49018,
  115             },
  116         ), patch("salt.utils.network.interfaces", MagicMock(return_value=interfaces)):
  117             assert salt.minion.resolve_dns(self.opts) == {
  118                 "master_ip": "127.0.0.1",
  119                 "source_ret_port": 49017,
  120                 "source_publish_port": 49018,
  121                 "master_uri": "tcp://127.0.0.1:4555",
  122             }
  123 
  124     @slowTest
  125     def test_source_address(self):
  126         """
  127         test when source_address is set
  128         """
  129         interfaces = {
  130             "bond0.1234": {
  131                 "hwaddr": "01:01:01:d0:d0:d0",
  132                 "up": False,
  133                 "inet": [
  134                     {
  135                         "broadcast": "111.1.111.255",
  136                         "netmask": "111.1.0.0",
  137                         "label": "bond0",
  138                         "address": "111.1.0.1",
  139                     }
  140                 ],
  141             }
  142         }
  143         with patch.dict(
  144             self.opts,
  145             {
  146                 "ipv6": False,
  147                 "master": "127.0.0.1",
  148                 "master_port": "4555",
  149                 "file_client": "local",
  150                 "source_interface_name": "",
  151                 "source_address": "111.1.0.1",
  152                 "source_ret_port": 49017,
  153                 "source_publish_port": 49018,
  154             },
  155         ), patch("salt.utils.network.interfaces", MagicMock(return_value=interfaces)):
  156             assert salt.minion.resolve_dns(self.opts) == {
  157                 "source_publish_port": 49018,
  158                 "source_ret_port": 49017,
  159                 "master_uri": "tcp://127.0.0.1:4555",
  160                 "source_ip": "111.1.0.1",
  161                 "master_ip": "127.0.0.1",
  162             }
  163 
  164     # Tests for _handle_decoded_payload in the salt.minion.Minion() class: 3
  165 
  166     @slowTest
  167     def test_handle_decoded_payload_jid_match_in_jid_queue(self):
  168         """
  169         Tests that the _handle_decoded_payload function returns when a jid is given that is already present
  170         in the jid_queue.
  171 
  172         Note: This test doesn't contain all of the patch decorators above the function like the other tests
  173         for _handle_decoded_payload below. This is essential to this test as the call to the function must
  174         return None BEFORE any of the processes are spun up because we should be avoiding firing duplicate
  175         jobs.
  176         """
  177         mock_opts = salt.config.DEFAULT_MINION_OPTS.copy()
  178         mock_data = {"fun": "foo.bar", "jid": 123}
  179         mock_jid_queue = [123]
  180         minion = salt.minion.Minion(
  181             mock_opts,
  182             jid_queue=copy.copy(mock_jid_queue),
  183             io_loop=salt.ext.tornado.ioloop.IOLoop(),
  184         )
  185         try:
  186             ret = minion._handle_decoded_payload(mock_data).result()
  187             self.assertEqual(minion.jid_queue, mock_jid_queue)
  188             self.assertIsNone(ret)
  189         finally:
  190             minion.destroy()
  191 
  192     @slowTest
  193     def test_handle_decoded_payload_jid_queue_addition(self):
  194         """
  195         Tests that the _handle_decoded_payload function adds a jid to the minion's jid_queue when the new
  196         jid isn't already present in the jid_queue.
  197         """
  198         with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch(
  199             "salt.utils.process.SignalHandlingProcess.start",
  200             MagicMock(return_value=True),
  201         ), patch(
  202             "salt.utils.process.SignalHandlingProcess.join",
  203             MagicMock(return_value=True),
  204         ):
  205             mock_jid = 11111
  206             mock_opts = salt.config.DEFAULT_MINION_OPTS.copy()
  207             mock_data = {"fun": "foo.bar", "jid": mock_jid}
  208             mock_jid_queue = [123, 456]
  209             minion = salt.minion.Minion(
  210                 mock_opts,
  211                 jid_queue=copy.copy(mock_jid_queue),
  212                 io_loop=salt.ext.tornado.ioloop.IOLoop(),
  213             )
  214             try:
  215 
  216                 # Assert that the minion's jid_queue attribute matches the mock_jid_queue as a baseline
  217                 # This can help debug any test failures if the _handle_decoded_payload call fails.
  218                 self.assertEqual(minion.jid_queue, mock_jid_queue)
  219 
  220                 # Call the _handle_decoded_payload function and update the mock_jid_queue to include the new
  221                 # mock_jid. The mock_jid should have been added to the jid_queue since the mock_jid wasn't
  222                 # previously included. The minion's jid_queue attribute and the mock_jid_queue should be equal.
  223                 minion._handle_decoded_payload(mock_data).result()
  224                 mock_jid_queue.append(mock_jid)
  225                 self.assertEqual(minion.jid_queue, mock_jid_queue)
  226             finally:
  227                 minion.destroy()
  228 
  229     @slowTest
  230     def test_handle_decoded_payload_jid_queue_reduced_minion_jid_queue_hwm(self):
  231         """
  232         Tests that the _handle_decoded_payload function removes a jid from the minion's jid_queue when the
  233         minion's jid_queue high water mark (minion_jid_queue_hwm) is hit.
  234         """
  235         with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch(
  236             "salt.utils.process.SignalHandlingProcess.start",
  237             MagicMock(return_value=True),
  238         ), patch(
  239             "salt.utils.process.SignalHandlingProcess.join",
  240             MagicMock(return_value=True),
  241         ):
  242             mock_opts = salt.config.DEFAULT_MINION_OPTS.copy()
  243             mock_opts["minion_jid_queue_hwm"] = 2
  244             mock_data = {"fun": "foo.bar", "jid": 789}
  245             mock_jid_queue = [123, 456]
  246             minion = salt.minion.Minion(
  247                 mock_opts,
  248                 jid_queue=copy.copy(mock_jid_queue),
  249                 io_loop=salt.ext.tornado.ioloop.IOLoop(),
  250             )
  251             try:
  252 
  253                 # Assert that the minion's jid_queue attribute matches the mock_jid_queue as a baseline
  254                 # This can help debug any test failures if the _handle_decoded_payload call fails.
  255                 self.assertEqual(minion.jid_queue, mock_jid_queue)
  256 
  257                 # Call the _handle_decoded_payload function and check that the queue is smaller by one item
  258                 # and contains the new jid
  259                 minion._handle_decoded_payload(mock_data).result()
  260                 self.assertEqual(len(minion.jid_queue), 2)
  261                 self.assertEqual(minion.jid_queue, [456, 789])
  262             finally:
  263                 minion.destroy()
  264 
  265     @slowTest
  266     def test_process_count_max(self):
  267         """
  268         Tests that the _handle_decoded_payload function does not spawn more than the configured amount of processes,
  269         as per process_count_max.
  270         """
  271         with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch(
  272             "salt.utils.process.SignalHandlingProcess.start",
  273             MagicMock(return_value=True),
  274         ), patch(
  275             "salt.utils.process.SignalHandlingProcess.join",
  276             MagicMock(return_value=True),
  277         ), patch(
  278             "salt.utils.minion.running", MagicMock(return_value=[])
  279         ), patch(
  280             "salt.ext.tornado.gen.sleep",
  281             MagicMock(return_value=salt.ext.tornado.concurrent.Future()),
  282         ):
  283             process_count_max = 10
  284             mock_opts = salt.config.DEFAULT_MINION_OPTS.copy()
  285             mock_opts["__role"] = "minion"
  286             mock_opts["minion_jid_queue_hwm"] = 100
  287             mock_opts["process_count_max"] = process_count_max
  288 
  289             io_loop = salt.ext.tornado.ioloop.IOLoop()
  290             minion = salt.minion.Minion(mock_opts, jid_queue=[], io_loop=io_loop)
  291             try:
  292 
  293                 # mock gen.sleep to throw a special Exception when called, so that we detect it
  294                 class SleepCalledException(Exception):
  295                     """Thrown when sleep is called"""
  296 
  297                 salt.ext.tornado.gen.sleep.return_value.set_exception(
  298                     SleepCalledException()
  299                 )
  300 
  301                 # up until process_count_max: gen.sleep does not get called, processes are started normally
  302                 for i in range(process_count_max):
  303                     mock_data = {"fun": "foo.bar", "jid": i}
  304                     io_loop.run_sync(
  305                         lambda data=mock_data: minion._handle_decoded_payload(data)
  306                     )
  307                     self.assertEqual(
  308                         salt.utils.process.SignalHandlingProcess.start.call_count, i + 1
  309                     )
  310                     self.assertEqual(len(minion.jid_queue), i + 1)
  311                     salt.utils.minion.running.return_value += [i]
  312 
  313                 # above process_count_max: gen.sleep does get called, JIDs are created but no new processes are started
  314                 mock_data = {"fun": "foo.bar", "jid": process_count_max + 1}
  315 
  316                 self.assertRaises(
  317                     SleepCalledException,
  318                     lambda: io_loop.run_sync(
  319                         lambda: minion._handle_decoded_payload(mock_data)
  320                     ),
  321                 )
  322                 self.assertEqual(
  323                     salt.utils.process.SignalHandlingProcess.start.call_count,
  324                     process_count_max,
  325                 )
  326                 self.assertEqual(len(minion.jid_queue), process_count_max + 1)
  327             finally:
  328                 minion.destroy()
  329 
  330     @slowTest
  331     def test_beacons_before_connect(self):
  332         """
  333         Tests that the 'beacons_before_connect' option causes the beacons to be initialized before connect.
  334         """
  335         with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch(
  336             "salt.minion.Minion.sync_connect_master",
  337             MagicMock(side_effect=RuntimeError("stop execution")),
  338         ), patch(
  339             "salt.utils.process.SignalHandlingProcess.start",
  340             MagicMock(return_value=True),
  341         ), patch(
  342             "salt.utils.process.SignalHandlingProcess.join",
  343             MagicMock(return_value=True),
  344         ):
  345             mock_opts = self.get_config("minion", from_scratch=True)
  346             mock_opts["beacons_before_connect"] = True
  347             io_loop = salt.ext.tornado.ioloop.IOLoop()
  348             io_loop.make_current()
  349             minion = salt.minion.Minion(mock_opts, io_loop=io_loop)
  350             try:
  351 
  352                 try:
  353                     minion.tune_in(start=True)
  354                 except RuntimeError:
  355                     pass
  356 
  357                 # Make sure beacons are initialized but the sheduler is not
  358                 self.assertTrue("beacons" in minion.periodic_callbacks)
  359                 self.assertTrue("schedule" not in minion.periodic_callbacks)
  360             finally:
  361                 minion.destroy()
  362 
  363     @slowTest
  364     def test_scheduler_before_connect(self):
  365         """
  366         Tests that the 'scheduler_before_connect' option causes the scheduler to be initialized before connect.
  367         """
  368         with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch(
  369             "salt.minion.Minion.sync_connect_master",
  370             MagicMock(side_effect=RuntimeError("stop execution")),
  371         ), patch(
  372             "salt.utils.process.SignalHandlingProcess.start",
  373             MagicMock(return_value=True),
  374         ), patch(
  375             "salt.utils.process.SignalHandlingProcess.join",
  376             MagicMock(return_value=True),
  377         ):
  378             mock_opts = self.get_config("minion", from_scratch=True)
  379             mock_opts["scheduler_before_connect"] = True
  380             io_loop = salt.ext.tornado.ioloop.IOLoop()
  381             io_loop.make_current()
  382             minion = salt.minion.Minion(mock_opts, io_loop=io_loop)
  383             try:
  384                 try:
  385                     minion.tune_in(start=True)
  386                 except RuntimeError:
  387                     pass
  388 
  389                 # Make sure the scheduler is initialized but the beacons are not
  390                 self.assertTrue("schedule" in minion.periodic_callbacks)
  391                 self.assertTrue("beacons" not in minion.periodic_callbacks)
  392             finally:
  393                 minion.destroy()
  394 
  395     @slowTest
  396     def test_when_ping_interval_is_set_the_callback_should_be_added_to_periodic_callbacks(
  397         self,
  398     ):
  399         with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch(
  400             "salt.minion.Minion.sync_connect_master",
  401             MagicMock(side_effect=RuntimeError("stop execution")),
  402         ), patch(
  403             "salt.utils.process.SignalHandlingProcess.start",
  404             MagicMock(return_value=True),
  405         ), patch(
  406             "salt.utils.process.SignalHandlingProcess.join",
  407             MagicMock(return_value=True),
  408         ):
  409             mock_opts = self.get_config("minion", from_scratch=True)
  410             mock_opts["ping_interval"] = 10
  411             io_loop = salt.ext.tornado.ioloop.IOLoop()
  412             io_loop.make_current()
  413             minion = salt.minion.Minion(mock_opts, io_loop=io_loop)
  414             try:
  415                 try:
  416                     minion.connected = MagicMock(side_effect=(False, True))
  417                     minion._fire_master_minion_start = MagicMock()
  418                     minion.tune_in(start=False)
  419                 except RuntimeError:
  420                     pass
  421 
  422                 # Make sure the scheduler is initialized but the beacons are not
  423                 self.assertTrue("ping" in minion.periodic_callbacks)
  424             finally:
  425                 minion.destroy()
  426 
  427     @slowTest
  428     def test_when_passed_start_event_grains(self):
  429         mock_opts = self.get_config("minion", from_scratch=True)
  430         # provide mock opts an os grain since we'll look for it later.
  431         mock_opts["grains"]["os"] = "linux"
  432         mock_opts["start_event_grains"] = ["os"]
  433         io_loop = salt.ext.tornado.ioloop.IOLoop()
  434         io_loop.make_current()
  435         minion = salt.minion.Minion(mock_opts, io_loop=io_loop)
  436         try:
  437             minion.tok = MagicMock()
  438             minion._send_req_sync = MagicMock()
  439             minion._fire_master(
  440                 "Minion has started", "minion_start", include_startup_grains=True
  441             )
  442             load = minion._send_req_sync.call_args[0][0]
  443 
  444             self.assertTrue("grains" in load)
  445             self.assertTrue("os" in load["grains"])
  446         finally:
  447             minion.destroy()
  448 
  449     @slowTest
  450     def test_when_not_passed_start_event_grains(self):
  451         mock_opts = self.get_config("minion", from_scratch=True)
  452         io_loop = salt.ext.tornado.ioloop.IOLoop()
  453         io_loop.make_current()
  454         minion = salt.minion.Minion(mock_opts, io_loop=io_loop)
  455         try:
  456             minion.tok = MagicMock()
  457             minion._send_req_sync = MagicMock()
  458             minion._fire_master("Minion has started", "minion_start")
  459             load = minion._send_req_sync.call_args[0][0]
  460 
  461             self.assertTrue("grains" not in load)
  462         finally:
  463             minion.destroy()
  464 
  465     @slowTest
  466     def test_when_other_events_fired_and_start_event_grains_are_set(self):
  467         mock_opts = self.get_config("minion", from_scratch=True)
  468         mock_opts["start_event_grains"] = ["os"]
  469         io_loop = salt.ext.tornado.ioloop.IOLoop()
  470         io_loop.make_current()
  471         minion = salt.minion.Minion(mock_opts, io_loop=io_loop)
  472         try:
  473             minion.tok = MagicMock()
  474             minion._send_req_sync = MagicMock()
  475             minion._fire_master("Custm_event_fired", "custom_event")
  476             load = minion._send_req_sync.call_args[0][0]
  477 
  478             self.assertTrue("grains" not in load)
  479         finally:
  480             minion.destroy()
  481 
  482     @slowTest
  483     def test_minion_retry_dns_count(self):
  484         """
  485         Tests that the resolve_dns will retry dns look ups for a maximum of
  486         3 times before raising a SaltMasterUnresolvableError exception.
  487         """
  488         with patch.dict(
  489             self.opts,
  490             {
  491                 "ipv6": False,
  492                 "master": "dummy",
  493                 "master_port": "4555",
  494                 "retry_dns": 1,
  495                 "retry_dns_count": 3,
  496             },
  497         ):
  498             self.assertRaises(
  499                 SaltMasterUnresolvableError, salt.minion.resolve_dns, self.opts
  500             )
  501 
  502     @slowTest
  503     def test_gen_modules_executors(self):
  504         """
  505         Ensure gen_modules is called with the correct arguments #54429
  506         """
  507         mock_opts = self.get_config("minion", from_scratch=True)
  508         io_loop = salt.ext.tornado.ioloop.IOLoop()
  509         io_loop.make_current()
  510         minion = salt.minion.Minion(mock_opts, io_loop=io_loop)
  511 
  512         class MockPillarCompiler:
  513             def compile_pillar(self):
  514                 return {}
  515 
  516         try:
  517             with patch("salt.pillar.get_pillar", return_value=MockPillarCompiler()):
  518                 with patch("salt.loader.executors") as execmock:
  519                     minion.gen_modules()
  520             assert execmock.called_with(minion.opts, minion.functions)
  521         finally:
  522             minion.destroy()
  523 
  524     @patch("salt.utils.process.default_signals")
  525     @slowTest
  526     def test_reinit_crypto_on_fork(self, def_mock):
  527         """
  528         Ensure salt.utils.crypt.reinit_crypto() is executed when forking for new job
  529         """
  530         mock_opts = self.get_config("minion", from_scratch=True)
  531         mock_opts["multiprocessing"] = True
  532 
  533         io_loop = salt.ext.tornado.ioloop.IOLoop()
  534         io_loop.make_current()
  535         minion = salt.minion.Minion(mock_opts, io_loop=io_loop)
  536 
  537         job_data = {"jid": "test-jid", "fun": "test.ping"}
  538 
  539         def mock_start(self):
  540             # pylint: disable=comparison-with-callable
  541             assert (
  542                 len(
  543                     [
  544                         x
  545                         for x in self._after_fork_methods
  546                         if x[0] == salt.utils.crypt.reinit_crypto
  547                     ]
  548                 )
  549                 == 1
  550             )
  551             # pylint: enable=comparison-with-callable
  552 
  553         with patch.object(
  554             salt.utils.process.SignalHandlingProcess, "start", mock_start
  555         ):
  556             io_loop.run_sync(lambda: minion._handle_decoded_payload(job_data))
  557 
  558     def test_minion_manage_schedule(self):
  559         """
  560         Tests that the manage_schedule will call the add function, adding
  561         schedule data into opts.
  562         """
  563         with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch(
  564             "salt.minion.Minion.sync_connect_master",
  565             MagicMock(side_effect=RuntimeError("stop execution")),
  566         ), patch(
  567             "salt.utils.process.SignalHandlingMultiprocessingProcess.start",
  568             MagicMock(return_value=True),
  569         ), patch(
  570             "salt.utils.process.SignalHandlingMultiprocessingProcess.join",
  571             MagicMock(return_value=True),
  572         ):
  573             mock_opts = self.get_config("minion", from_scratch=True)
  574             io_loop = salt.ext.tornado.ioloop.IOLoop()
  575             io_loop.make_current()
  576 
  577             with patch(
  578                 "salt.utils.schedule.clean_proc_dir", MagicMock(return_value=None)
  579             ):
  580                 mock_functions = {"test.ping": None}
  581 
  582                 minion = salt.minion.Minion(mock_opts, io_loop=io_loop)
  583                 minion.schedule = salt.utils.schedule.Schedule(
  584                     mock_opts, mock_functions, returners={}
  585                 )
  586 
  587                 schedule_data = {
  588                     "test_job": {
  589                         "function": "test.ping",
  590                         "return_job": False,
  591                         "jid_include": True,
  592                         "maxrunning": 2,
  593                         "seconds": 10,
  594                     }
  595                 }
  596 
  597                 data = {"name": "test-item", "schedule": schedule_data, "func": "add"}
  598                 tag = "manage_schedule"
  599 
  600                 minion.manage_schedule(tag, data)
  601                 self.assertIn("test_job", minion.opts["schedule"])
  602 
  603     def test_minion_manage_beacons(self):
  604         """
  605         Tests that the manage_beacons will call the add function, adding
  606         beacon data into opts.
  607         """
  608         with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch(
  609             "salt.minion.Minion.sync_connect_master",
  610             MagicMock(side_effect=RuntimeError("stop execution")),
  611         ), patch(
  612             "salt.utils.process.SignalHandlingMultiprocessingProcess.start",
  613             MagicMock(return_value=True),
  614         ), patch(
  615             "salt.utils.process.SignalHandlingMultiprocessingProcess.join",
  616             MagicMock(return_value=True),
  617         ):
  618             mock_opts = self.get_config("minion", from_scratch=True)
  619             io_loop = salt.ext.tornado.ioloop.IOLoop()
  620             io_loop.make_current()
  621 
  622             mock_functions = {"test.ping": None}
  623             minion = salt.minion.Minion(mock_opts, io_loop=io_loop)
  624             minion.beacons = salt.beacons.Beacon(mock_opts, mock_functions)
  625 
  626             bdata = [{"salt-master": "stopped"}, {"apache2": "stopped"}]
  627             data = {"name": "ps", "beacon_data": bdata, "func": "add"}
  628 
  629             tag = "manage_beacons"
  630 
  631             minion.manage_beacons(tag, data)
  632             self.assertIn("ps", minion.opts["beacons"])
  633             self.assertEqual(minion.opts["beacons"]["ps"], bdata)
  634 
  635     def test_prep_ip_port(self):
  636         _ip = ipaddress.ip_address
  637 
  638         opts = {"master": "10.10.0.3", "master_uri_format": "ip_only"}
  639         ret = salt.minion.prep_ip_port(opts)
  640         self.assertEqual(ret, {"master": _ip("10.10.0.3")})
  641 
  642         opts = {
  643             "master": "10.10.0.3",
  644             "master_port": 1234,
  645             "master_uri_format": "default",
  646         }
  647         ret = salt.minion.prep_ip_port(opts)
  648         self.assertEqual(ret, {"master": "10.10.0.3"})
  649 
  650         opts = {"master": "10.10.0.3:1234", "master_uri_format": "default"}
  651         ret = salt.minion.prep_ip_port(opts)
  652         self.assertEqual(ret, {"master": "10.10.0.3", "master_port": 1234})
  653 
  654         opts = {"master": "host name", "master_uri_format": "default"}
  655         self.assertRaises(SaltClientError, salt.minion.prep_ip_port, opts)
  656 
  657         opts = {"master": "10.10.0.3:abcd", "master_uri_format": "default"}
  658         self.assertRaises(SaltClientError, salt.minion.prep_ip_port, opts)
  659 
  660         opts = {"master": "10.10.0.3::1234", "master_uri_format": "default"}
  661         self.assertRaises(SaltClientError, salt.minion.prep_ip_port, opts)
  662 
  663 
  664 class MinionAsyncTestCase(
  665     TestCase, AdaptedConfigurationTestCaseMixin, salt.ext.tornado.testing.AsyncTestCase
  666 ):
  667     def setUp(self):
  668         super().setUp()
  669         self.opts = {}
  670         self.addCleanup(delattr, self, "opts")
  671 
  672     @skip_if_not_root
  673     def test_sock_path_len(self):
  674         """
  675         This tests whether or not a larger hash causes the sock path to exceed
  676         the system's max sock path length. See the below link for more
  677         information.
  678 
  679         https://github.com/saltstack/salt/issues/12172#issuecomment-43903643
  680         """
  681         opts = {
  682             "id": "salt-testing",
  683             "hash_type": "sha512",
  684             "sock_dir": os.path.join(salt.syspaths.SOCK_DIR, "minion"),
  685             "extension_modules": "",
  686         }
  687         with patch.dict(self.opts, opts):
  688             try:
  689                 event_publisher = event.AsyncEventPublisher(self.opts)
  690                 result = True
  691             except ValueError:
  692                 #  There are rare cases where we operate a closed socket, especially in containers.
  693                 # In this case, don't fail the test because we'll catch it down the road.
  694                 result = True
  695             except SaltSystemExit:
  696                 result = False
  697         self.assertTrue(result)
  698 
  699     @salt.ext.tornado.testing.gen_test
  700     @skipIf(
  701         salt.utils.platform.is_windows(), "Skipping, no Salt master running on Windows."
  702     )
  703     def test_master_type_failover(self):
  704         """
  705         Tests master_type "failover" to not fall back to 127.0.0.1 address when master does not resolve in DNS
  706         """
  707         mock_opts = salt.config.DEFAULT_MINION_OPTS.copy()
  708         mock_opts.update(
  709             {
  710                 "master_type": "failover",
  711                 "master": ["master1", "master2"],
  712                 "__role": "",
  713                 "retry_dns": 0,
  714             }
  715         )
  716 
  717         class MockPubChannel:
  718             def connect(self):
  719                 raise SaltClientError("MockedChannel")
  720 
  721             def close(self):
  722                 return
  723 
  724         def mock_resolve_dns(opts, fallback=False):
  725             self.assertFalse(fallback)
  726 
  727             if opts["master"] == "master1":
  728                 raise SaltClientError("Cannot resolve {}".format(opts["master"]))
  729 
  730             return {
  731                 "master_ip": "192.168.2.1",
  732                 "master_uri": "tcp://192.168.2.1:4505",
  733             }
  734 
  735         def mock_transport_factory(opts, **kwargs):
  736             self.assertEqual(opts["master"], "master2")
  737             return MockPubChannel()
  738 
  739         with patch("salt.minion.resolve_dns", mock_resolve_dns), patch(
  740             "salt.transport.client.AsyncPubChannel.factory", mock_transport_factory
  741         ), patch("salt.loader.grains", MagicMock(return_value=[])):
  742             with self.assertRaises(SaltClientError, msg="MockedChannel"):
  743                 minion = salt.minion.Minion(mock_opts)
  744                 yield minion.connect_master()
  745 
  746     @salt.ext.tornado.testing.gen_test
  747     def test_master_type_failover_no_masters(self):
  748         """
  749         Tests master_type "failover" to not fall back to 127.0.0.1 address when no master can be resolved
  750         """
  751         mock_opts = salt.config.DEFAULT_MINION_OPTS.copy()
  752         mock_opts.update(
  753             {
  754                 "master_type": "failover",
  755                 "master": ["master1", "master2"],
  756                 "__role": "",
  757                 "retry_dns": 0,
  758             }
  759         )
  760 
  761         def mock_resolve_dns(opts, fallback=False):
  762             self.assertFalse(fallback)
  763             raise SaltClientError("Cannot resolve {}".format(opts["master"]))
  764 
  765         with patch("salt.minion.resolve_dns", mock_resolve_dns), patch(
  766             "salt.loader.grains", MagicMock(return_value=[])
  767         ):
  768             with self.assertRaises(SaltClientError, msg="No master could be resolved"):
  769                 minion = salt.minion.Minion(mock_opts)
  770                 yield minion.connect_master()