# Buggy code
def _authenticate(self):
    """
    Authenticate with the master, this method breaks the functional
    paradigm, it will update the master information from a fresh sign
    in, signing in can occur as often as needed to keep up with the
    revolving master AES key.

    :rtype: Crypticle
    :returns: A crypticle used for encryption operations
    """
    acceptance_wait_time = self.opts["acceptance_wait_time"]
    acceptance_wait_time_max = self.opts["acceptance_wait_time_max"]
    if not acceptance_wait_time_max:
        acceptance_wait_time_max = acceptance_wait_time
    creds = None
    channel = salt.transport.client.AsyncReqChannel.factory(
        self.opts, crypt="clear", io_loop=self.io_loop
    )
    while True:
        try:
            creds = yield self.sign_in(channel=channel)
        except SaltClientError:
            break
        if creds == "retry":
            if self.opts.get("caller"):
                print(
                    "Minion failed to authenticate with the master, "
                    "has the minion key been accepted?"
                )
                sys.exit(2)
            if acceptance_wait_time:
                log.info(
                    "Waiting {0} seconds before retry.".format(acceptance_wait_time)
                )
                yield tornado.gen.sleep(acceptance_wait_time)
            if acceptance_wait_time < acceptance_wait_time_max:
                acceptance_wait_time += acceptance_wait_time
                log.debug(
                    "Authentication wait time is {0}".format(acceptance_wait_time)
                )
            continue
        break
    if not isinstance(creds, dict) or "aes" not in creds:
        try:
            del AsyncAuth.creds_map[self.__key(self.opts)]
        except KeyError:
            pass
        self._authenticate_future.set_exception(
            SaltClientError("Attempt to authenticate with the salt master failed")
        )
    else:
        AsyncAuth.creds_map[self.__key(self.opts)] = creds
        self._creds = creds
        self._crypticle = Crypticle(self.opts, creds["aes"])
        self._authenticate_future.set_result(True)  # mark the sign-in as complete


# Traceback (SaltClientError)
[root@ch3ll-cent7 ~]# salt-minion -ldebug
[DEBUG   ] Reading configuration from /etc/salt/minion
[DEBUG   ] Including configuration from '/etc/salt/minion.d/_schedule.conf'
[DEBUG   ] Reading configuration from /etc/salt/minion.d/_schedule.conf
[DEBUG   ] Using cached minion ID from /etc/salt/minion_id: ch3ll-cent7.c7.saltstack.net
[DEBUG   ] Configuration file path: /etc/salt/minion
[WARNING ] Insecure logging configuration detected! Sensitive data may be logged.
[INFO    ] Setting up the Salt Minion "ch3ll-cent7.c7.saltstack.net"
[DEBUG   ] Created pidfile: /var/run/salt-minion.pid
[DEBUG   ] Reading configuration from /etc/salt/minion
[DEBUG   ] Including configuration from '/etc/salt/minion.d/_schedule.conf'
[DEBUG   ] Reading configuration from /etc/salt/minion.d/_schedule.conf
[WARNING ] IMPORTANT: Do not use md5 hashing algorithm! Please set "hash_type" to sha256 in Salt Minion config!
[INFO    ] The Salt Minion is starting up
[INFO    ] Minion is starting as user 'root'
[DEBUG   ] AsyncEventPublisher PUB socket URI: ipc:///var/run/salt/minion/minion_event_9126913ec3_pub.ipc
[DEBUG   ] AsyncEventPublisher PULL socket URI: ipc:///var/run/salt/minion/minion_event_9126913ec3_pull.ipc
[INFO    ] Starting pub socket on ipc:///var/run/salt/minion/minion_event_9126913ec3_pub.ipc
[INFO    ] Starting pull socket on ipc:///var/run/salt/minion/minion_event_9126913ec3_pull.ipc
[DEBUG   ] Minion 'ch3ll-cent7.c7.saltstack.net' trying to tune in
[DEBUG   ] Initializing new AsyncAuth for ('/etc/salt/pki/minion', 'ch3ll-cent7.c7.saltstack.net', 'tcp://54.159.114.180:4506')
[DEBUG   ] Generated random reconnect delay between '1000ms' and '11000ms' (2882)
[DEBUG   ] Setting zmq_reconnect_ivl to '2882ms'
[DEBUG   ] Setting zmq_reconnect_ivl_max to '11000ms'
[DEBUG   ] Initializing new AsyncZeroMQReqChannel for ('/etc/salt/pki/minion', 'ch3ll-cent7.c7.saltstack.net', 'tcp://54.159.114.180:4506', 'clear')
[INFO    ] The Salt Minion is shut down
[WARNING ] /usr/lib/python2.7/site-packages/salt/scripts.py:83: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6
log.error('Minion failed to start: {0}'.format(exc.message), exc_info=True)

