diff --git a/salt/loader/__init__.py b/salt/loader/__init__.py index b41cc64b8e..558839e857 100644 --- a/salt/loader/__init__.py +++ b/salt/loader/__init__.py @@ -335,6 +335,10 @@ def minion_mods( pack_self="__salt__", ) + # Allow the usage of salt dunder in utils modules. + if utils and isinstance(utils, _LazyLoader): + utils.pack["__salt__"] = ret + # Load any provider overrides from the configuration file providers option # Note: Providers can be pkg, service, user or group - not to be confused # with cloud providers. diff --git a/salt/loader/context.py b/salt/loader/context.py index cc45441c96..34bf69989d 100644 --- a/salt/loader/context.py +++ b/salt/loader/context.py @@ -12,6 +12,8 @@ # Py<3.7 import contextvars +import salt.exceptions + DEFAULT_CTX_VAR = "loader_ctxvar" loader_ctxvar = contextvars.ContextVar(DEFAULT_CTX_VAR) @@ -69,7 +71,12 @@ def value(self): return loader.pack[self.name] if self.name == loader.pack_self: return loader - return loader.pack[self.name] + try: + return loader.pack[self.name] + except KeyError: + raise salt.exceptions.LoaderError( + f"LazyLoader does not have a packed value for: {self.name}" + ) def get(self, key, default=None): return self.value().get(key, default) diff --git a/salt/minion.py b/salt/minion.py index 2ccd0cd5a9..2680ecc1a9 100644 --- a/salt/minion.py +++ b/salt/minion.py @@ -1251,6 +1251,7 @@ def __init__( self.ready = False self.jid_queue = [] if jid_queue is None else jid_queue self.periodic_callbacks = {} + self.req_channel = None if io_loop is None: self.io_loop = salt.ext.tornado.ioloop.IOLoop.current() @@ -1361,6 +1362,16 @@ def connect_master(self, failed=False): """ Return a future which will complete when you are connected to a master """ + if hasattr(self, "pub_channel") and self.pub_channel: + self.pub_channel.on_recv(None) + if hasattr(self.pub_channel, "auth"): + self.pub_channel.auth.invalidate() + if hasattr(self.pub_channel, "close"): + self.pub_channel.close() + if hasattr(self, "req_channel") and self.req_channel: + self.req_channel.close() + self.req_channel = None + master, self.pub_channel = yield self.eval_master( self.opts, self.timeout, self.safe, failed ) @@ -2775,7 +2786,9 @@ def handle_event(self, package): self.pub_channel.auth.invalidate() if hasattr(self.pub_channel, "close"): self.pub_channel.close() - del self.pub_channel + if hasattr(self, "req_channel") and self.req_channel: + self.req_channel.close() + self.req_channel = None # if eval_master finds a new master for us, self.connected # will be True again on successful master authentication @@ -3210,6 +3223,9 @@ def destroy(self): if hasattr(self.pub_channel, "close"): self.pub_channel.close() del self.pub_channel + if hasattr(self, "req_channel") and self.req_channel: + self.req_channel.close() + self.req_channel = None if hasattr(self, "periodic_callbacks"): for cb in self.periodic_callbacks.values(): cb.stop() diff --git a/salt/utils/azurearm.py b/salt/utils/azurearm.py index 276cbb66b3..9ae128273c 100644 --- a/salt/utils/azurearm.py +++ b/salt/utils/azurearm.py @@ -47,8 +47,6 @@ except ImportError: HAS_AZURE = False -__opts__ = salt.config.minion_config("/etc/salt/minion") -__salt__ = salt.loader.minion_mods(__opts__) log = logging.getLogger(__name__) diff --git a/salt/utils/vault.py b/salt/utils/vault.py index cbd2aec2b0..07fcf3929b 100644 --- a/salt/utils/vault.py +++ b/salt/utils/vault.py @@ -36,8 +36,9 @@ def __virtual__(): logging.getLogger("requests").setLevel(logging.WARNING) return True except Exception as e: # pylint: disable=broad-except - log.error("Could not load __salt__: %s", e) + log.error("Could not load __salt__: %s", e, exc_info=True) return False + return True def _get_token_and_url_from_master(): diff --git a/tests/pytests/integration/cli/test_matcher.py b/tests/pytests/integration/cli/test_matcher.py index 6b3b21bbaa..ecd9e31265 100644 --- a/tests/pytests/integration/cli/test_matcher.py +++ b/tests/pytests/integration/cli/test_matcher.py @@ -506,7 +506,9 @@ def test_salt_documentation(salt_cli, salt_minion): """ Test to see if we're supporting --doc """ - ret = salt_cli.run("-d", "test", minion_tgt=salt_minion.id) + # Setting an explicity long timeout otherwise this test may fail when the + # system is under load. + ret = salt_cli.run("-d", "test", minion_tgt=salt_minion.id, _timeout=90) assert ret.returncode == 0 assert "test.ping" in ret.data diff --git a/tests/pytests/integration/minion/test_return_retries.py b/tests/pytests/integration/minion/test_return_retries.py index a7f5eaeff1..cd67b0b25d 100644 --- a/tests/pytests/integration/minion/test_return_retries.py +++ b/tests/pytests/integration/minion/test_return_retries.py @@ -28,7 +28,7 @@ def salt_minion_retry(salt_master_factory, salt_minion_id): @pytest.mark.slow_test def test_publish_retry(salt_master, salt_minion_retry, salt_cli, salt_run_cli): # run job that takes some time for warmup - rtn = salt_cli.run("test.sleep", "5", "--async", minion_tgt=salt_minion_retry.id) + rtn = salt_cli.run("test.sleep", "3.5", "--async", minion_tgt=salt_minion_retry.id) # obtain JID jid = rtn.stdout.strip().split(" ")[-1] diff --git a/tests/pytests/scenarios/multimaster/beacons/test_inotify.py b/tests/pytests/scenarios/multimaster/beacons/test_inotify.py index c16384d7b6..16babf1cf7 100644 --- a/tests/pytests/scenarios/multimaster/beacons/test_inotify.py +++ b/tests/pytests/scenarios/multimaster/beacons/test_inotify.py @@ -46,6 +46,7 @@ def setup_beacons(mm_master_1_salt_cli, salt_mm_minion_1, inotify_test_path): "inotify", beacon_data=[{"files": {str(inotify_test_path): {"mask": ["create"]}}}], minion_tgt=salt_mm_minion_1.id, + timeout=60, ) assert ret.returncode == 0 log.debug("Inotify beacon add returned: %s", ret.data or ret.stdout) diff --git a/tests/pytests/unit/loader/test_loader.py b/tests/pytests/unit/loader/test_loader.py index 86348749db..e62fa317bc 100644 --- a/tests/pytests/unit/loader/test_loader.py +++ b/tests/pytests/unit/loader/test_loader.py @@ -10,6 +10,7 @@ import pytest +import salt.exceptions import salt.loader import salt.loader.lazy @@ -62,3 +63,23 @@ def test_raw_mod_functions(): ret = salt.loader.raw_mod(opts, "grains", "get") for k, v in ret.items(): assert isinstance(v, salt.loader.lazy.LoadedFunc) + + +def test_named_loader_context_name_not_packed(tmp_path): + opts = { + "optimization_order": [0], + } + contents = """ + from salt.loader.context import LoaderContext + loader_context = LoaderContext() + __not_packed__ = loader_context.named_context("__not_packed__") + def foobar(): + return __not_packed__["not.packed"]() + """ + with pytest.helpers.temp_file("mymod.py", contents, directory=tmp_path): + loader = salt.loader.LazyLoader([tmp_path], opts) + with pytest.raises( + salt.exceptions.LoaderError, + match="LazyLoader does not have a packed value for: __not_packed__", + ): + loader["mymod.foobar"]() diff --git a/tests/pytests/unit/loader/test_loading_modules.py b/tests/pytests/unit/loader/test_loading_modules.py new file mode 100644 index 0000000000..861e9197ec --- /dev/null +++ b/tests/pytests/unit/loader/test_loading_modules.py @@ -0,0 +1,38 @@ +import pytest + +import salt.loader +import salt.loader.lazy +import salt.modules.boto_vpc +import salt.modules.virt + + +@pytest.fixture +def minion_mods(minion_opts): + utils = salt.loader.utils(minion_opts) + return salt.loader.minion_mods(minion_opts, utils=utils) + + +@pytest.mark.skipif( + not salt.modules.boto_vpc.HAS_BOTO, reason="boto must be installed." +) +def test_load_boto_vpc(minion_mods): + func = None + try: + func = minion_mods["boto_vpc.check_vpc"] + except KeyError: + pytest.fail("loader should not raise KeyError") + assert func is not None + assert isinstance(func, salt.loader.lazy.LoadedFunc) + + +@pytest.mark.skipif( + not salt.modules.virt.HAS_LIBVIRT, reason="libvirt-python must be installed." +) +def test_load_virt(minion_mods): + func = None + try: + func = minion_mods["virt.ctrl_alt_del"] + except KeyError: + pytest.fail("loader should not raise KeyError") + assert func is not None + assert isinstance(func, salt.loader.lazy.LoadedFunc)