From c0d7cc4bb8f2cba3ecff6e8171369dfdc326173579f012272459fb4c68d93a11 Mon Sep 17 00:00:00 2001 From: Bo Maryniuk Date: Mon, 17 Sep 2018 14:18:45 +0000 Subject: [PATCH] Accepting request 636187 from home:mdinca:branches:systemsmanagement:saltstack - Prepend current directory when path is just filename (bsc#1095942) - Integration of MSI authentication for azurearm - Adds fix for SUSE Expanded Support os grain detection - Fixes 509x remote signing - Fix for StringIO import in Python2 - Use Adler32 algorithm to compute string checksums (bsc#1102819) - Only do reverse DNS lookup on IPs for salt-ssh (bsc#1104154) - Add support for Python 3.7 - Fix license macro to build on SLE12SP2 - Decode file contents for python2 (bsc#1102013) - Fix for sorting of multi-version packages (bsc#1097174 and bsc#1097413) - Fix mine.get not returning data - workaround for #48020 (bsc#1100142) - Added: * change-stringio-import-in-python2-to-import-the-clas.patch * integration-of-msi-authentication-with-azurearm-clou.patch * x509-fixes-for-remote-signing-106.patch * fix-for-suse-expanded-support-detection.patch * only-do-reverse-dns-lookup-on-ips-for-salt-ssh.patch * prepend-current-directory-when-path-is-just-filename.patch * add-support-for-python-3.7.patch * decode-file-contents-for-python2-bsc-1102013.patch * fix-mine.get-not-returning-data-workaround-for-48020.patch * x509-fixes-111.patch * use-adler32-algorithm-to-compute-string-checksums.patch - Modified: * fix-for-sorting-of-multi-version-packages-bsc-109717.patch OBS-URL: https://build.opensuse.org/request/show/636187 OBS-URL: https://build.opensuse.org/package/show/systemsmanagement:saltstack/salt?expand=0&rev=129 --- _lastrevision | 2 +- add-support-for-python-3.7.patch | 1171 +++++++++++++++++ ...import-in-python2-to-import-the-clas.patch | 57 + ...ile-contents-for-python2-bsc-1102013.patch | 27 + ...of-multi-version-packages-bsc-109717.patch | 35 + fix-for-suse-expanded-support-detection.patch | 43 + ...-returning-data-workaround-for-48020.patch | 34 + ...si-authentication-with-azurearm-clou.patch | 118 ++ ...verse-dns-lookup-on-ips-for-salt-ssh.patch | 29 + ...directory-when-path-is-just-filename.patch | 38 + salt.changes | 32 + salt.spec | 40 + ...lgorithm-to-compute-string-checksums.patch | 151 +++ x509-fixes-111.patch | 518 ++++++++ x509-fixes-for-remote-signing-106.patch | 80 ++ 15 files changed, 2374 insertions(+), 1 deletion(-) create mode 100644 add-support-for-python-3.7.patch create mode 100644 change-stringio-import-in-python2-to-import-the-clas.patch create mode 100644 decode-file-contents-for-python2-bsc-1102013.patch create mode 100644 fix-for-sorting-of-multi-version-packages-bsc-109717.patch create mode 100644 fix-for-suse-expanded-support-detection.patch create mode 100644 fix-mine.get-not-returning-data-workaround-for-48020.patch create mode 100644 integration-of-msi-authentication-with-azurearm-clou.patch create mode 100644 only-do-reverse-dns-lookup-on-ips-for-salt-ssh.patch create mode 100644 prepend-current-directory-when-path-is-just-filename.patch create mode 100644 use-adler32-algorithm-to-compute-string-checksums.patch create mode 100644 x509-fixes-111.patch create mode 100644 x509-fixes-for-remote-signing-106.patch diff --git a/_lastrevision b/_lastrevision index 0d45aab..e229c4b 100644 --- a/_lastrevision +++ b/_lastrevision @@ -1 +1 @@ -eb0ca38e07c96eb021ac7490ac1f61a54dc9d904 +ebc77d067d9fa300bdc5bb5dcccaa09e1787f688 \ No newline at end of file diff --git a/add-support-for-python-3.7.patch b/add-support-for-python-3.7.patch new file mode 100644 index 0000000..9bcd8b0 --- /dev/null +++ b/add-support-for-python-3.7.patch @@ -0,0 +1,1171 @@ +From 2c4ea84af38aca525e516e0391ea831d1fe6f611 Mon Sep 17 00:00:00 2001 +From: Bo Maryniuk +Date: Mon, 23 Jul 2018 10:51:41 +0200 +Subject: [PATCH] Add support for Python 3.7 + +Rename module to full wording + +Fix imports + +Fix docstring typo + +Fix CLI config + +Fix comments + +Fix docstrings + +Rename async function to asynchronous + +Change internal function signatures to avoid reserved word + +Remove internal variables/properties with the reserved words + +Fix local opts from CLI + +Fix log error/info/warning and exception messages + +Cleanup docstrings at module level + +Fix function signatures in Cassandra module + +Lintfix: PEP8 requires two empty lines + +Deprecate 'async' parameter in Mandrill API + +Revert api call: it is about "functionname_async" suffix. + +Add 'async' backward compatibility + +Update docstring + +Use kwargs instead of directly named parameters + +Support original API + +Fix nag-message + +Keep runner API unchanged + +fix unicode literals + +Remove async keyword, moving it into the kwargs. + +Fix configuration setting +--- + salt/client/__init__.py | 2 +- + salt/client/api.py | 6 ++--- + salt/client/mixins.py | 4 ++-- + salt/cloud/clouds/msazure.py | 2 +- + salt/cloud/clouds/profitbricks.py | 2 +- + salt/cloud/clouds/xen.py | 2 +- + salt/daemons/masterapi.py | 6 ++--- + salt/engines/slack.py | 4 ++-- + salt/master.py | 6 ++--- + salt/minion.py | 4 ++-- + salt/modules/cassandra_cql.py | 22 ++++++++++--------- + salt/modules/mandrill.py | 21 ++++++++++++------ + salt/modules/saltutil.py | 6 +++-- + salt/netapi/rest_cherrypy/app.py | 4 ++-- + salt/netapi/rest_cherrypy/event_processor.py | 2 +- + salt/netapi/rest_tornado/event_processor.py | 2 +- + salt/netapi/rest_tornado/saltnado.py | 8 +++---- + .../rest_tornado/saltnado_websockets.py | 2 +- + salt/returners/cassandra_cql_return.py | 8 +++---- + salt/runner.py | 10 ++++----- + salt/thorium/runner.py | 6 ++--- + salt/thorium/wheel.py | 4 ++-- + salt/transport/client.py | 2 +- + salt/transport/ipc.py | 10 ++++----- + salt/transport/server.py | 2 +- + salt/transport/tcp.py | 16 +++++++------- + salt/utils/{async.py => asynchronous.py} | 22 +++++++++---------- + salt/utils/event.py | 18 +++++++-------- + salt/utils/process.py | 4 ++-- + salt/utils/thin.py | 2 +- + salt/wheel/__init__.py | 4 ++-- + .../files/engines/runtests_engine.py | 4 ++-- + .../netapi/rest_tornado/test_app.py | 2 +- + tests/support/case.py | 10 +++++---- + tests/unit/utils/test_async.py | 20 ++++++++--------- + 35 files changed, 131 insertions(+), 118 deletions(-) + rename salt/utils/{async.py => asynchronous.py} (81%) + +diff --git a/salt/client/__init__.py b/salt/client/__init__.py +index 9bf8e32491..dcbc1473e1 100644 +--- a/salt/client/__init__.py ++++ b/salt/client/__init__.py +@@ -284,7 +284,7 @@ class LocalClient(object): + 'No command was sent, no jid was assigned.') + return {} + +- # don't install event subscription listeners when the request is async ++ # don't install event subscription listeners when the request is asynchronous + # and doesn't care. this is important as it will create event leaks otherwise + if not listen: + return pub_data +diff --git a/salt/client/api.py b/salt/client/api.py +index ac6f6de24a..b2aab460fa 100644 +--- a/salt/client/api.py ++++ b/salt/client/api.py +@@ -93,7 +93,7 @@ class APIClient(object): + + The cmd dict items are as follows: + +- mode: either 'sync' or 'async'. Defaults to 'async' if missing ++ mode: either 'sync' or 'asynchronous'. Defaults to 'asynchronous' if missing + fun: required. If the function is to be run on the master using either + a wheel or runner client then the fun: includes either + 'wheel.' or 'runner.' as a prefix and has three parts separated by '.'. +@@ -120,7 +120,7 @@ class APIClient(object): + ''' + cmd = dict(cmd) # make copy + client = 'minion' # default to local minion client +- mode = cmd.get('mode', 'async') # default to 'async' ++ mode = cmd.get('mode', 'async') + + # check for wheel or runner prefix to fun name to use wheel or runner client + funparts = cmd.get('fun', '').split('.') +@@ -162,7 +162,7 @@ class APIClient(object): + ''' + return self.runnerClient.master_call(**kwargs) + +- runner_sync = runner_async # always runner async, so works in either mode ++ runner_sync = runner_async # always runner asynchronous, so works in either mode + + def wheel_sync(self, **kwargs): + ''' +diff --git a/salt/client/mixins.py b/salt/client/mixins.py +index 29b6077661..4182fa5b81 100644 +--- a/salt/client/mixins.py ++++ b/salt/client/mixins.py +@@ -458,7 +458,7 @@ class SyncClientMixin(object): + + class AsyncClientMixin(object): + ''' +- A mixin for *Client interfaces to enable easy async function execution ++ A mixin for *Client interfaces to enable easy asynchronous function execution + ''' + client = None + tag_prefix = None +@@ -510,7 +510,7 @@ class AsyncClientMixin(object): + tag = salt.utils.event.tagify(jid, prefix=self.tag_prefix) + return {'tag': tag, 'jid': jid} + +- def async(self, fun, low, user='UNKNOWN', pub=None): ++ def asynchronous(self, fun, low, user='UNKNOWN', pub=None): + ''' + Execute the function in a multiprocess and return the event tag to use + to watch for the return +diff --git a/salt/cloud/clouds/msazure.py b/salt/cloud/clouds/msazure.py +index aa5cd14255..4a95c3af96 100644 +--- a/salt/cloud/clouds/msazure.py ++++ b/salt/cloud/clouds/msazure.py +@@ -888,7 +888,7 @@ def _wait_for_async(conn, request_id): + while result.status == 'InProgress': + count = count + 1 + if count > 120: +- raise ValueError('Timed out waiting for async operation to complete.') ++ raise ValueError('Timed out waiting for asynchronous operation to complete.') + time.sleep(5) + result = conn.get_operation_status(request_id) + +diff --git a/salt/cloud/clouds/profitbricks.py b/salt/cloud/clouds/profitbricks.py +index 1ce0a162f0..8d13bf7b70 100644 +--- a/salt/cloud/clouds/profitbricks.py ++++ b/salt/cloud/clouds/profitbricks.py +@@ -1098,7 +1098,7 @@ def _wait_for_completion(conn, promise, wait_timeout, msg): + ) + + raise Exception( +- 'Timed out waiting for async operation {0} "{1}" to complete.'.format( ++ 'Timed out waiting for asynchronous operation {0} "{1}" to complete.'.format( + msg, six.text_type(promise['requestId']) + ) + ) +diff --git a/salt/cloud/clouds/xen.py b/salt/cloud/clouds/xen.py +index 0b79d4dfb9..6f23b813a7 100644 +--- a/salt/cloud/clouds/xen.py ++++ b/salt/cloud/clouds/xen.py +@@ -719,7 +719,7 @@ def _wait_for_ip(name, session): + + def _run_async_task(task=None, session=None): + ''' +- Run XenAPI task in async mode to prevent timeouts ++ Run XenAPI task in asynchronous mode to prevent timeouts + ''' + if task is None or session is None: + return None +diff --git a/salt/daemons/masterapi.py b/salt/daemons/masterapi.py +index 84537fab3b..62dd0cd1ea 100644 +--- a/salt/daemons/masterapi.py ++++ b/salt/daemons/masterapi.py +@@ -1068,9 +1068,9 @@ class LocalFuncs(object): + try: + fun = load.pop('fun') + runner_client = salt.runner.RunnerClient(self.opts) +- return runner_client.async(fun, +- load.get('kwarg', {}), +- username) ++ return runner_client.asynchronous(fun, ++ load.get('kwarg', {}), ++ username) + except Exception as exc: + log.exception('Exception occurred while introspecting %s') + return {'error': {'name': exc.__class__.__name__, +diff --git a/salt/engines/slack.py b/salt/engines/slack.py +index e664bbee03..c35435e42e 100644 +--- a/salt/engines/slack.py ++++ b/salt/engines/slack.py +@@ -740,7 +740,7 @@ class SlackClient(object): + :param interval: time to wait between ending a loop and beginning the next + + ''' +- log.debug('Going to run a command async') ++ log.debug('Going to run a command asynchronous') + runner_functions = sorted(salt.runner.Runner(__opts__).functions) + # Parse args and kwargs + cmd = msg['cmdline'][0] +@@ -762,7 +762,7 @@ class SlackClient(object): + log.debug('Command %s will run via runner_functions', cmd) + # pylint is tripping + # pylint: disable=missing-whitespace-after-comma +- job_id_dict = runner.async(cmd, {'args': args, 'kwargs': kwargs}) ++ job_id_dict = runner.asynchronous(cmd, {'args': args, 'kwargs': kwargs}) + job_id = job_id_dict['jid'] + + # Default to trying to run as a client module. +diff --git a/salt/master.py b/salt/master.py +index e400054d72..86b639dd5b 100644 +--- a/salt/master.py ++++ b/salt/master.py +@@ -1878,9 +1878,9 @@ class ClearFuncs(object): + try: + fun = clear_load.pop('fun') + runner_client = salt.runner.RunnerClient(self.opts) +- return runner_client.async(fun, +- clear_load.get('kwarg', {}), +- username) ++ return runner_client.asynchronous(fun, ++ clear_load.get('kwarg', {}), ++ username) + except Exception as exc: + log.error('Exception occurred while introspecting %s: %s', fun, exc) + return {'error': {'name': exc.__class__.__name__, +diff --git a/salt/minion.py b/salt/minion.py +index 0a6771dccd..17e11c0ebe 100644 +--- a/salt/minion.py ++++ b/salt/minion.py +@@ -926,7 +926,7 @@ class MinionManager(MinionBase): + install_zmq() + self.io_loop = ZMQDefaultLoop.current() + self.process_manager = ProcessManager(name='MultiMinionProcessManager') +- self.io_loop.spawn_callback(self.process_manager.run, async=True) ++ self.io_loop.spawn_callback(self.process_manager.run, **{'async': True}) # Tornado backward compat + + def __del__(self): + self.destroy() +@@ -1123,7 +1123,7 @@ class Minion(MinionBase): + time.sleep(sleep_time) + + self.process_manager = ProcessManager(name='MinionProcessManager') +- self.io_loop.spawn_callback(self.process_manager.run, async=True) ++ self.io_loop.spawn_callback(self.process_manager.run, **{'async': True}) + # We don't have the proxy setup yet, so we can't start engines + # Engines need to be able to access __proxy__ + if not salt.utils.platform.is_proxy(): +diff --git a/salt/modules/cassandra_cql.py b/salt/modules/cassandra_cql.py +index 82b211bddf..30db93dccc 100644 +--- a/salt/modules/cassandra_cql.py ++++ b/salt/modules/cassandra_cql.py +@@ -93,6 +93,7 @@ from salt.exceptions import CommandExecutionError + # Import 3rd-party libs + from salt.ext import six + from salt.ext.six.moves import range ++import salt.utils.versions + + SSL_VERSION = 'ssl_version' + +@@ -128,7 +129,7 @@ def __virtual__(): + + + def _async_log_errors(errors): +- log.error('Cassandra_cql async call returned: %s', errors) ++ log.error('Cassandra_cql asynchronous call returned: %s', errors) + + + def _load_properties(property_name, config_option, set_default=False, default=None): +@@ -361,9 +362,8 @@ def cql_query(query, contact_points=None, port=None, cql_user=None, cql_pass=Non + return ret + + +-def cql_query_with_prepare(query, statement_name, statement_arguments, async=False, +- callback_errors=None, +- contact_points=None, port=None, cql_user=None, cql_pass=None): ++def cql_query_with_prepare(query, statement_name, statement_arguments, callback_errors=None, contact_points=None, ++ port=None, cql_user=None, cql_pass=None, **kwargs): + ''' + Run a query on a Cassandra cluster and return a dictionary. + +@@ -377,8 +377,8 @@ def cql_query_with_prepare(query, statement_name, statement_arguments, async=Fal + :type statement_name: str + :param statement_arguments: Bind parameters for the SQL statement + :type statement_arguments: list[str] +- :param async: Run this query in asynchronous mode +- :type async: bool ++ :param async: Run this query in asynchronous mode ++ :type async: bool + :param callback_errors: Function to call after query runs if there is an error + :type callback_errors: Function callable + :param contact_points: The Cassandra cluster addresses, can either be a string or a list of IPs. +@@ -401,12 +401,14 @@ def cql_query_with_prepare(query, statement_name, statement_arguments, async=Fal + + # Insert data asynchronously + salt this-node cassandra_cql.cql_query_with_prepare "name_insert" "INSERT INTO USERS (first_name, last_name) VALUES (?, ?)" \ +- statement_arguments=['John','Doe'], async=True ++ statement_arguments=['John','Doe'], asynchronous=True + + # Select data, should not be asynchronous because there is not currently a facility to return data from a future + salt this-node cassandra_cql.cql_query_with_prepare "name_select" "SELECT * FROM USERS WHERE first_name=?" \ + statement_arguments=['John'] + ''' ++ # Backward-compatibility with Python 3.7: "async" is a reserved word ++ asynchronous = kwargs.get('async', False) + try: + cluster, session = _connect(contact_points=contact_points, port=port, + cql_user=cql_user, cql_pass=cql_pass) +@@ -431,7 +433,7 @@ def cql_query_with_prepare(query, statement_name, statement_arguments, async=Fal + ret = [] + + try: +- if async: ++ if asynchronous: + future_results = session.execute_async(bound_statement.bind(statement_arguments)) + # future_results.add_callbacks(_async_log_errors) + else: +@@ -441,7 +443,7 @@ def cql_query_with_prepare(query, statement_name, statement_arguments, async=Fal + msg = "ERROR: Cassandra query failed: {0} reason: {1}".format(query, e) + raise CommandExecutionError(msg) + +- if not async and results: ++ if not asynchronous and results: + for result in results: + values = {} + for key, value in six.iteritems(result): +@@ -456,7 +458,7 @@ def cql_query_with_prepare(query, statement_name, statement_arguments, async=Fal + + # If this was a synchronous call, then we either have an empty list + # because there was no return, or we have a return +- # If this was an async call we only return the empty list ++ # If this was an asynchronous call we only return the empty list + return ret + + +diff --git a/salt/modules/mandrill.py b/salt/modules/mandrill.py +index 248939d09c..7044060154 100644 +--- a/salt/modules/mandrill.py ++++ b/salt/modules/mandrill.py +@@ -24,6 +24,7 @@ import logging + + # Import Salt libs + import salt.utils.json ++import salt.utils.versions + + # import third party + try: +@@ -137,12 +138,13 @@ def _http_request(url, + + + def send(message, +- async=False, ++ asynchronous=False, + ip_pool=None, + send_at=None, + api_url=None, + api_version=None, +- api_key=None): ++ api_key=None, ++ **kwargs): + ''' + Send out the email using the details from the ``message`` argument. + +@@ -151,14 +153,14 @@ def send(message, + sent as dictionary with at fields as specified in the Mandrill API + documentation. + +- async: ``False`` ++ asynchronous: ``False`` + Enable a background sending mode that is optimized for bulk sending. +- In async mode, messages/send will immediately return a status of +- "queued" for every recipient. To handle rejections when sending in async ++ In asynchronous mode, messages/send will immediately return a status of ++ "queued" for every recipient. To handle rejections when sending in asynchronous + mode, set up a webhook for the 'reject' event. Defaults to false for + messages with no more than 10 recipients; messages with more than 10 + recipients are always sent asynchronously, regardless of the value of +- async. ++ asynchronous. + + ip_pool + The name of the dedicated ip pool that should be used to send the +@@ -229,6 +231,11 @@ def send(message, + result: + True + ''' ++ if 'async' in kwargs: # Remove this in Sodium ++ salt.utils.versions.warn_until('Sodium', 'Parameter "async" is renamed to "asynchronous" ' ++ 'and will be removed in version {version}.') ++ asynchronous = bool(kwargs['async']) ++ + params = _get_api_params(api_url=api_url, + api_version=api_version, + api_key=api_key) +@@ -238,7 +245,7 @@ def send(message, + data = { + 'key': params['api_key'], + 'message': message, +- 'async': async, ++ 'async': asynchronous, + 'ip_pool': ip_pool, + 'send_at': send_at + } +diff --git a/salt/modules/saltutil.py b/salt/modules/saltutil.py +index 9cb27858d1..2c152e3ff1 100644 +--- a/salt/modules/saltutil.py ++++ b/salt/modules/saltutil.py +@@ -947,10 +947,11 @@ def refresh_pillar(): + ret = False # Effectively a no-op, since we can't really return without an event system + return ret + ++ + pillar_refresh = salt.utils.functools.alias_function(refresh_pillar, 'pillar_refresh') + + +-def refresh_modules(async=True): ++def refresh_modules(**kwargs): + ''' + Signal the minion to refresh the module and grain data + +@@ -964,8 +965,9 @@ def refresh_modules(async=True): + + salt '*' saltutil.refresh_modules + ''' ++ asynchronous = bool(kwargs.get('async', True)) + try: +- if async: ++ if asynchronous: + # If we're going to block, first setup a listener + ret = __salt__['event.fire']({}, 'module_refresh') + else: +diff --git a/salt/netapi/rest_cherrypy/app.py b/salt/netapi/rest_cherrypy/app.py +index 077ccce0be..78ea3c3fef 100644 +--- a/salt/netapi/rest_cherrypy/app.py ++++ b/salt/netapi/rest_cherrypy/app.py +@@ -529,7 +529,7 @@ described above, the most effective and most scalable way to use both Salt and + salt-api is to run commands asynchronously using the ``local_async``, + ``runner_async``, and ``wheel_async`` clients. + +-Running async jobs results in being able to process 3x more commands per second ++Running asynchronous jobs results in being able to process 3x more commands per second + for ``LocalClient`` and 17x more commands per second for ``RunnerClient``, in + addition to much less network traffic and memory requirements. Job returns can + be fetched from Salt's job cache via the ``/jobs/`` endpoint, or they can +@@ -2534,7 +2534,7 @@ class WebsocketEndpoint(object): + parent_pipe, child_pipe = Pipe() + handler.pipe = parent_pipe + handler.opts = self.opts +- # Process to handle async push to a client. ++ # Process to handle asynchronous push to a client. + # Each GET request causes a process to be kicked off. + proc = Process(target=event_stream, args=(handler, child_pipe)) + proc.start() +diff --git a/salt/netapi/rest_cherrypy/event_processor.py b/salt/netapi/rest_cherrypy/event_processor.py +index e409a00180..f0cf6d361a 100644 +--- a/salt/netapi/rest_cherrypy/event_processor.py ++++ b/salt/netapi/rest_cherrypy/event_processor.py +@@ -180,7 +180,7 @@ class SaltInfo(object): + 'expr_type': 'list', + 'mode': 'client', + 'client': 'local', +- 'async': 'local_async', ++ 'asynchronous': 'local_async', + 'token': token, + }) + +diff --git a/salt/netapi/rest_tornado/event_processor.py b/salt/netapi/rest_tornado/event_processor.py +index d8c338836e..70a379e2c5 100644 +--- a/salt/netapi/rest_tornado/event_processor.py ++++ b/salt/netapi/rest_tornado/event_processor.py +@@ -194,7 +194,7 @@ class SaltInfo(object): + 'expr_type': 'list', + 'mode': 'client', + 'client': 'local', +- 'async': 'local_async', ++ 'asynchronous': 'local_async', + 'token': token, + }) + +diff --git a/salt/netapi/rest_tornado/saltnado.py b/salt/netapi/rest_tornado/saltnado.py +index 2da44960c8..7942033c59 100644 +--- a/salt/netapi/rest_tornado/saltnado.py ++++ b/salt/netapi/rest_tornado/saltnado.py +@@ -244,7 +244,7 @@ def _json_dumps(obj, **kwargs): + + # # master side + # - "runner" (done) +-# - "wheel" (need async api...) ++# - "wheel" (need asynchronous api...) + + + AUTH_TOKEN_HEADER = 'X-Auth-Token' +@@ -273,7 +273,7 @@ class Any(Future): + class EventListener(object): + ''' + Class responsible for listening to the salt master event bus and updating +- futures. This is the core of what makes this async, this allows us to do ++ futures. This is the core of what makes this asynchronous, this allows us to do + non-blocking work in the main processes and "wait" for an event to happen + ''' + +@@ -336,7 +336,7 @@ class EventListener(object): + timeout=None + ): + ''' +- Get an event (async of course) return a future that will get it later ++ Get an event (asynchronous of course) return a future that will get it later + ''' + # if the request finished, no reason to allow event fetching, since we + # can't send back to the client +@@ -653,7 +653,7 @@ class SaltAuthHandler(BaseSaltAPIHandler): # pylint: disable=W0223 + + self.write(self.serialize(ret)) + +- # TODO: make async? Underlying library isn't... and we ARE making disk calls :( ++ # TODO: make asynchronous? Underlying library isn't... and we ARE making disk calls :( + def post(self): + ''' + :ref:`Authenticate ` against Salt's eauth system +diff --git a/salt/netapi/rest_tornado/saltnado_websockets.py b/salt/netapi/rest_tornado/saltnado_websockets.py +index 89cdfd039a..cf6d51852f 100644 +--- a/salt/netapi/rest_tornado/saltnado_websockets.py ++++ b/salt/netapi/rest_tornado/saltnado_websockets.py +@@ -411,7 +411,7 @@ class FormattedEventsHandler(AllEventsHandler): # pylint: disable=W0223,W0232 + 'tgt': '*', + 'token': self.token, + 'mode': 'client', +- 'async': 'local_async', ++ 'asynchronous': 'local_async', + 'client': 'local' + }) + while True: +diff --git a/salt/returners/cassandra_cql_return.py b/salt/returners/cassandra_cql_return.py +index 8e92e32147..0ec8c2db27 100644 +--- a/salt/returners/cassandra_cql_return.py ++++ b/salt/returners/cassandra_cql_return.py +@@ -204,7 +204,7 @@ def returner(ret): + __salt__['cassandra_cql.cql_query_with_prepare'](query, + 'returner_return', + tuple(statement_arguments), +- async=True) ++ asynchronous=True) + except CommandExecutionError: + log.critical('Could not insert into salt_returns with Cassandra returner.') + raise +@@ -228,7 +228,7 @@ def returner(ret): + __salt__['cassandra_cql.cql_query_with_prepare'](query, + 'returner_minion', + tuple(statement_arguments), +- async=True) ++ asynchronous=True) + except CommandExecutionError: + log.critical('Could not store minion ID with Cassandra returner.') + raise +@@ -270,7 +270,7 @@ def event_return(events): + try: + __salt__['cassandra_cql.cql_query_with_prepare'](query, 'salt_events', + statement_arguments, +- async=True) ++ asynchronous=True) + except CommandExecutionError: + log.critical('Could not store events with Cassandra returner.') + raise +@@ -300,7 +300,7 @@ def save_load(jid, load, minions=None): + try: + __salt__['cassandra_cql.cql_query_with_prepare'](query, 'save_load', + statement_arguments, +- async=True) ++ asynchronous=True) + except CommandExecutionError: + log.critical('Could not save load in jids table.') + raise +diff --git a/salt/runner.py b/salt/runner.py +index 188064665b..ec389a45b0 100644 +--- a/salt/runner.py ++++ b/salt/runner.py +@@ -240,13 +240,13 @@ class Runner(RunnerClient): + if self.opts.get('eauth'): + async_pub = self.cmd_async(low) + else: +- async_pub = self.async(self.opts['fun'], +- low, +- user=user, +- pub=async_pub) ++ async_pub = self.asynchronous(self.opts['fun'], ++ low, ++ user=user, ++ pub=async_pub) + # by default: info will be not enougth to be printed out ! + log.warning( +- 'Running in async mode. Results of this execution may ' ++ 'Running in asynchronous mode. Results of this execution may ' + 'be collected by attaching to the master event bus or ' + 'by examing the master job cache, if configured. ' + 'This execution is running under tag %s', async_pub['tag'] +diff --git a/salt/thorium/runner.py b/salt/thorium/runner.py +index d6235d40e7..9545eac35c 100644 +--- a/salt/thorium/runner.py ++++ b/salt/thorium/runner.py +@@ -1,6 +1,6 @@ + # -*- coding: utf-8 -*- + ''' +-React by calling async runners ++React by calling asynchronous runners + ''' + # Import python libs + from __future__ import absolute_import, print_function, unicode_literals +@@ -14,7 +14,7 @@ def cmd( + arg=(), + **kwargs): + ''' +- Execute a runner async: ++ Execute a runner asynchronous: + + USAGE: + +@@ -42,7 +42,7 @@ def cmd( + func = name + local_opts = {} + local_opts.update(__opts__) +- local_opts['async'] = True # ensure this will be run async ++ local_opts['async'] = True # ensure this will be run asynchronous + local_opts.update({ + 'fun': func, + 'arg': arg, +diff --git a/salt/thorium/wheel.py b/salt/thorium/wheel.py +index 7c98eff4bd..e3c4bf1701 100644 +--- a/salt/thorium/wheel.py ++++ b/salt/thorium/wheel.py +@@ -1,6 +1,6 @@ + # -*- coding: utf-8 -*- + ''' +-React by calling async runners ++React by calling asynchronous runners + ''' + # Import python libs + from __future__ import absolute_import, print_function, unicode_literals +@@ -14,7 +14,7 @@ def cmd( + arg=(), + **kwargs): + ''' +- Execute a runner async: ++ Execute a runner asynchronous: + + USAGE: + +diff --git a/salt/transport/client.py b/salt/transport/client.py +index 86c4962f94..ca83ac9376 100644 +--- a/salt/transport/client.py ++++ b/salt/transport/client.py +@@ -10,7 +10,7 @@ from __future__ import absolute_import, print_function, unicode_literals + import logging + + # Import Salt Libs +-from salt.utils.async import SyncWrapper ++from salt.utils.asynchronous import SyncWrapper + + log = logging.getLogger(__name__) + +diff --git a/salt/transport/ipc.py b/salt/transport/ipc.py +index 108e62da1f..a6e46e8eed 100644 +--- a/salt/transport/ipc.py ++++ b/salt/transport/ipc.py +@@ -130,7 +130,7 @@ class IPCServer(object): + else: + self.sock = tornado.netutil.bind_unix_socket(self.socket_path) + +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + tornado.netutil.add_accept_handler( + self.sock, + self.handle_connection, +@@ -196,7 +196,7 @@ class IPCServer(object): + log.trace('IPCServer: Handling connection ' + 'to address: %s', address) + try: +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + stream = IOStream( + connection, + ) +@@ -329,7 +329,7 @@ class IPCClient(object): + break + + if self.stream is None: +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + self.stream = IOStream( + socket.socket(sock_type, socket.SOCK_STREAM), + ) +@@ -510,7 +510,7 @@ class IPCMessagePublisher(object): + else: + self.sock = tornado.netutil.bind_unix_socket(self.socket_path) + +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + tornado.netutil.add_accept_handler( + self.sock, + self.handle_connection, +@@ -549,7 +549,7 @@ class IPCMessagePublisher(object): + if self.opts['ipc_write_buffer'] > 0: + kwargs['max_write_buffer_size'] = self.opts['ipc_write_buffer'] + log.trace('Setting IPC connection write buffer: %s', (self.opts['ipc_write_buffer'])) +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + stream = IOStream( + connection, + **kwargs +diff --git a/salt/transport/server.py b/salt/transport/server.py +index 46c14bdb39..1d67dc98af 100644 +--- a/salt/transport/server.py ++++ b/salt/transport/server.py +@@ -55,7 +55,7 @@ class ReqServerChannel(object): + ''' + Do anything you need post-fork. This should handle all incoming payloads + and call payload_handler. You will also be passed io_loop, for all of your +- async needs ++ asynchronous needs + ''' + pass + +diff --git a/salt/transport/tcp.py b/salt/transport/tcp.py +index 4b9f14768a..d9c15773a9 100644 +--- a/salt/transport/tcp.py ++++ b/salt/transport/tcp.py +@@ -19,7 +19,7 @@ import traceback + + # Import Salt Libs + import salt.crypt +-import salt.utils.async ++import salt.utils.asynchronous + import salt.utils.event + import salt.utils.files + import salt.utils.platform +@@ -476,7 +476,7 @@ class AsyncTCPPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.tran + 'tok': self.tok, + 'data': data, + 'tag': tag} +- req_channel = salt.utils.async.SyncWrapper( ++ req_channel = salt.utils.asynchronous.SyncWrapper( + AsyncTCPReqChannel, (self.opts,) + ) + try: +@@ -603,7 +603,7 @@ class TCPReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, salt.tra + self.payload_handler = payload_handler + self.io_loop = io_loop + self.serial = salt.payload.Serial(self.opts) +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + if USE_LOAD_BALANCER: + self.req_server = LoadBalancerWorker(self.socket_queue, + self.handle_message, +@@ -869,7 +869,7 @@ class SaltMessageClient(object): + + self.io_loop = io_loop or tornado.ioloop.IOLoop.current() + +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + self._tcp_client = TCPClientKeepAlive(opts, resolver=resolver) + + self._mid = 1 +@@ -895,7 +895,7 @@ class SaltMessageClient(object): + if hasattr(self, '_stream') and not self._stream.closed(): + # If _stream_return() hasn't completed, it means the IO + # Loop is stopped (such as when using +- # 'salt.utils.async.SyncWrapper'). Ensure that ++ # 'salt.utils.asynchronous.SyncWrapper'). Ensure that + # _stream_return() completes by restarting the IO Loop. + # This will prevent potential errors on shutdown. + try: +@@ -969,7 +969,7 @@ class SaltMessageClient(object): + 'source_port': self.source_port} + else: + log.warning('If you need a certain source IP/port, consider upgrading Tornado >= 4.5') +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + self._stream = yield self._tcp_client.connect(self.host, + self.port, + ssl_options=self.opts.get('ssl'), +@@ -1441,9 +1441,9 @@ class TCPPubServerChannel(salt.transport.server.PubServerChannel): + pull_uri = int(self.opts.get('tcp_master_publish_pull', 4514)) + else: + pull_uri = os.path.join(self.opts['sock_dir'], 'publish_pull.ipc') +- # TODO: switch to the actual async interface ++ # TODO: switch to the actual asynchronous interface + #pub_sock = salt.transport.ipc.IPCMessageClient(self.opts, io_loop=self.io_loop) +- pub_sock = salt.utils.async.SyncWrapper( ++ pub_sock = salt.utils.asynchronous.SyncWrapper( + salt.transport.ipc.IPCMessageClient, + (pull_uri,) + ) +diff --git a/salt/utils/async.py b/salt/utils/asynchronous.py +similarity index 81% +rename from salt/utils/async.py +rename to salt/utils/asynchronous.py +index 55d21d0ccc..16a7088360 100644 +--- a/salt/utils/async.py ++++ b/salt/utils/asynchronous.py +@@ -1,6 +1,6 @@ + # -*- coding: utf-8 -*- + ''' +-Helpers/utils for working with tornado async stuff ++Helpers/utils for working with tornado asynchronous stuff + ''' + + from __future__ import absolute_import, print_function, unicode_literals +@@ -30,9 +30,9 @@ class SyncWrapper(object): + + This is uses as a simple wrapper, for example: + +- async = AsyncClass() ++ asynchronous = AsyncClass() + # this method would reguarly return a future +- future = async.async_method() ++ future = asynchronous.async_method() + + sync = SyncWrapper(async_factory_method, (arg1, arg2), {'kwarg1': 'val'}) + # the sync wrapper will automatically wait on the future +@@ -46,15 +46,15 @@ class SyncWrapper(object): + kwargs['io_loop'] = self.io_loop + + with current_ioloop(self.io_loop): +- self.async = method(*args, **kwargs) ++ self.asynchronous = method(*args, **kwargs) + + def __getattribute__(self, key): + try: + return object.__getattribute__(self, key) + except AttributeError as ex: +- if key == 'async': ++ if key == 'asynchronous': + raise ex +- attr = getattr(self.async, key) ++ attr = getattr(self.asynchronous, key) + if hasattr(attr, '__call__'): + def wrap(*args, **kwargs): + # Overload the ioloop for the func call-- since it might call .current() +@@ -75,15 +75,15 @@ class SyncWrapper(object): + + def __del__(self): + ''' +- On deletion of the async wrapper, make sure to clean up the async stuff ++ On deletion of the asynchronous wrapper, make sure to clean up the asynchronous stuff + ''' +- if hasattr(self, 'async'): +- if hasattr(self.async, 'close'): ++ if hasattr(self, 'asynchronous'): ++ if hasattr(self.asynchronous, 'close'): + # Certain things such as streams should be closed before + # their associated io_loop is closed to allow for proper + # cleanup. +- self.async.close() +- del self.async ++ self.asynchronous.close() ++ del self.asynchronous + self.io_loop.close() + del self.io_loop + elif hasattr(self, 'io_loop'): +diff --git a/salt/utils/event.py b/salt/utils/event.py +index 9a62b6c353..a2390730fe 100644 +--- a/salt/utils/event.py ++++ b/salt/utils/event.py +@@ -72,7 +72,7 @@ import tornado.iostream + # Import salt libs + import salt.config + import salt.payload +-import salt.utils.async ++import salt.utils.asynchronous + import salt.utils.cache + import salt.utils.dicttrim + import salt.utils.files +@@ -228,7 +228,7 @@ class SaltEvent(object): + :param Bool keep_loop: Pass a boolean to determine if we want to keep + the io loop or destroy it when the event handle + is destroyed. This is useful when using event +- loops from within third party async code ++ loops from within third party asynchronous code + ''' + self.serial = salt.payload.Serial({'serial': 'msgpack'}) + self.keep_loop = keep_loop +@@ -364,7 +364,7 @@ class SaltEvent(object): + return True + + if self._run_io_loop_sync: +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + if self.subscriber is None: + self.subscriber = salt.transport.ipc.IPCMessageSubscriber( + self.puburi, +@@ -383,7 +383,7 @@ class SaltEvent(object): + io_loop=self.io_loop + ) + +- # For the async case, the connect will be defered to when ++ # For the asynchronous case, the connect will be defered to when + # set_event_handler() is invoked. + self.cpub = True + return self.cpub +@@ -409,7 +409,7 @@ class SaltEvent(object): + return True + + if self._run_io_loop_sync: +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + if self.pusher is None: + self.pusher = salt.transport.ipc.IPCMessageClient( + self.pulluri, +@@ -427,7 +427,7 @@ class SaltEvent(object): + self.pulluri, + io_loop=self.io_loop + ) +- # For the async case, the connect will be deferred to when ++ # For the asynchronous case, the connect will be deferred to when + # fire_event() is invoked. + self.cpush = True + return self.cpush +@@ -632,7 +632,7 @@ class SaltEvent(object): + + ret = self._check_pending(tag, match_func) + if ret is None: +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + if auto_reconnect: + raise_errors = self.raise_errors + self.raise_errors = True +@@ -743,7 +743,7 @@ class SaltEvent(object): + serialized_data]) + msg = salt.utils.stringutils.to_bytes(event, 'utf-8') + if self._run_io_loop_sync: +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + try: + self.io_loop.run_sync(lambda: self.pusher.send(msg)) + except Exception as ex: +@@ -1083,7 +1083,7 @@ class EventPublisher(salt.utils.process.SignalHandlingMultiprocessingProcess): + ''' + salt.utils.process.appendproctitle(self.__class__.__name__) + self.io_loop = tornado.ioloop.IOLoop() +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + if self.opts['ipc_mode'] == 'tcp': + epub_uri = int(self.opts['tcp_master_pub_port']) + epull_uri = int(self.opts['tcp_master_pull_port']) +diff --git a/salt/utils/process.py b/salt/utils/process.py +index 20f7feee8a..95c2288da3 100644 +--- a/salt/utils/process.py ++++ b/salt/utils/process.py +@@ -472,7 +472,7 @@ class ProcessManager(object): + del self._process_map[pid] + + @gen.coroutine +- def run(self, async=False): ++ def run(self, asynchronous=False): + ''' + Load and start all available api modules + ''' +@@ -495,7 +495,7 @@ class ProcessManager(object): + # The event-based subprocesses management code was removed from here + # because os.wait() conflicts with the subprocesses management logic + # implemented in `multiprocessing` package. See #35480 for details. +- if async: ++ if asynchronous: + yield gen.sleep(10) + else: + time.sleep(10) +diff --git a/salt/utils/thin.py b/salt/utils/thin.py +index b99e407583..9a74b8d7d6 100644 +--- a/salt/utils/thin.py ++++ b/salt/utils/thin.py +@@ -701,7 +701,7 @@ def gen_min(cachedir, extra_mods='', overwrite=False, so_mods='', + 'salt/utils/openstack', + 'salt/utils/openstack/__init__.py', + 'salt/utils/openstack/swift.py', +- 'salt/utils/async.py', ++ 'salt/utils/asynchronous.py', + 'salt/utils/process.py', + 'salt/utils/jinja.py', + 'salt/utils/rsax931.py', +diff --git a/salt/wheel/__init__.py b/salt/wheel/__init__.py +index abfd776342..65092ef974 100644 +--- a/salt/wheel/__init__.py ++++ b/salt/wheel/__init__.py +@@ -57,7 +57,7 @@ class WheelClient(salt.client.mixins.SyncClientMixin, + return self.low(fun, kwargs, print_event=kwargs.get('print_event', True), full_return=kwargs.get('full_return', False)) + + # TODO: Inconsistent with runner client-- the runner client's master_call gives +- # an async return, unlike this ++ # an asynchronous return, unlike this + def master_call(self, **kwargs): + ''' + Execute a wheel function through the master network interface (eauth). +@@ -120,7 +120,7 @@ class WheelClient(salt.client.mixins.SyncClientMixin, + {'jid': '20131219224744416681', 'tag': 'salt/wheel/20131219224744416681'} + ''' + fun = low.pop('fun') +- return self.async(fun, low) ++ return self.asynchronous(fun, low) + + def cmd(self, fun, arg=None, pub_data=None, kwarg=None, print_event=True, full_return=False): + ''' +diff --git a/tests/integration/files/engines/runtests_engine.py b/tests/integration/files/engines/runtests_engine.py +index ddb52d5c7f..426ab2a5b2 100644 +--- a/tests/integration/files/engines/runtests_engine.py ++++ b/tests/integration/files/engines/runtests_engine.py +@@ -21,7 +21,7 @@ import logging + + # Import salt libs + import salt.utils.event +-import salt.utils.async ++import salt.utils.asynchronous + + # Import 3rd-party libs + from tornado import gen +@@ -70,7 +70,7 @@ class PyTestEngine(object): + self.sock.bind(('localhost', port)) + # become a server socket + self.sock.listen(5) +- with salt.utils.async.current_ioloop(self.io_loop): ++ with salt.utils.asynchronous.current_ioloop(self.io_loop): + netutil.add_accept_handler( + self.sock, + self.handle_connection, +diff --git a/tests/integration/netapi/rest_tornado/test_app.py b/tests/integration/netapi/rest_tornado/test_app.py +index 2efd2e9f3d..beb085db1e 100644 +--- a/tests/integration/netapi/rest_tornado/test_app.py ++++ b/tests/integration/netapi/rest_tornado/test_app.py +@@ -398,7 +398,7 @@ class TestMinionSaltAPIHandler(_SaltnadoIntegrationTestCase): + + def test_post_with_incorrect_client(self): + ''' +- The /minions endpoint is async only, so if you try something else ++ The /minions endpoint is asynchronous only, so if you try something else + make sure you get an error + ''' + # get a token for this test +diff --git a/tests/support/case.py b/tests/support/case.py +index 9de6b81fb7..87aeb13bf6 100644 +--- a/tests/support/case.py ++++ b/tests/support/case.py +@@ -13,7 +13,7 @@ + # pylint: disable=repr-flag-used-in-string + + # Import python libs +-from __future__ import absolute_import ++from __future__ import absolute_import, unicode_literals + import os + import re + import sys +@@ -143,17 +143,19 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin): + arg_str, + with_retcode=False, + catch_stderr=False, +- async=False, ++ asynchronous=False, + timeout=60, +- config_dir=None): ++ config_dir=None, ++ **kwargs): + ''' + Execute salt-run + ''' ++ asynchronous = kwargs.get('async', asynchronous) + arg_str = '-c {0}{async_flag} -t {timeout} {1}'.format( + config_dir or self.get_config_dir(), + arg_str, + timeout=timeout, +- async_flag=' --async' if async else '') ++ async_flag=' --async' if asynchronous else '') + return self.run_script('salt-run', + arg_str, + with_retcode=with_retcode, +diff --git a/tests/unit/utils/test_async.py b/tests/unit/utils/test_async.py +index c93538f0dd..694a7aebfe 100644 +--- a/tests/unit/utils/test_async.py ++++ b/tests/unit/utils/test_async.py +@@ -8,7 +8,7 @@ import tornado.testing + import tornado.gen + from tornado.testing import AsyncTestCase + +-import salt.utils.async as async ++import salt.utils.asynchronous as asynchronous + + + class HelperA(object): +@@ -24,7 +24,7 @@ class HelperA(object): + class HelperB(object): + def __init__(self, a=None, io_loop=None): + if a is None: +- a = async.SyncWrapper(HelperA) ++ a = asynchronous.SyncWrapper(HelperA) + self.a = a + + @tornado.gen.coroutine +@@ -38,7 +38,7 @@ class TestSyncWrapper(AsyncTestCase): + @tornado.testing.gen_test + def test_helpers(self): + ''' +- Test that the helper classes do what we expect within a regular async env ++ Test that the helper classes do what we expect within a regular asynchronous env + ''' + ha = HelperA() + ret = yield ha.sleep() +@@ -50,29 +50,29 @@ class TestSyncWrapper(AsyncTestCase): + + def test_basic_wrap(self): + ''' +- Test that we can wrap an async caller. ++ Test that we can wrap an asynchronous caller. + ''' +- sync = async.SyncWrapper(HelperA) ++ sync = asynchronous.SyncWrapper(HelperA) + ret = sync.sleep() + self.assertTrue(ret) + + def test_double(self): + ''' +- Test when the async wrapper object itself creates a wrap of another thing ++ Test when the asynchronous wrapper object itself creates a wrap of another thing + + This works fine since the second wrap is based on the first's IOLoop so we + don't have to worry about complex start/stop mechanics + ''' +- sync = async.SyncWrapper(HelperB) ++ sync = asynchronous.SyncWrapper(HelperB) + ret = sync.sleep() + self.assertFalse(ret) + + def test_double_sameloop(self): + ''' +- Test async wrappers initiated from the same IOLoop, to ensure that ++ Test asynchronous wrappers initiated from the same IOLoop, to ensure that + we don't wire up both to the same IOLoop (since it causes MANY problems). + ''' +- a = async.SyncWrapper(HelperA) +- sync = async.SyncWrapper(HelperB, (a,)) ++ a = asynchronous.SyncWrapper(HelperA) ++ sync = asynchronous.SyncWrapper(HelperB, (a,)) + ret = sync.sleep() + self.assertFalse(ret) +-- +2.17.1 + + diff --git a/change-stringio-import-in-python2-to-import-the-clas.patch b/change-stringio-import-in-python2-to-import-the-clas.patch new file mode 100644 index 0000000..8c43a7f --- /dev/null +++ b/change-stringio-import-in-python2-to-import-the-clas.patch @@ -0,0 +1,57 @@ +From a0d5af98c8d2a22c5eb56943ff320ca287fa79ea Mon Sep 17 00:00:00 2001 +From: Florian Bergmann +Date: Tue, 11 Sep 2018 14:03:33 +0200 +Subject: [PATCH] Change StringIO import in python2 to import the class. + (#107) + +Instead of using StringIO in python3, use the correct BytesIO class instead. +--- + salt/modules/hashutil.py | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/salt/modules/hashutil.py b/salt/modules/hashutil.py +index 721957973d..5123cc7cd7 100644 +--- a/salt/modules/hashutil.py ++++ b/salt/modules/hashutil.py +@@ -17,9 +17,10 @@ import salt.utils.hashutils + import salt.utils.stringutils + + if six.PY2: +- import StringIO ++ from StringIO import StringIO ++ BytesIO = StringIO + elif six.PY3: +- from io import StringIO ++ from io import BytesIO, StringIO + + + def digest(instr, checksum='md5'): +@@ -155,13 +156,13 @@ def base64_encodefile(fname): + + salt '*' hashutil.base64_encodefile /path/to/binary_file + ''' +- encoded_f = StringIO.StringIO() ++ encoded_f = BytesIO() + + with salt.utils.files.fopen(fname, 'rb') as f: + base64.encode(f, encoded_f) + + encoded_f.seek(0) +- return encoded_f.read() ++ return salt.utils.stringutils.to_str(encoded_f.read()) + + + def base64_decodestring(instr): +@@ -192,7 +193,7 @@ def base64_decodefile(instr, outfile): + + salt '*' hashutil.base64_decodefile instr='Z2V0IHNhbHRlZAo=' outfile='/path/to/binary_file' + ''' +- encoded_f = StringIO.StringIO(instr) ++ encoded_f = StringIO(instr) + + with salt.utils.files.fopen(outfile, 'wb') as f: + base64.decode(encoded_f, f) +-- +2.19.0 + + diff --git a/decode-file-contents-for-python2-bsc-1102013.patch b/decode-file-contents-for-python2-bsc-1102013.patch new file mode 100644 index 0000000..50b90aa --- /dev/null +++ b/decode-file-contents-for-python2-bsc-1102013.patch @@ -0,0 +1,27 @@ +From 58913b6801b92bd59374cd53fa48fa74171abb73 Mon Sep 17 00:00:00 2001 +From: Abid Mehmood +Date: Wed, 1 Aug 2018 17:19:11 +0200 +Subject: [PATCH] Decode file contents for python2(bsc#1102013) + +--- + salt/states/file.py | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/salt/states/file.py b/salt/states/file.py +index e1d247ae4f..db82098a33 100644 +--- a/salt/states/file.py ++++ b/salt/states/file.py +@@ -1105,8 +1105,7 @@ def _get_template_texts(source_list=None, + tmplines = None + with salt.utils.files.fopen(rndrd_templ_fn, 'rb') as fp_: + tmplines = fp_.read() +- if six.PY3: +- tmplines = tmplines.decode(__salt_system_encoding__) ++ tmplines = tmplines.decode(__salt_system_encoding__) + tmplines = tmplines.splitlines(True) + if not tmplines: + msg = 'Failed to read rendered template file {0} ({1})' +-- +2.17.1 + + diff --git a/fix-for-sorting-of-multi-version-packages-bsc-109717.patch b/fix-for-sorting-of-multi-version-packages-bsc-109717.patch new file mode 100644 index 0000000..bbdc35f --- /dev/null +++ b/fix-for-sorting-of-multi-version-packages-bsc-109717.patch @@ -0,0 +1,35 @@ +From f0f63dc8dd5979b51db71cf759d4350da1078383 Mon Sep 17 00:00:00 2001 +From: Jochen Breuer +Date: Wed, 13 Jun 2018 17:51:13 +0200 +Subject: [PATCH] Fix for sorting of multi-version packages (bsc#1097174 + and bsc#1097413) + +--- + salt/modules/rpm.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/salt/modules/rpm.py b/salt/modules/rpm.py +index 3683234f59..8e71992f81 100644 +--- a/salt/modules/rpm.py ++++ b/salt/modules/rpm.py +@@ -9,6 +9,7 @@ import logging + import os + import re + import datetime ++from distutils.version import LooseVersion + + # Import Salt libs + import salt.utils.decorators.path +@@ -609,7 +610,7 @@ def info(*packages, **kwargs): + # pick only latest versions + # (in case multiple packages installed, e.g. kernel) + ret = dict() +- for pkg_data in reversed(sorted(_ret, key=lambda x: x['edition'])): ++ for pkg_data in reversed(sorted(_ret, key=lambda x: LooseVersion(x['edition']))): + pkg_name = pkg_data.pop('name') + # Filter out GPG public keys packages + if pkg_name.startswith('gpg-pubkey'): +-- +2.17.1 + + diff --git a/fix-for-suse-expanded-support-detection.patch b/fix-for-suse-expanded-support-detection.patch new file mode 100644 index 0000000..0cd6f18 --- /dev/null +++ b/fix-for-suse-expanded-support-detection.patch @@ -0,0 +1,43 @@ +From 1c9cba3a397d53e399b82320507fb5141234c67f Mon Sep 17 00:00:00 2001 +From: Jochen Breuer +Date: Thu, 6 Sep 2018 17:15:18 +0200 +Subject: [PATCH] Fix for SUSE Expanded Support detection + +A SUSE ES installation has both, the centos-release and redhat-release +file. Since os_data only used the centos-release file to detect a +CentOS installation, this lead to SUSE ES being detected as CentOS. + +This change also adds a check for redhat-release and then marks the +'lsb_distrib_id' as RedHat. +--- + salt/grains/core.py | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/salt/grains/core.py b/salt/grains/core.py +index dc472a6c0a..a5c3a6a8cf 100644 +--- a/salt/grains/core.py ++++ b/salt/grains/core.py +@@ -1593,8 +1593,17 @@ def os_data(): + grains['lsb_distrib_codename'] = \ + comps[3].replace('(', '').replace(')', '') + elif os.path.isfile('/etc/centos-release'): +- # CentOS Linux +- grains['lsb_distrib_id'] = 'CentOS' ++ log.trace('Parsing distrib info from /etc/centos-release') ++ # Maybe CentOS Linux; could also be SUSE Expanded Support. ++ # SUSE ES has both, centos-release and redhat-release. ++ if os.path.isfile('/etc/redhat-release'): ++ with salt.utils.files.fopen('/etc/redhat-release') as ifile: ++ for line in ifile: ++ if "red hat enterprise linux server" in line.lower(): ++ # This is a SUSE Expanded Support Rhel installation ++ grains['lsb_distrib_id'] = 'RedHat' ++ break ++ grains.setdefault('lsb_distrib_id', 'CentOS') + with salt.utils.files.fopen('/etc/centos-release') as ifile: + for line in ifile: + # Need to pull out the version and codename +-- +2.19.0 + + diff --git a/fix-mine.get-not-returning-data-workaround-for-48020.patch b/fix-mine.get-not-returning-data-workaround-for-48020.patch new file mode 100644 index 0000000..5d36f29 --- /dev/null +++ b/fix-mine.get-not-returning-data-workaround-for-48020.patch @@ -0,0 +1,34 @@ +From f8c0811c3a05ef334eef1943a906fe01b13c1afc Mon Sep 17 00:00:00 2001 +From: Federico Ceratto +Date: Wed, 25 Jul 2018 10:33:09 +0000 +Subject: [PATCH] Fix mine.get not returning data (Workaround for #48020) + +--- + salt/utils/minions.py | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/salt/utils/minions.py b/salt/utils/minions.py +index c3acc6ba90..bb0cbaa589 100644 +--- a/salt/utils/minions.py ++++ b/salt/utils/minions.py +@@ -239,12 +239,12 @@ class CkMinions(object): + Retreive complete minion list from PKI dir. + Respects cache if configured + ''' +- if self.opts.get('__role') == 'master' and self.opts.get('__cli') == 'salt-run': +- # Compiling pillar directly on the master, just return the master's +- # ID as that is the only one that is available. +- return [self.opts['id']] + minions = [] + pki_cache_fn = os.path.join(self.opts['pki_dir'], self.acc, '.key_cache') ++ try: ++ os.makedirs(os.path.dirname(pki_cache_fn)) ++ except OSError: ++ pass + try: + if self.opts['key_cache'] and os.path.exists(pki_cache_fn): + log.debug('Returning cached minion list') +-- +2.17.1 + + diff --git a/integration-of-msi-authentication-with-azurearm-clou.patch b/integration-of-msi-authentication-with-azurearm-clou.patch new file mode 100644 index 0000000..5afa827 --- /dev/null +++ b/integration-of-msi-authentication-with-azurearm-clou.patch @@ -0,0 +1,118 @@ +From 06aff97c83342cf9635fa750222f774ab1664a0d Mon Sep 17 00:00:00 2001 +From: ed lane +Date: Thu, 30 Aug 2018 06:07:08 -0600 +Subject: [PATCH] Integration of MSI authentication with azurearm cloud + driver (#105) + +--- + salt/cloud/clouds/azurearm.py | 47 +++++++++++++++++++++++++++-------- + 1 file changed, 36 insertions(+), 11 deletions(-) + +diff --git a/salt/cloud/clouds/azurearm.py b/salt/cloud/clouds/azurearm.py +index bd9a25a7e2..8b9a9e8903 100644 +--- a/salt/cloud/clouds/azurearm.py ++++ b/salt/cloud/clouds/azurearm.py +@@ -25,6 +25,9 @@ The Azure cloud module is used to control access to Microsoft Azure + * ``client_id`` + * ``secret`` + ++ if using MSI-style authentication: ++ * ``subscription_id`` ++ + Example ``/etc/salt/cloud.providers`` or + ``/etc/salt/cloud.providers.d/azure.conf`` configuration: + +@@ -48,6 +51,10 @@ Example ``/etc/salt/cloud.providers`` or + For example, this creates a service principal with 'owner' role for the whole subscription: + az ad sp create-for-rbac -n "http://mysaltapp" --role owner --scopes /subscriptions/3287abc8-f98a-c678-3bde-326766fd3617 + *Note: review the details of Service Principals. Owner role is more than you normally need, and you can restrict scope to a resource group or individual resources. ++ ++ Or my-azure-config with MSI-style authentication: ++ driver: azure ++ subscription_id: 3287abc8-f98a-c678-3bde-326766fd3617 + ''' + # pylint: disable=E0102 + +@@ -86,6 +93,7 @@ try: + UserPassCredentials, + ServicePrincipalCredentials, + ) ++ from msrestazure.azure_active_directory import MSIAuthentication + from azure.mgmt.compute import ComputeManagementClient + from azure.mgmt.compute.models import ( + CachingTypes, +@@ -166,19 +174,30 @@ def get_configured_provider(): + ''' + Return the first configured instance. + ''' ++ # check if using Service Principle style authentication... + provider = config.is_provider_configured( + __opts__, + __active_provider_name__ or __virtualname__, +- ('subscription_id', 'tenant', 'client_id', 'secret') ++ required_keys=('subscription_id', 'tenant', 'client_id', 'secret'), ++ log_message=False #... allowed to fail so no need to log warnings + ) + if provider is False: +- return config.is_provider_configured( ++ # check if using username/password style authentication... ++ provider = config.is_provider_configured( + __opts__, + __active_provider_name__ or __virtualname__, +- ('subscription_id', 'username', 'password') ++ required_keys=('subscription_id', 'username', 'password'), ++ log_message=False + ) +- else: +- return provider ++ if provider is False: ++ # check if using MSI style credentials... ++ provider = config.is_provider_configured( ++ __opts__, ++ __active_provider_name__ or __virtualname__, ++ required_keys=('subscription_id',), ++ log_message=False ++ ) ++ return provider + + + def get_dependencies(): +@@ -210,6 +229,7 @@ def get_conn(Client=None): + get_configured_provider(), __opts__, search_global=False + ) + if tenant is not None: ++ # using Service Principle style authentication... + client_id = config.get_cloud_config_value( + 'client_id', + get_configured_provider(), __opts__, search_global=False +@@ -224,15 +244,20 @@ def get_conn(Client=None): + 'username', + get_configured_provider(), __opts__, search_global=False + ) +- password = config.get_cloud_config_value( +- 'password', +- get_configured_provider(), __opts__, search_global=False +- ) +- credentials = UserPassCredentials(username, password) ++ if username is not None: ++ # using username/password style authentication... ++ password = config.get_cloud_config_value( ++ 'password', ++ get_configured_provider(), __opts__, search_global=False ++ ) ++ credentials = UserPassCredentials(username, password) ++ else: ++ # using MSI style authentication ... ++ credentials = MSIAuthentication() + + client = Client( + credentials=credentials, +- subscription_id=subscription_id, ++ subscription_id=str(subscription_id), + ) + client.config.add_user_agent('SaltCloud/{0}'.format(salt.version.__version__)) + return client +-- +2.19.0 + + diff --git a/only-do-reverse-dns-lookup-on-ips-for-salt-ssh.patch b/only-do-reverse-dns-lookup-on-ips-for-salt-ssh.patch new file mode 100644 index 0000000..9f5dc84 --- /dev/null +++ b/only-do-reverse-dns-lookup-on-ips-for-salt-ssh.patch @@ -0,0 +1,29 @@ +From f346e83f6d4651a1cdcaad8c995642b55f66ddbc Mon Sep 17 00:00:00 2001 +From: Daniel Wallace +Date: Wed, 25 Jul 2018 09:48:29 -0500 +Subject: [PATCH] only do reverse dns lookup on ips for salt-ssh + +Fixes #48676 +--- + salt/client/ssh/__init__.py | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py +index 8a85cc2480..d6ff0c3479 100644 +--- a/salt/client/ssh/__init__.py ++++ b/salt/client/ssh/__init__.py +@@ -349,7 +349,9 @@ class SSH(object): + return + + hostname = self.opts['tgt'].split('@')[-1] +- needs_expansion = '*' not in hostname and salt.utils.network.is_reachable_host(hostname) ++ needs_expansion = '*' not in hostname and \ ++ salt.utils.network.is_reachable_host(hostname) and \ ++ salt.utils.network.is_ip(hostname) + if needs_expansion: + hostname = salt.utils.network.ip_to_host(hostname) + if hostname is None: +-- +2.17.1 + + diff --git a/prepend-current-directory-when-path-is-just-filename.patch b/prepend-current-directory-when-path-is-just-filename.patch new file mode 100644 index 0000000..34ea013 --- /dev/null +++ b/prepend-current-directory-when-path-is-just-filename.patch @@ -0,0 +1,38 @@ +From 341ee0c44cabf2f34bdd2f4b54e4b83053a3133e Mon Sep 17 00:00:00 2001 +From: Mihai Dinca +Date: Thu, 23 Aug 2018 16:14:36 +0200 +Subject: [PATCH] Prepend current directory when path is just filename + (bsc#1095942) + +--- + salt/utils/parsers.py | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/salt/utils/parsers.py b/salt/utils/parsers.py +index 5a415ab576..9a7f27ac11 100644 +--- a/salt/utils/parsers.py ++++ b/salt/utils/parsers.py +@@ -591,10 +591,19 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): + ) + ) + ++ def _logfile_callback(option, opt, value, parser, *args, **kwargs): ++ if not os.path.dirname(value): ++ # if the path is only a file name (no parent directory), assume current directory ++ value = os.path.join(os.path.curdir, value) ++ setattr(parser.values, self._logfile_config_setting_name_, value) ++ + group.add_option( + '--log-file', + dest=self._logfile_config_setting_name_, + default=None, ++ action='callback', ++ type='string', ++ callback=_logfile_callback, + help='Log file path. Default: \'{0}\'.'.format( + self._default_logging_logfile_ + ) +-- +2.19.0 + + diff --git a/salt.changes b/salt.changes index e7b7269..013ad21 100644 --- a/salt.changes +++ b/salt.changes @@ -1,3 +1,35 @@ +------------------------------------------------------------------- +Mon Sep 17 13:47:09 UTC 2018 - bo@suse.de + +- Prepend current directory when path is just filename (bsc#1095942) +- Integration of MSI authentication for azurearm +- Adds fix for SUSE Expanded Support os grain detection +- Fixes 509x remote signing +- Fix for StringIO import in Python2 +- Use Adler32 algorithm to compute string checksums (bsc#1102819) +- Only do reverse DNS lookup on IPs for salt-ssh (bsc#1104154) +- Add support for Python 3.7 +- Fix license macro to build on SLE12SP2 +- Decode file contents for python2 (bsc#1102013) +- Fix for sorting of multi-version packages (bsc#1097174 and bsc#1097413) +- Fix mine.get not returning data - workaround for #48020 (bsc#1100142) + +- Added: + * change-stringio-import-in-python2-to-import-the-clas.patch + * integration-of-msi-authentication-with-azurearm-clou.patch + * x509-fixes-for-remote-signing-106.patch + * fix-for-suse-expanded-support-detection.patch + * only-do-reverse-dns-lookup-on-ips-for-salt-ssh.patch + * prepend-current-directory-when-path-is-just-filename.patch + * add-support-for-python-3.7.patch + * decode-file-contents-for-python2-bsc-1102013.patch + * fix-mine.get-not-returning-data-workaround-for-48020.patch + * x509-fixes-111.patch + * use-adler32-algorithm-to-compute-string-checksums.patch + +- Modified: + * fix-for-sorting-of-multi-version-packages-bsc-109717.patch + ------------------------------------------------------------------- Mon Jul 30 10:42:01 UTC 2018 - mihai.dinca@suse.com diff --git a/salt.spec b/salt.spec index 086d54c..a9e53bb 100644 --- a/salt.spec +++ b/salt.spec @@ -126,6 +126,30 @@ Patch29: remove-old-hack-when-reporting-multiversion-packages.patch Patch30: add-engine-relaying-libvirt-events.patch # PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/48781 Patch31: avoid-incomprehensive-message-if-crashes.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/48765 +Patch32: fix-mine.get-not-returning-data-workaround-for-48020.patch +# PATCH-FIX_OPENSUSE bsc#1097174 and bsc#1097413 +Patch33: fix-for-sorting-of-multi-version-packages-bsc-109717.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/48863 +Patch34: decode-file-contents-for-python2-bsc-1102013.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49052 +Patch35: add-support-for-python-3.7.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/48771 +Patch36: only-do-reverse-dns-lookup-on-ips-for-salt-ssh.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49277 +Patch37: prepend-current-directory-when-path-is-just-filename.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49063 +Patch38: integration-of-msi-authentication-with-azurearm-clou.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49538 +Patch39: fix-for-suse-expanded-support-detection.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49508 +Patch40: x509-fixes-for-remote-signing-106.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49555 +Patch41: change-stringio-import-in-python2-to-import-the-clas.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/48812 +Patch42: use-adler32-algorithm-to-compute-string-checksums.patch +# PATCH-FIX_UPSTREAM https://github.com/saltstack/salt/pull/49497 +Patch43: x509-fixes-111.patch # BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -615,6 +639,18 @@ cp %{S:5} ./.travis.yml %patch29 -p1 %patch30 -p1 %patch31 -p1 +%patch32 -p1 +%patch33 -p1 +%patch34 -p1 +%patch35 -p1 +%patch36 -p1 +%patch37 -p1 +%patch38 -p1 +%patch39 -p1 +%patch40 -p1 +%patch41 -p1 +%patch42 -p1 +%patch43 -p1 %build %if 0%{?build_py2} @@ -1256,8 +1292,12 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version %{_mandir}/man1/salt-call.1.gz %{_mandir}/man1/spm.1.gz %config(noreplace) %{_sysconfdir}/logrotate.d/salt +%if 0%{?suse_version} < 1500 +%doc LICENSE AUTHORS README.rst HACKING.rst README.SUSE +%else %license LICENSE %doc AUTHORS README.rst HACKING.rst README.SUSE +%endif # %dir %attr(0750, root, salt) %{_sysconfdir}/salt %dir %attr(0750, root, salt) %{_sysconfdir}/salt/pki diff --git a/use-adler32-algorithm-to-compute-string-checksums.patch b/use-adler32-algorithm-to-compute-string-checksums.patch new file mode 100644 index 0000000..8ff8ef6 --- /dev/null +++ b/use-adler32-algorithm-to-compute-string-checksums.patch @@ -0,0 +1,151 @@ +From 1cb2d2bc6c1cf1a39e735120c184d6ade9e64c34 Mon Sep 17 00:00:00 2001 +From: Bo Maryniuk +Date: Sat, 28 Jul 2018 22:59:04 +0200 +Subject: [PATCH] Use Adler32 algorithm to compute string checksums + +Generate the same numeric value across all Python versions and platforms + +Re-add getting hash by Python shell-out method + +Add an option to choose between default hashing, Adler32 or CRC32 algorithms + +Set default config option for server_id hashing to False on minion + +Choose CRC method, default to faster but less reliable "adler32", if crc is in use + +Add warning for Sodium. +--- + salt/config/__init__.py | 13 +++++++++- + salt/grains/core.py | 54 +++++++++++++++++++++++++++-------------- + 2 files changed, 48 insertions(+), 19 deletions(-) + +diff --git a/salt/config/__init__.py b/salt/config/__init__.py +index feda0abac1..59df7e1cba 100644 +--- a/salt/config/__init__.py ++++ b/salt/config/__init__.py +@@ -1186,6 +1186,16 @@ VALID_OPTS = { + + # Enable calling ssh minions from the salt master + 'enable_ssh_minions': bool, ++ ++ # Thorium saltenv ++ 'thoriumenv': (type(None), six.string_types), ++ ++ # Thorium top file location ++ 'thorium_top': six.string_types, ++ ++ # Use Adler32 hashing algorithm for server_id (default False until Sodium, "adler32" after) ++ # Possible values are: False, adler32, crc32 ++ 'server_id_use_crc': (bool, six.string_types), + } + + # default configurations +@@ -1486,7 +1496,8 @@ DEFAULT_MINION_OPTS = { + }, + 'discovery': False, + 'schedule': {}, +- 'ssh_merge_pillar': True ++ 'ssh_merge_pillar': True, ++ 'server_id_use_crc': False, + } + + DEFAULT_MASTER_OPTS = { +diff --git a/salt/grains/core.py b/salt/grains/core.py +index a5c3a6a8cf..6aaf38096d 100644 +--- a/salt/grains/core.py ++++ b/salt/grains/core.py +@@ -20,6 +20,7 @@ import platform + import logging + import locale + import uuid ++import zlib + from errno import EACCES, EPERM + import datetime + +@@ -46,6 +47,8 @@ import salt.utils.files + import salt.utils.network + import salt.utils.path + import salt.utils.platform ++import salt.utils.stringutils ++import salt.utils.versions + from salt.ext import six + from salt.ext.six.moves import range + +@@ -2420,40 +2423,55 @@ def _hw_data(osdata): + return grains + + +-def get_server_id(): ++def _get_hash_by_shell(): + ''' +- Provides an integer based on the FQDN of a machine. +- Useful as server-id in MySQL replication or anywhere else you'll need an ID +- like this. ++ Shell-out Python 3 for compute reliable hash ++ :return: + ''' +- # Provides: +- # server_id +- +- if salt.utils.platform.is_proxy(): +- return {} + id_ = __opts__.get('id', '') + id_hash = None + py_ver = sys.version_info[:2] + if py_ver >= (3, 3): + # Python 3.3 enabled hash randomization, so we need to shell out to get + # a reliable hash. +- id_hash = __salt__['cmd.run']( +- [sys.executable, '-c', 'print(hash("{0}"))'.format(id_)], +- env={'PYTHONHASHSEED': '0'} +- ) ++ id_hash = __salt__['cmd.run']([sys.executable, '-c', 'print(hash("{0}"))'.format(id_)], ++ env={'PYTHONHASHSEED': '0'}) + try: + id_hash = int(id_hash) + except (TypeError, ValueError): +- log.debug( +- 'Failed to hash the ID to get the server_id grain. Result of ' +- 'hash command: %s', id_hash +- ) ++ log.debug('Failed to hash the ID to get the server_id grain. Result of hash command: %s', id_hash) + id_hash = None + if id_hash is None: + # Python < 3.3 or error encountered above + id_hash = hash(id_) + +- return {'server_id': abs(id_hash % (2 ** 31))} ++ return abs(id_hash % (2 ** 31)) ++ ++ ++def get_server_id(): ++ ''' ++ Provides an integer based on the FQDN of a machine. ++ Useful as server-id in MySQL replication or anywhere else you'll need an ID ++ like this. ++ ''' ++ # Provides: ++ # server_id ++ ++ if salt.utils.platform.is_proxy(): ++ server_id = {} ++ else: ++ use_crc = __opts__.get('server_id_use_crc') ++ if bool(use_crc): ++ id_hash = getattr(zlib, use_crc, zlib.adler32)(__opts__.get('id', '').encode()) & 0xffffffff ++ else: ++ salt.utils.versions.warn_until('Sodium', 'This server_id is computed nor by Adler32 neither by CRC32. ' ++ 'Please use "server_id_use_crc" option and define algorithm you' ++ 'prefer (default "Adler32"). The server_id will be computed with' ++ 'Adler32 by default.') ++ id_hash = _get_hash_by_shell() ++ server_id = {'server_id': id_hash} ++ ++ return server_id + + + def get_master(): +-- +2.19.0 + + diff --git a/x509-fixes-111.patch b/x509-fixes-111.patch new file mode 100644 index 0000000..c34067f --- /dev/null +++ b/x509-fixes-111.patch @@ -0,0 +1,518 @@ +From 053d97afcc7486f7300e339bc56cb3c850cc523b Mon Sep 17 00:00:00 2001 +From: Florian Bergmann +Date: Fri, 14 Sep 2018 10:30:39 +0200 +Subject: [PATCH] X509 fixes (#111) + +* Return proper content type for the x509 certificate + +* Remove parenthesis + +* Remove extra-variables during the import + +* Comment fix + +* Remove double returns + +* Change log level from trace to debug + +* Remove 'pass' and add logging instead + +* Remove unnecessary wrapping + +Remove wrapping + +* PEP 8: line too long + +PEP8: line too long + +* PEP8: Redefine RSAError variable in except clause + +* Do not return None if name was not found + +* Do not return None if no matched minions found + +* Fix unit tests +--- + salt/modules/publish.py | 8 +- + salt/modules/x509.py | 132 ++++++++++++-------------------- + salt/states/x509.py | 22 ++++-- + tests/unit/modules/test_x509.py | 9 ++- + 4 files changed, 74 insertions(+), 97 deletions(-) + +diff --git a/salt/modules/publish.py b/salt/modules/publish.py +index 2de99583f4..ac31b4b65f 100644 +--- a/salt/modules/publish.py ++++ b/salt/modules/publish.py +@@ -83,10 +83,8 @@ def _publish( + in minion configuration but `via_master` was specified.') + else: + # Find the master in the list of master_uris generated by the minion base class +- matching_master_uris = [master for master +- in __opts__['master_uri_list'] +- if '//{0}:'.format(via_master) +- in master] ++ matching_master_uris = [master for master in __opts__['master_uri_list'] ++ if '//{0}:'.format(via_master) in master] + + if not matching_master_uris: + raise SaltInvocationError('Could not find match for {0} in \ +@@ -176,6 +174,8 @@ def _publish( + else: + return ret + ++ return {} ++ + + def publish(tgt, + fun, +diff --git a/salt/modules/x509.py b/salt/modules/x509.py +index 9901bc5bd9..45afcccd99 100644 +--- a/salt/modules/x509.py ++++ b/salt/modules/x509.py +@@ -36,14 +36,13 @@ from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS + # Import 3rd Party Libs + try: + import M2Crypto +- HAS_M2 = True + except ImportError: +- HAS_M2 = False ++ M2Crypto = None ++ + try: + import OpenSSL +- HAS_OPENSSL = True + except ImportError: +- HAS_OPENSSL = False ++ OpenSSL = None + + __virtualname__ = 'x509' + +@@ -81,10 +80,7 @@ def __virtual__(): + ''' + only load this module if m2crypto is available + ''' +- if HAS_M2: +- return __virtualname__ +- else: +- return (False, 'Could not load x509 module, m2crypto unavailable') ++ return __virtualname__ if M2Crypto is not None else False, 'Could not load x509 module, m2crypto unavailable' + + + class _Ctx(ctypes.Structure): +@@ -127,10 +123,8 @@ def _new_extension(name, value, critical=0, issuer=None, _pyfree=1): + doesn't support getting the publickeyidentifier from the issuer + to create the authoritykeyidentifier extension. + ''' +- if name == 'subjectKeyIdentifier' and \ +- value.strip('0123456789abcdefABCDEF:') is not '': +- raise salt.exceptions.SaltInvocationError( +- 'value must be precomputed hash') ++ if name == 'subjectKeyIdentifier' and value.strip('0123456789abcdefABCDEF:') is not '': ++ raise salt.exceptions.SaltInvocationError('value must be precomputed hash') + + # ensure name and value are bytes + name = salt.utils.stringutils.to_str(name) +@@ -145,7 +139,7 @@ def _new_extension(name, value, critical=0, issuer=None, _pyfree=1): + x509_ext_ptr = M2Crypto.m2.x509v3_ext_conf(None, ctx, name, value) + lhash = None + except AttributeError: +- lhash = M2Crypto.m2.x509v3_lhash() # pylint: disable=no-member ++ lhash = M2Crypto.m2.x509v3_lhash() # pylint: disable=no-member + ctx = M2Crypto.m2.x509v3_set_conf_lhash( + lhash) # pylint: disable=no-member + # ctx not zeroed +@@ -196,10 +190,8 @@ def _get_csr_extensions(csr): + csrtempfile.flush() + csryaml = _parse_openssl_req(csrtempfile.name) + csrtempfile.close() +- if csryaml and 'Requested Extensions' in \ +- csryaml['Certificate Request']['Data']: +- csrexts = \ +- csryaml['Certificate Request']['Data']['Requested Extensions'] ++ if csryaml and 'Requested Extensions' in csryaml['Certificate Request']['Data']: ++ csrexts = csryaml['Certificate Request']['Data']['Requested Extensions'] + + if not csrexts: + return ret +@@ -294,7 +286,7 @@ def _get_signing_policy(name): + signing_policy = policies.get(name) + if signing_policy: + return signing_policy +- return __salt__['config.get']('x509_signing_policies', {}).get(name) ++ return __salt__['config.get']('x509_signing_policies', {}).get(name) or {} + + + def _pretty_hex(hex_str): +@@ -321,9 +313,11 @@ def _text_or_file(input_): + ''' + if os.path.isfile(input_): + with salt.utils.files.fopen(input_) as fp_: +- return salt.utils.stringutils.to_str(fp_.read()) ++ out = salt.utils.stringutils.to_str(fp_.read()) + else: +- return salt.utils.stringutils.to_str(input_) ++ out = salt.utils.stringutils.to_str(input_) ++ ++ return out + + + def _parse_subject(subject): +@@ -341,7 +335,7 @@ def _parse_subject(subject): + ret[nid_name] = val + nids.append(nid_num) + except TypeError as err: +- log.trace("Missing attribute '%s'. Error: %s", nid_name, err) ++ log.debug("Missing attribute '%s'. Error: %s", nid_name, err) + + return ret + +@@ -520,8 +514,8 @@ def get_pem_entries(glob_path): + if os.path.isfile(path): + try: + ret[path] = get_pem_entry(text=path) +- except ValueError: +- pass ++ except ValueError as err: ++ log.debug('Unable to get PEM entries from %s: %s', path, err) + + return ret + +@@ -599,8 +593,8 @@ def read_certificates(glob_path): + if os.path.isfile(path): + try: + ret[path] = read_certificate(certificate=path) +- except ValueError: +- pass ++ except ValueError as err: ++ log.debug('Unable to read certificate %s: %s', path, err) + + return ret + +@@ -629,12 +623,10 @@ def read_csr(csr): + # Get size returns in bytes. The world thinks of key sizes in bits. + 'Subject': _parse_subject(csr.get_subject()), + 'Subject Hash': _dec2hex(csr.get_subject().as_hash()), +- 'Public Key Hash': hashlib.sha1(csr.get_pubkey().get_modulus())\ +- .hexdigest() ++ 'Public Key Hash': hashlib.sha1(csr.get_pubkey().get_modulus()).hexdigest(), ++ 'X509v3 Extensions': _get_csr_extensions(csr), + } + +- ret['X509v3 Extensions'] = _get_csr_extensions(csr) +- + return ret + + +@@ -937,7 +929,7 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals + # pyOpenSSL Note due to current limitations in pyOpenSSL it is impossible + # to specify a digest For signing the CRL. This will hopefully be fixed + # soon: https://github.com/pyca/pyopenssl/pull/161 +- if not HAS_OPENSSL: ++ if OpenSSL is None: + raise salt.exceptions.SaltInvocationError( + 'Could not load OpenSSL module, OpenSSL unavailable' + ) +@@ -962,8 +954,7 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals + continue + + if 'revocation_date' not in rev_item: +- rev_item['revocation_date'] = datetime.datetime\ +- .now().strftime('%Y-%m-%d %H:%M:%S') ++ rev_item['revocation_date'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + rev_date = datetime.datetime.strptime( + rev_item['revocation_date'], '%Y-%m-%d %H:%M:%S') +@@ -1002,8 +993,9 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals + try: + crltext = crl.export(**export_kwargs) + except (TypeError, ValueError): +- log.warning( +- 'Error signing crl with specified digest. Are you using pyopenssl 0.15 or newer? The default md5 digest will be used.') ++ log.warning('Error signing crl with specified digest. ' ++ 'Are you using pyopenssl 0.15 or newer? ' ++ 'The default md5 digest will be used.') + export_kwargs.pop('digest', None) + crltext = crl.export(**export_kwargs) + +@@ -1042,8 +1034,7 @@ def sign_remote_certificate(argdic, **kwargs): + if 'signing_policy' in argdic: + signing_policy = _get_signing_policy(argdic['signing_policy']) + if not signing_policy: +- return 'Signing policy {0} does not exist.'.format( +- argdic['signing_policy']) ++ return 'Signing policy {0} does not exist.'.format(argdic['signing_policy']) + + if isinstance(signing_policy, list): + dict_ = {} +@@ -1080,6 +1071,7 @@ def get_signing_policy(signing_policy_name): + signing_policy = _get_signing_policy(signing_policy_name) + if not signing_policy: + return 'Signing policy {0} does not exist.'.format(signing_policy_name) ++ + if isinstance(signing_policy, list): + dict_ = {} + for item in signing_policy: +@@ -1092,10 +1084,9 @@ def get_signing_policy(signing_policy_name): + pass + + try: +- signing_policy['signing_cert'] = get_pem_entry( +- signing_policy['signing_cert'], 'CERTIFICATE') ++ signing_policy['signing_cert'] = get_pem_entry(signing_policy['signing_cert'], 'CERTIFICATE') + except KeyError: +- pass ++ log.debug('Unable to get "certificate" PEM entry') + + return signing_policy + +@@ -1346,8 +1337,7 @@ def create_certificate( + signing_private_key='/etc/pki/myca.key' csr='/etc/pki/myca.csr'} + ''' + +- if not path and not text and \ +- ('testrun' not in kwargs or kwargs['testrun'] is False): ++ if not path and not text and ('testrun' not in kwargs or kwargs['testrun'] is False): + raise salt.exceptions.SaltInvocationError( + 'Either path or text must be specified.') + if path and text: +@@ -1376,8 +1366,7 @@ def create_certificate( + # Including listen_in and preqreuired because they are not included + # in STATE_INTERNAL_KEYWORDS + # for salt 2014.7.2 +- for ignore in list(_STATE_INTERNAL_KEYWORDS) + \ +- ['listen_in', 'preqrequired', '__prerequired__']: ++ for ignore in list(_STATE_INTERNAL_KEYWORDS) + ['listen_in', 'preqrequired', '__prerequired__']: + kwargs.pop(ignore, None) + + certs = __salt__['publish.publish']( +@@ -1484,8 +1473,7 @@ def create_certificate( + continue + + # Use explicitly set values first, fall back to CSR values. +- extval = kwargs.get(extname) or kwargs.get(extlongname) or \ +- csrexts.get(extname) or csrexts.get(extlongname) ++ extval = kwargs.get(extname) or kwargs.get(extlongname) or csrexts.get(extname) or csrexts.get(extlongname) + + critical = False + if extval.startswith('critical '): +@@ -1608,8 +1596,8 @@ def create_csr(path=None, text=False, **kwargs): + + if 'private_key' not in kwargs and 'public_key' in kwargs: + kwargs['private_key'] = kwargs['public_key'] +- log.warning( +- "OpenSSL no longer allows working with non-signed CSRs. A private_key must be specified. Attempting to use public_key as private_key") ++ log.warning("OpenSSL no longer allows working with non-signed CSRs. " ++ "A private_key must be specified. Attempting to use public_key as private_key") + + if 'private_key' not in kwargs: + raise salt.exceptions.SaltInvocationError('private_key is required') +@@ -1621,11 +1609,9 @@ def create_csr(path=None, text=False, **kwargs): + kwargs['private_key_passphrase'] = None + if 'public_key_passphrase' not in kwargs: + kwargs['public_key_passphrase'] = None +- if kwargs['public_key_passphrase'] and not kwargs[ +- 'private_key_passphrase']: ++ if kwargs['public_key_passphrase'] and not kwargs['private_key_passphrase']: + kwargs['private_key_passphrase'] = kwargs['public_key_passphrase'] +- if kwargs['private_key_passphrase'] and not kwargs[ +- 'public_key_passphrase']: ++ if kwargs['private_key_passphrase'] and not kwargs['public_key_passphrase']: + kwargs['public_key_passphrase'] = kwargs['private_key_passphrase'] + + csr.set_pubkey(get_public_key(kwargs['public_key'], +@@ -1669,18 +1655,10 @@ def create_csr(path=None, text=False, **kwargs): + extstack.push(ext) + + csr.add_extensions(extstack) +- + csr.sign(_get_private_key_obj(kwargs['private_key'], + passphrase=kwargs['private_key_passphrase']), kwargs['algorithm']) + +- if path: +- return write_pem( +- text=csr.as_pem(), +- path=path, +- pem_type='CERTIFICATE REQUEST' +- ) +- else: +- return csr.as_pem() ++ return write_pem(text=csr.as_pem(), path=path, pem_type='CERTIFICATE REQUEST') if path else csr.as_pem() + + + def verify_private_key(private_key, public_key, passphrase=None): +@@ -1705,8 +1683,7 @@ def verify_private_key(private_key, public_key, passphrase=None): + salt '*' x509.verify_private_key private_key=/etc/pki/myca.key \\ + public_key=/etc/pki/myca.crt + ''' +- return bool(get_public_key(private_key, passphrase) +- == get_public_key(public_key)) ++ return get_public_key(private_key, passphrase) == get_public_key(public_key) + + + def verify_signature(certificate, signing_pub_key=None, +@@ -1760,9 +1737,8 @@ def verify_crl(crl, cert): + salt '*' x509.verify_crl crl=/etc/pki/myca.crl cert=/etc/pki/myca.crt + ''' + if not salt.utils.path.which('openssl'): +- raise salt.exceptions.SaltInvocationError( +- 'openssl binary not found in path' +- ) ++ raise salt.exceptions.SaltInvocationError('External command "openssl" not found') ++ + crltext = _text_or_file(crl) + crltext = get_pem_entry(crltext, pem_type='X509 CRL') + crltempfile = tempfile.NamedTemporaryFile() +@@ -1783,10 +1759,7 @@ def verify_crl(crl, cert): + crltempfile.close() + certtempfile.close() + +- if 'verify OK' in output: +- return True +- else: +- return False ++ return 'verify OK' in output + + + def expired(certificate): +@@ -1823,8 +1796,9 @@ def expired(certificate): + ret['expired'] = True + else: + ret['expired'] = False +- except ValueError: +- pass ++ except ValueError as err: ++ log.debug('Failed to get data of expired certificate: %s', err) ++ log.trace(err, exc_info=True) + + return ret + +@@ -1847,6 +1821,7 @@ def will_expire(certificate, days): + + salt '*' x509.will_expire "/etc/pki/mycert.crt" days=30 + ''' ++ ts_pt = "%Y-%m-%d %H:%M:%S" + ret = {} + + if os.path.isfile(certificate): +@@ -1856,18 +1831,13 @@ def will_expire(certificate, days): + + cert = _get_certificate_obj(certificate) + +- _check_time = datetime.datetime.utcnow() + \ +- datetime.timedelta(days=days) ++ _check_time = datetime.datetime.utcnow() + datetime.timedelta(days=days) + _expiration_date = cert.get_not_after().get_datetime() + + ret['cn'] = _parse_subject(cert.get_subject())['CN'] +- +- if _expiration_date.strftime("%Y-%m-%d %H:%M:%S") <= \ +- _check_time.strftime("%Y-%m-%d %H:%M:%S"): +- ret['will_expire'] = True +- else: +- ret['will_expire'] = False +- except ValueError: +- pass ++ ret['will_expire'] = _expiration_date.strftime(ts_pt) <= _check_time.strftime(ts_pt) ++ except ValueError as err: ++ log.debug('Unable to return details of a sertificate expiration: %s', err) ++ log.trace(err, exc_info=True) + + return ret +diff --git a/salt/states/x509.py b/salt/states/x509.py +index 7bb941f393..3ba4f79c79 100644 +--- a/salt/states/x509.py ++++ b/salt/states/x509.py +@@ -163,6 +163,7 @@ import copy + + # Import Salt Libs + import salt.exceptions ++import salt.utils.stringutils + + # Import 3rd-party libs + from salt.ext import six +@@ -170,7 +171,7 @@ from salt.ext import six + try: + from M2Crypto.RSA import RSAError + except ImportError: +- pass ++ RSAError = Exception('RSA Error') + + + def __virtual__(): +@@ -180,7 +181,7 @@ def __virtual__(): + if 'x509.get_pem_entry' in __salt__: + return 'x509' + else: +- return (False, 'Could not load x509 state: m2crypto unavailable') ++ return False, 'Could not load x509 state: the x509 is not available' + + + def _revoked_to_list(revs): +@@ -267,7 +268,8 @@ def private_key_managed(name, + + new: + Always create a new key. Defaults to False. +- Combining new with :mod:`prereq `, or when used as part of a `managed_private_key` can allow key rotation whenever a new certificiate is generated. ++ Combining new with :mod:`prereq `, or when used as part of a ++ `managed_private_key` can allow key rotation whenever a new certificiate is generated. + + overwrite: + Overwrite an existing private key if the provided passphrase cannot decrypt it. +@@ -453,8 +455,10 @@ def certificate_managed(name, + private_key_args['name'], pem_type='RSA PRIVATE KEY') + else: + new_private_key = True +- private_key = __salt__['x509.create_private_key'](text=True, bits=private_key_args['bits'], passphrase=private_key_args[ +- 'passphrase'], cipher=private_key_args['cipher'], verbose=private_key_args['verbose']) ++ private_key = __salt__['x509.create_private_key'](text=True, bits=private_key_args['bits'], ++ passphrase=private_key_args['passphrase'], ++ cipher=private_key_args['cipher'], ++ verbose=private_key_args['verbose']) + + kwargs['public_key'] = private_key + +@@ -664,8 +668,10 @@ def crl_managed(name, + else: + current = '{0} does not exist.'.format(name) + +- new_crl = __salt__['x509.create_crl'](text=True, signing_private_key=signing_private_key, signing_private_key_passphrase=signing_private_key_passphrase, +- signing_cert=signing_cert, revoked=revoked, days_valid=days_valid, digest=digest, include_expired=include_expired) ++ new_crl = __salt__['x509.create_crl'](text=True, signing_private_key=signing_private_key, ++ signing_private_key_passphrase=signing_private_key_passphrase, ++ signing_cert=signing_cert, revoked=revoked, days_valid=days_valid, ++ digest=digest, include_expired=include_expired) + + new = __salt__['x509.read_crl'](crl=new_crl) + new_comp = new.copy() +@@ -707,6 +713,6 @@ def pem_managed(name, + Any arguments supported by :state:`file.managed ` are supported. + ''' + file_args, kwargs = _get_file_args(name, **kwargs) +- file_args['contents'] = __salt__['x509.get_pem_entry'](text=text) ++ file_args['contents'] = salt.utils.stringutils.to_str(__salt__['x509.get_pem_entry'](text=text)) + + return __states__['file.managed'](**file_args) +diff --git a/tests/unit/modules/test_x509.py b/tests/unit/modules/test_x509.py +index c300a56d64..7e00c97140 100644 +--- a/tests/unit/modules/test_x509.py ++++ b/tests/unit/modules/test_x509.py +@@ -67,10 +67,11 @@ class X509TestCase(TestCase, LoaderModuleMockMixin): + + subj = FakeSubject() + x509._parse_subject(subj) +- x509.log.trace.assert_called_once() +- assert x509.log.trace.call_args[0][0] == "Missing attribute '%s'. Error: %s" +- assert x509.log.trace.call_args[0][1] == list(subj.nid.keys())[0] +- assert isinstance(x509.log.trace.call_args[0][2], TypeError) ++ x509.log.debug.assert_called_once() ++ ++ assert x509.log.debug.call_args[0][0] == "Missing attribute '%s'. Error: %s" ++ assert x509.log.debug.call_args[0][1] == list(subj.nid.keys())[0] ++ assert isinstance(x509.log.debug.call_args[0][2], TypeError) + + @skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypt is unavailble') + def test_get_pem_entry(self): +-- +2.19.0 + + diff --git a/x509-fixes-for-remote-signing-106.patch b/x509-fixes-for-remote-signing-106.patch new file mode 100644 index 0000000..d9507c2 --- /dev/null +++ b/x509-fixes-for-remote-signing-106.patch @@ -0,0 +1,80 @@ +From 6276eb2cd3f2b396c13118a111998230477cc65a Mon Sep 17 00:00:00 2001 +From: Florian Bergmann +Date: Tue, 11 Sep 2018 14:02:55 +0200 +Subject: [PATCH] X509 fixes for remote signing (#106) + +* Use to_str salt.utils when writing to a file. + +* Assign the certificate as a string. + +* Convert to string before sending via 'publish'. + +Otherwise the publish call with receive a "b''" string, which can not be used +in the functions. + +* Do not silently ignore errors. + +At least log the occurring errors to debug and trace. +--- + salt/modules/x509.py | 10 +++++----- + salt/states/x509.py | 2 +- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/salt/modules/x509.py b/salt/modules/x509.py +index 15de06e200..9901bc5bd9 100644 +--- a/salt/modules/x509.py ++++ b/salt/modules/x509.py +@@ -658,7 +658,7 @@ def read_crl(crl): + text = get_pem_entry(text, pem_type='X509 CRL') + + crltempfile = tempfile.NamedTemporaryFile() +- crltempfile.write(text) ++ crltempfile.write(salt.utils.stringutils.to_str(text)) + crltempfile.flush() + crlparsed = _parse_openssl_crl(crltempfile.name) + crltempfile.close() +@@ -1368,9 +1368,9 @@ def create_certificate( + pem_type='CERTIFICATE REQUEST').replace('\n', '') + if 'public_key' in kwargs: + # Strip newlines to make passing through as cli functions easier +- kwargs['public_key'] = get_public_key( ++ kwargs['public_key'] = salt.utils.stringutils.to_str(get_public_key( + kwargs['public_key'], +- passphrase=kwargs['public_key_passphrase']).replace('\n', '') ++ passphrase=kwargs['public_key_passphrase'])).replace('\n', '') + + # Remove system entries in kwargs + # Including listen_in and preqreuired because they are not included +@@ -1766,13 +1766,13 @@ def verify_crl(crl, cert): + crltext = _text_or_file(crl) + crltext = get_pem_entry(crltext, pem_type='X509 CRL') + crltempfile = tempfile.NamedTemporaryFile() +- crltempfile.write(crltext) ++ crltempfile.write(salt.utils.stringutils.to_str(crltext)) + crltempfile.flush() + + certtext = _text_or_file(cert) + certtext = get_pem_entry(certtext, pem_type='CERTIFICATE') + certtempfile = tempfile.NamedTemporaryFile() +- certtempfile.write(certtext) ++ certtempfile.write(salt.utils.stringutils.to_str(certtext)) + certtempfile.flush() + + cmd = ('openssl crl -noout -in {0} -CAfile {1}'.format( +diff --git a/salt/states/x509.py b/salt/states/x509.py +index 832f74168c..7bb941f393 100644 +--- a/salt/states/x509.py ++++ b/salt/states/x509.py +@@ -545,7 +545,7 @@ def certificate_managed(name, + if not private_ret['result']: + return private_ret + +- file_args['contents'] += certificate ++ file_args['contents'] += salt.utils.stringutils.to_str(certificate) + + if not append_certs: + append_certs = [] +-- +2.19.0 + +