Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2018.3] Adding retry_dns_count to minion #49764

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions conf/minion
Expand Up @@ -66,6 +66,11 @@
# Set to zero if the minion should shutdown and not retry.
# retry_dns: 30

# Set the number of times to attempt to resolve
# the master hostname if name resolution fails. Defaults to None,
# which will attempt the resolution indefinitely.
# retry_dns_count: 3

# Set the port used by the master reply and authentication server.
#master_port: 4506

Expand Down
17 changes: 17 additions & 0 deletions doc/ref/configuration/minion.rst
Expand Up @@ -307,6 +307,23 @@ Set to zero if the minion should shutdown and not retry.

retry_dns: 30

.. conf_minion:: retry_dns_count

``retry_dns_count``
-------------------

.. versionadded:: 2018.3.4

Default: ``None``

Set the number of attempts to perform when resolving
the master hostname if name resolution fails.
By default the minion will retry indefinitely.

.. code-block:: yaml

retry_dns_count: 3

.. conf_minion:: master_port

``master_port``
Expand Down
2 changes: 2 additions & 0 deletions salt/config/__init__.py
Expand Up @@ -518,6 +518,7 @@ def _gather_buffer_space():
# The number of seconds to sleep between retrying an attempt to resolve the hostname of a
# salt master
'retry_dns': float,
'retry_dns_count': (type(None), int),

# In the case when the resolve of the salt master hostname fails, fall back to localhost
'resolve_dns_fallback': bool,
Expand Down Expand Up @@ -1397,6 +1398,7 @@ def _gather_buffer_space():
'update_url': False,
'update_restart_services': [],
'retry_dns': 30,
garethgreenaway marked this conversation as resolved.
Show resolved Hide resolved
'retry_dns_count': None,
'resolve_dns_fallback': True,
'recon_max': 10000,
'recon_default': 1000,
Expand Down
6 changes: 6 additions & 0 deletions salt/exceptions.py
Expand Up @@ -96,6 +96,12 @@ class SaltSyndicMasterError(SaltException):
'''


class SaltMasterUnresolvableError(SaltException):
'''
Problem resolving the name of the Salt master
'''


class MasterExit(SystemExit):
'''
Rise when the master exits
Expand Down
29 changes: 26 additions & 3 deletions salt/minion.py
Expand Up @@ -115,6 +115,7 @@
SaltSystemExit,
SaltDaemonNotRunning,
SaltException,
SaltMasterUnresolvableError
)


Expand Down Expand Up @@ -154,8 +155,13 @@ def resolve_dns(opts, fallback=True):
True,
opts['ipv6'])
except SaltClientError:
retry_dns_count = opts.get('retry_dns_count', None)
if opts['retry_dns']:
while True:
if retry_dns_count is not None:
if retry_dns_count == 0:
raise SaltMasterUnresolvableError
retry_dns_count -= 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that's the thing. I'd set it default to something big enough. Any None/0 or negative is "until forever".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks correct to me. It should default "until forever" because that is the current behavior which we don't want to break for people.

import salt.log
msg = ('Master hostname: \'{0}\' not found or not responsive. '
'Retrying in {1} seconds').format(opts['master'], opts['retry_dns'])
Expand Down Expand Up @@ -962,7 +968,17 @@ def _create_minion_object(self, opts, timeout, safe,
loaded_base_name=loaded_base_name,
jid_queue=jid_queue)

def _spawn_minions(self):
def _check_minions(self):
'''
Check the size of self.minions and raise an error if it's empty
'''
if not self.minions:
err = ('Minion unable to successfully connect to '
'a Salt Master. Exiting.')
log.error(err)
raise SaltSystemExit(code=42, msg=err)

def _spawn_minions(self, timeout=60):
'''
Spawn all the coroutines which will sign in to masters
'''
Expand All @@ -981,8 +997,9 @@ def _spawn_minions(self):
loaded_base_name='salt.loader.{0}'.format(s_opts['master']),
jid_queue=self.jid_queue,
)
self.minions.append(minion)
self.io_loop.spawn_callback(self._connect_minion, minion)

self._connect_minion(minion)
self.io_loop.call_later(timeout, self._check_minions)

@tornado.gen.coroutine
def _connect_minion(self, minion):
Expand All @@ -1000,6 +1017,7 @@ def _connect_minion(self, minion):
minion.setup_scheduler(before_connect=True)
yield minion.connect_master(failed=failed)
minion.tune_in(start=False)
self.minions.append(minion)
break
except SaltClientError as exc:
failed = True
Expand All @@ -1011,6 +1029,11 @@ def _connect_minion(self, minion):
if auth_wait < self.max_auth_wait:
auth_wait += self.auth_wait
yield tornado.gen.sleep(auth_wait) # TODO: log?
except SaltMasterUnresolvableError:
err = 'Master address: \'{0}\' could not be resolved. Invalid or unresolveable address. ' \
'Set \'master\' value in minion config.'.format(minion.opts['master'])
log.error(err)
break
except Exception as e:
failed = True
log.critical(
Expand Down
13 changes: 12 additions & 1 deletion tests/unit/test_minion.py
Expand Up @@ -16,7 +16,7 @@
# Import salt libs
import salt.minion
import salt.utils.event as event
from salt.exceptions import SaltSystemExit
from salt.exceptions import SaltSystemExit, SaltMasterUnresolvableError
import salt.syspaths
import tornado
import tornado.testing
Expand Down Expand Up @@ -282,6 +282,17 @@ def test_scheduler_before_connect(self):
finally:
minion.destroy()

def test_minion_retry_dns_count(self):
'''
Tests that the resolve_dns will retry dns look ups for a maximum of
3 times before raising a SaltMasterUnresolvableError exception.
'''
with patch.dict(__opts__, {'ipv6': False, 'master': 'dummy',
'master_port': '4555',
'retry_dns': 1, 'retry_dns_count': 3}):
self.assertRaises(SaltMasterUnresolvableError,
salt.minion.resolve_dns, __opts__)


@skipIf(NO_MOCK, NO_MOCK_REASON)
class MinionAsyncTestCase(TestCase, AdaptedConfigurationTestCaseMixin, tornado.testing.AsyncTestCase):
Expand Down