Ensure channels are closed on connection errors

This commit is contained in:
Daniel A. Wozniak 2024-01-30 16:38:39 -07:00 committed by Pedro Algarvio
parent b1b0c37871
commit 0568864435
3 changed files with 54 additions and 12 deletions

View file

@ -594,14 +594,22 @@ class AsyncPubChannel:
def _decode_payload(self, payload):
# we need to decrypt it
log.trace("Decoding payload: %s", payload)
reauth = False
if payload["enc"] == "aes":
self._verify_master_signature(payload)
try:
payload["load"] = self.auth.crypticle.loads(payload["load"])
except salt.crypt.AuthenticationError:
yield self.auth.authenticate()
payload["load"] = self.auth.crypticle.loads(payload["load"])
reauth = True
if reauth:
try:
yield self.auth.authenticate()
payload["load"] = self.auth.crypticle.loads(payload["load"])
except salt.crypt.AuthenticationError:
log.error(
"Payload decryption failed even after re-authenticating with master %s",
self.opts["master_ip"],
)
raise salt.ext.tornado.gen.Return(payload)
def __enter__(self):

View file

@ -1152,17 +1152,20 @@ class MinionManager(MinionBase):
self.minions.append(minion)
break
except SaltClientError as exc:
minion.destroy()
failed = True
log.error(
"Error while bringing up minion for multi-master. Is "
"master at %s responding?",
"master at %s responding? The error message was %s",
minion.opts["master"],
exc,
)
last = time.time()
if auth_wait < self.max_auth_wait:
auth_wait += self.auth_wait
yield salt.ext.tornado.gen.sleep(auth_wait) # TODO: log?
except SaltMasterUnresolvableError:
minion.destroy()
err = (
"Master address: '{}' could not be resolved. Invalid or"
" unresolveable address. Set 'master' value in minion config.".format(
@ -1172,6 +1175,7 @@ class MinionManager(MinionBase):
log.error(err)
break
except Exception as e: # pylint: disable=broad-except
minion.destroy()
failed = True
log.critical(
"Unexpected error while connecting to %s",
@ -3281,20 +3285,14 @@ class Minion(MinionBase):
"""
Tear down the minion
"""
if self._running is False:
return
self._running = False
if hasattr(self, "schedule"):
del self.schedule
if hasattr(self, "pub_channel") and self.pub_channel is not None:
self.pub_channel.on_recv(None)
if hasattr(self.pub_channel, "close"):
self.pub_channel.close()
del self.pub_channel
if hasattr(self, "req_channel") and self.req_channel:
self.pub_channel.close()
if hasattr(self, "req_channel") and self.req_channel is not None:
self.req_channel.close()
self.req_channel = None
if hasattr(self, "periodic_callbacks"):
for cb in self.periodic_callbacks.values():
cb.stop()

View file

@ -1119,3 +1119,39 @@ def test_load_args_and_kwargs(minion_opts):
_args = [{"max_sleep": 40, "__kwarg__": True}]
with pytest.raises(salt.exceptions.SaltInvocationError):
ret = salt.minion.load_args_and_kwargs(test_mod.rand_sleep, _args)
def test_connect_master_salt_client_error(minion_opts):
"""
Ensure minion's destory method is called on an salt client error while connecting to master.
"""
mm = salt.minion.MinionManager(minion_opts)
minion = salt.minion.Minion(minion_opts)
minion.connect_master = MagicMock(side_effect=SaltClientError)
minion.destroy = MagicMock()
mm._connect_minion(minion)
minion.destroy.assert_called_once()
def test_connect_master_unresolveable_error(minion_opts):
"""
Ensure minion's destory method is called on an unresolvable while connecting to master.
"""
mm = salt.minion.MinionManager(minion_opts)
minion = salt.minion.Minion(minion_opts)
minion.connect_master = MagicMock(side_effect=SaltMasterUnresolvableError)
minion.destroy = MagicMock()
mm._connect_minion(minion)
minion.destroy.assert_called_once()
def test_connect_master_general_exception_error(minion_opts):
"""
Ensure minion's destory method is called on an un-handled exception while connecting to master.
"""
mm = salt.minion.MinionManager(minion_opts)
minion = salt.minion.Minion(minion_opts)
minion.connect_master = MagicMock(side_effect=Exception)
minion.destroy = MagicMock()
mm._connect_minion(minion)
minion.destroy.assert_called_once()