[ERROR   ] Minion failed to start: Attempt to authenticate with the salt master failed
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/salt/scripts.py", line 81, in minion_process
minion.start()
File "/usr/lib/python2.7/site-packages/salt/cli/daemons.py", line 320, in start
self.minion.tune_in()
File "/usr/lib/python2.7/site-packages/salt/minion.py", line 1699, in tune_in
self.sync_connect_master()
File "/usr/lib/python2.7/site-packages/salt/minion.py", line 760, in sync_connect_master
raise six.reraise(*future_exception)
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 876, in run
yielded = self.gen.throw(*exc_info)
File "/usr/lib/python2.7/site-packages/salt/minion.py", line 767, in connect_master
master, self.pub_channel = yield self.eval_master(self.opts, self.timeout, self.safe)
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 870, in run
value = future.result()
File "/usr/lib64/python2.7/site-packages/tornado/concurrent.py", line 214, in result
raise_exc_info(self._exc_info)
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 876, in run
yielded = self.gen.throw(*exc_info)
File "/usr/lib/python2.7/site-packages/salt/minion.py", line 503, in eval_master
yield pub_channel.connect()
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 870, in run
value = future.result()
File "/usr/lib64/python2.7/site-packages/tornado/concurrent.py", line 214, in result
raise_exc_info(self._exc_info)
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 876, in run
yielded = self.gen.throw(*exc_info)
File "/usr/lib/python2.7/site-packages/salt/transport/zeromq.py", line 338, in connect
yield self.auth.authenticate()
File "/usr/lib64/python2.7/site-packages/tornado/gen.py", line 870, in run
value = future.result()
File "/usr/lib64/python2.7/site-packages/tornado/concurrent.py", line 214, in result
raise_exc_info(self._exc_info)
File "<string>", line 3, in raise_exc_info
SaltClientError: Attempt to authenticate with the salt master failed
[WARNING ] ** Restarting minion **
[INFO    ] Sleeping random_reauth_delay of 6 seconds
[DEBUG   ] Reading configuration from /etc/salt/minion
[DEBUG   ] Including configuration from '/etc/salt/minion.d/_schedule.conf'

# Fixed code
def _authenticate(self):
    """
    Authenticate with the master, this method breaks the functional
    paradigm, it will update the master information from a fresh sign
    in, signing in can occur as often as needed to keep up with the
    revolving master AES key.

    :rtype: Crypticle
    :returns: A crypticle used for encryption operations
    """
    acceptance_wait_time = self.opts["acceptance_wait_time"]
    acceptance_wait_time_max = self.opts["acceptance_wait_time_max"]
    if not acceptance_wait_time_max:
        acceptance_wait_time_max = acceptance_wait_time
    creds = None
    channel = salt.transport.client.AsyncReqChannel.factory(
        self.opts, crypt="clear", io_loop=self.io_loop
    )
    error = None
    while True:
        try:
            creds = yield self.sign_in(channel=channel)
        except SaltClientError as error:
            break
        if creds == "retry":
            if self.opts.get("caller"):
                print(
                    "Minion failed to authenticate with the master, "
                    "has the minion key been accepted?"
                )
                sys.exit(2)
            if acceptance_wait_time:
                log.info(
                    "Waiting {0} seconds before retry.".format(acceptance_wait_time)
                )
                yield tornado.gen.sleep(acceptance_wait_time)
            if acceptance_wait_time < acceptance_wait_time_max:
                acceptance_wait_time += acceptance_wait_time
                log.debug(
                    "Authentication wait time is {0}".format(acceptance_wait_time)
                )
            continue
        break
    if not isinstance(creds, dict) or "aes" not in creds:
        try:
            del AsyncAuth.creds_map[self.__key(self.opts)]
        except KeyError:
            pass
        if not error:
            error = SaltClientError(
                "Attempt to authenticate with the salt master failed"
            )
        self._authenticate_future.set_exception(error)
    else:
        AsyncAuth.creds_map[self.__key(self.opts)] = creds
        self._creds = creds
        self._crypticle = Crypticle(self.opts, creds["aes"])
        self._authenticate_future.set_result(True)  # mark the sign-in as complete