Merge branch 'nitrogen' into 'develop'

Conflicts:
  - salt/states/keystone.py
This commit is contained in:
rallytime 2017-06-06 12:50:35 -06:00
commit 2cb4f2baa9
32 changed files with 264 additions and 97 deletions

View File

@ -1584,6 +1584,7 @@ class LocalClient(object):
expect_minions=(verbose or show_timeout), expect_minions=(verbose or show_timeout),
**kwargs **kwargs
): ):
log.debug('return event: %s', ret)
return_count = return_count + 1 return_count = return_count + 1
if progress: if progress:
for id_, min_ret in six.iteritems(ret): for id_, min_ret in six.iteritems(ret):

View File

@ -42,12 +42,12 @@ Example of usage
.. code:: txt .. code:: txt
08:33:57 @gtmanfred » !ping 08:33:57 @gtmanfred > !ping
08:33:57 gtmanbot » gtmanfred: pong 08:33:57 gtmanbot > gtmanfred: pong
08:34:02 @gtmanfred » !echo ping 08:34:02 @gtmanfred > !echo ping
08:34:02 gtmanbot » ping 08:34:02 gtmanbot > ping
08:34:17 @gtmanfred » !event test/tag/ircbot irc is usefull 08:34:17 @gtmanfred > !event test/tag/ircbot irc is usefull
08:34:17 gtmanbot » gtmanfred: TaDa! 08:34:17 gtmanbot > gtmanfred: TaDa!
.. code:: txt .. code:: txt

View File

@ -28,7 +28,7 @@ of the following fields:
10. raw (the raw event data forwarded from the device) 10. raw (the raw event data forwarded from the device)
The topic title can consist of any of the combination of above fields, The topic title can consist of any of the combination of above fields,
but the topic has to start with jnpr/syslog. but the topic has to start with 'jnpr/syslog'.
So, we can have different combinations: So, we can have different combinations:
- jnpr/syslog/hostip/daemon/event - jnpr/syslog/hostip/daemon/event
- jnpr/syslog/daemon/severity - jnpr/syslog/daemon/severity
@ -37,7 +37,7 @@ The corresponding dynamic topic sent on salt event bus would look something like
- jnpr/syslog/1.1.1.1/mgd/UI_COMMIT_COMPLETED - jnpr/syslog/1.1.1.1/mgd/UI_COMMIT_COMPLETED
- jnpr/syslog/sshd/7 - jnpr/syslog/sshd/7
The default topic title is jnpr/syslog/hostname/event. The default topic title is 'jnpr/syslog/hostname/event'.
The user can choose the type of data he/she wants of the event bus. The user can choose the type of data he/she wants of the event bus.
Like, if one wants only events pertaining to a particular daemon, he/she can Like, if one wants only events pertaining to a particular daemon, he/she can

View File

@ -592,20 +592,37 @@ class Client(object):
destfp = None destfp = None
try: try:
# Tornado calls streaming_callback on redirect response bodies. # Tornado calls streaming_callback on redirect response bodies.
# But we need streaming to support fetching large files (> RAM avail). # But we need streaming to support fetching large files (> RAM
# Here we working this around by disabling recording the body for redirections. # avail). Here we are working around this by disabling recording
# The issue is fixed in Tornado 4.3.0 so on_header callback could be removed # the body for redirections. The issue is fixed in Tornado 4.3.0
# when we'll deprecate Tornado<4.3.0. # so on_header callback could be removed when we'll deprecate
# See #27093 and #30431 for details. # Tornado<4.3.0. See #27093 and #30431 for details.
# Use list here to make it writable inside the on_header callback. Simple bool doesn't # Use list here to make it writable inside the on_header callback.
# work here: on_header creates a new local variable instead. This could be avoided in # Simple bool doesn't work here: on_header creates a new local
# Py3 with 'nonlocal' statement. There is no Py2 alternative for this. # variable instead. This could be avoided in Py3 with 'nonlocal'
# statement. There is no Py2 alternative for this.
#
# write_body[0] is used by the on_chunk callback to tell it whether
# or not we need to write the body of the request to disk. For
# 30x redirects we set this to False because we don't want to
# write the contents to disk, as we will need to wait until we
# get to the redirected URL.
#
# write_body[1] will contain a tornado.httputil.HTTPHeaders
# instance that we will use to parse each header line. We
# initialize this to False, and after we parse the status line we
# will replace it with the HTTPHeaders instance. If/when we have
# found the encoding used in the request, we set this value to
# False to signify that we are done parsing.
#
# write_body[2] is where the encoding will be stored
write_body = [None, False, None] write_body = [None, False, None]
def on_header(hdr): def on_header(hdr):
if write_body[1] is not False and write_body[2] is None: if write_body[1] is not False and write_body[2] is None:
# Try to find out what content type encoding is used if this is a text file # Try to find out what content type encoding is used if
# this is a text file
write_body[1].parse_line(hdr) # pylint: disable=no-member write_body[1].parse_line(hdr) # pylint: disable=no-member
if 'Content-Type' in write_body[1]: if 'Content-Type' in write_body[1]:
content_type = write_body[1].get('Content-Type') # pylint: disable=no-member content_type = write_body[1].get('Content-Type') # pylint: disable=no-member
@ -621,17 +638,22 @@ class Client(object):
# We have found our encoding. Stop processing headers. # We have found our encoding. Stop processing headers.
write_body[1] = False write_body[1] = False
if write_body[0] is not None: # If write_body[0] is False, this means that this
# We already parsed the first line. No need to run the code below again # header is a 30x redirect, so we need to reset
return # write_body[0] to None so that we parse the HTTP
# status code from the redirect target.
if write_body[0] is write_body[1] is False:
write_body[0] = None
try: # Check the status line of the HTTP request
hdr = parse_response_start_line(hdr) if write_body[0] is None:
except HTTPInputError: try:
# Not the first line, do nothing hdr = parse_response_start_line(hdr)
return except HTTPInputError:
write_body[0] = hdr.code not in [301, 302, 303, 307] # Not the first line, do nothing
write_body[1] = HTTPHeaders() return
write_body[0] = hdr.code not in [301, 302, 303, 307]
write_body[1] = HTTPHeaders()
if no_cache: if no_cache:
result = [] result = []

View File

@ -1538,6 +1538,7 @@ class Minion(MinionBase):
else: else:
data['ret'] = opts['return'] data['ret'] = opts['return']
log.debug('minion return: %s', ret)
# TODO: make a list? Seems odd to split it this late :/ # TODO: make a list? Seems odd to split it this late :/
if data['ret'] and isinstance(data['ret'], six.string_types): if data['ret'] and isinstance(data['ret'], six.string_types):
if 'ret_config' in data: if 'ret_config' in data:

View File

@ -296,7 +296,7 @@ def ec2_credentials_list(user_id=None, name=None, profile=None,
return ret return ret
def endpoint_get(service, region=None, profile=None, **connection_args): def endpoint_get(service, region=None, profile=None, interface=None, **connection_args):
''' '''
Return a specific endpoint (keystone endpoint-get) Return a specific endpoint (keystone endpoint-get)
@ -304,7 +304,9 @@ def endpoint_get(service, region=None, profile=None, **connection_args):
.. code-block:: bash .. code-block:: bash
salt '*' keystone.endpoint_get nova [region=RegionOne] salt 'v2' keystone.endpoint_get nova [region=RegionOne]
salt 'v3' keystone.endpoint_get nova interface=admin [region=RegionOne]
''' '''
auth(profile, **connection_args) auth(profile, **connection_args)
services = service_list(profile, **connection_args) services = service_list(profile, **connection_args)
@ -315,12 +317,13 @@ def endpoint_get(service, region=None, profile=None, **connection_args):
e = [_f for _f in [e e = [_f for _f in [e
if e['service_id'] == service_id and if e['service_id'] == service_id and
(e['region'] == region if region else True) else None for e in endpoints.values()] if _f] (e['region'] == region if region else True) and
(e['interface'] == interface if interface else True)
else None for e in endpoints.values()] if _f]
if len(e) > 1: if len(e) > 1:
return {'Error': 'Multiple endpoints found ({0}) for the {1} service. Please specify region.'.format(e, service)} return {'Error': 'Multiple endpoints found ({0}) for the {1} service. Please specify region.'.format(e, service)}
if len(e) == 1: if len(e) == 1:
return e[0] return e[0]
#log.debug('Could not find endpoint for the specified service {0}, service_id: {3} and region {1} in endpoints {2}'.format(service, region, endpoints.values(), service_id))
return {'Error': 'Could not find endpoint for the specified service'} return {'Error': 'Could not find endpoint for the specified service'}
@ -374,10 +377,10 @@ def endpoint_create(service, publicurl=None, internalurl=None, adminurl=None,
publicurl=publicurl, publicurl=publicurl,
adminurl=adminurl, adminurl=adminurl,
internalurl=internalurl) internalurl=internalurl)
return endpoint_get(service, region, profile, **connection_args) return endpoint_get(service, region, profile, interface, **connection_args)
def endpoint_delete(service, region=None, profile=None, **connection_args): def endpoint_delete(service, region=None, profile=None, interface=None, **connection_args):
''' '''
Delete endpoints of an Openstack service Delete endpoints of an Openstack service
@ -385,14 +388,16 @@ def endpoint_delete(service, region=None, profile=None, **connection_args):
.. code-block:: bash .. code-block:: bash
salt '*' keystone.endpoint_delete nova [region=RegionOne] salt 'v2' keystone.endpoint_delete nova [region=RegionOne]
salt 'v3' keystone.endpoint_delete nova interface=admin [region=RegionOne]
''' '''
kstone = auth(profile, **connection_args) kstone = auth(profile, **connection_args)
endpoint = endpoint_get(service, region, profile, **connection_args) endpoint = endpoint_get(service, region, profile, interface, **connection_args)
if not endpoint or 'Error' in endpoint: if not endpoint or 'Error' in endpoint:
return {'Error': 'Could not find any endpoints for the service'} return {'Error': 'Could not find any endpoints for the service'}
kstone.endpoints.delete(endpoint['id']) kstone.endpoints.delete(endpoint['id'])
endpoint = endpoint_get(service, region, profile, **connection_args) endpoint = endpoint_get(service, region, profile, interface, **connection_args)
if not endpoint or 'Error' in endpoint: if not endpoint or 'Error' in endpoint:
return True return True

View File

@ -117,8 +117,12 @@ def bin_pkg_info(path, saltenv='base'):
output, output,
osarch=__grains__['osarch'] osarch=__grains__['osarch']
) )
for field in pkginfo._fields: try:
ret[field] = getattr(pkginfo, field) for field in pkginfo._fields:
ret[field] = getattr(pkginfo, field)
except AttributeError:
# pkginfo is None
return None
return ret return ret

View File

@ -5,6 +5,7 @@ Module for running arbitrary tests
from __future__ import absolute_import from __future__ import absolute_import
# Import Python libs # Import Python libs
import logging
import os import os
import sys import sys
import time import time
@ -27,6 +28,8 @@ __func_alias__ = {
'false_': 'false' 'false_': 'false'
} }
log = logging.getLogger(__name__)
@depends('non_existantmodulename') @depends('non_existantmodulename')
def missing_func(): def missing_func():
@ -112,6 +115,7 @@ def ping():
''' '''
if not salt.utils.is_proxy(): if not salt.utils.is_proxy():
log.debug('test.ping received for minion \'%s\'', __opts__.get('id'))
return True return True
else: else:
ping_cmd = __opts__['proxy']['proxytype'] + '.ping' ping_cmd = __opts__['proxy']['proxytype'] + '.ping'

View File

@ -981,21 +981,24 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals
OpenSSL.crypto.FILETYPE_PEM, OpenSSL.crypto.FILETYPE_PEM,
get_pem_entry(signing_private_key)) get_pem_entry(signing_private_key))
export_kwargs = {
'cert': cert,
'key': key,
'type': OpenSSL.crypto.FILETYPE_PEM,
'days': days_valid
}
if digest:
export_kwargs['digest'] = bytes(digest)
else:
log.warning('No digest specified. The default md5 digest will be used.')
try: try:
crltext = crl.export( crltext = crl.export(**export_kwargs)
cert, except (TypeError, ValueError):
key,
OpenSSL.crypto.FILETYPE_PEM,
days=days_valid,
digest=bytes(digest))
except TypeError:
log.warning( log.warning(
'Error signing crl with specified digest. Are you using pyopenssl 0.15 or newer? The default md5 digest will be used.') 'Error signing crl with specified digest. Are you using pyopenssl 0.15 or newer? The default md5 digest will be used.')
crltext = crl.export( export_kwargs.pop('digest', None)
cert, crltext = crl.export(**export_kwargs)
key,
OpenSSL.crypto.FILETYPE_PEM,
days=days_valid)
if text: if text:
return crltext return crltext

View File

@ -82,10 +82,10 @@ from __future__ import absolute_import
import yaml import yaml
import pprint import pprint
import logging import logging
import urllib
# pylint: disable=import-error,no-name-in-module,redefined-builtin # pylint: disable=import-error,no-name-in-module,redefined-builtin
import salt.ext.six.moves.http_client import salt.ext.six.moves.http_client
from salt.ext.six.moves.urllib.parse import urlencode as _urlencode
# pylint: enable=import-error,no-name-in-module,redefined-builtin # pylint: enable=import-error,no-name-in-module,redefined-builtin
# Import Salt Libs # Import Salt Libs
@ -168,7 +168,7 @@ def _post_message(channel,
api_key=api_key, api_key=api_key,
method='POST', method='POST',
header_dict={'Content-Type': 'application/x-www-form-urlencoded'}, header_dict={'Content-Type': 'application/x-www-form-urlencoded'},
data=urllib.urlencode(parameters)) data=_urlencode(parameters))
log.debug('result {0}'.format(result)) log.debug('result {0}'.format(result))
if result: if result:

View File

@ -55,7 +55,6 @@ def _ping(tgt, tgt_type, timeout, gather_job_timeout):
tgt_type, tgt_type,
gather_job_timeout=gather_job_timeout): gather_job_timeout=gather_job_timeout):
log.debug('fn_ret: %s', fn_ret)
if fn_ret: if fn_ret:
for mid, _ in six.iteritems(fn_ret): for mid, _ in six.iteritems(fn_ret):
log.debug('minion \'%s\' returned from ping', mid) log.debug('minion \'%s\' returned from ping', mid)

View File

@ -24,6 +24,7 @@ from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: dis
# Import salt libs # Import salt libs
import salt.utils import salt.utils
import salt.utils.files import salt.utils.files
import salt.utils.url
from salt.exceptions import CommandExecutionError, CommandNotFoundError from salt.exceptions import CommandExecutionError, CommandNotFoundError
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -888,7 +889,9 @@ def extracted(name,
ret['result'] = None ret['result'] = None
ret['comment'] = ( ret['comment'] = (
'Archive {0} would be downloaded to cache and checked to ' 'Archive {0} would be downloaded to cache and checked to '
'discover if extraction is necessary'.format(source_match) 'discover if extraction is necessary'.format(
salt.utils.url.redact_http_basic_auth(source_match)
)
) )
return ret return ret
@ -918,18 +921,27 @@ def extracted(name,
try: try:
if not file_result['result']: if not file_result['result']:
log.debug('failed to download {0}'.format(source_match)) log.debug(
'failed to download %s',
salt.utils.url.redact_http_basic_auth(source_match)
)
return file_result return file_result
except TypeError: except TypeError:
if not file_result: if not file_result:
log.debug('failed to download {0}'.format(source_match)) log.debug(
'failed to download %s',
salt.utils.url.redact_http_basic_auth(source_match)
)
return file_result return file_result
if source_hash: if source_hash:
_update_checksum(cached_source) _update_checksum(cached_source)
else: else:
log.debug('Archive %s is already in cache', source_match) log.debug(
'Archive %s is already in cache',
salt.utils.url.redact_http_basic_auth(source_match)
)
if archive_format == 'zip' and not password: if archive_format == 'zip' and not password:
log.debug('Checking %s to see if it is password-protected', log.debug('Checking %s to see if it is password-protected',
@ -950,7 +962,7 @@ def extracted(name,
ret['comment'] = ( ret['comment'] = (
'Archive {0} is password-protected, but no password was ' 'Archive {0} is password-protected, but no password was '
'specified. Please set the \'password\' argument.'.format( 'specified. Please set the \'password\' argument.'.format(
source_match salt.utils.url.redact_http_basic_auth(source_match)
) )
) )
return ret return ret
@ -1138,7 +1150,7 @@ def extracted(name,
ret['result'] = None ret['result'] = None
ret['comment'] = \ ret['comment'] = \
'Archive {0} would be extracted to {1}'.format( 'Archive {0} would be extracted to {1}'.format(
source_match, salt.utils.url.redact_http_basic_auth(source_match),
name name
) )
if clean and contents is not None: if clean and contents is not None:
@ -1421,13 +1433,18 @@ def extracted(name,
if created_destdir: if created_destdir:
ret['changes']['directories_created'] = [name] ret['changes']['directories_created'] = [name]
ret['changes']['extracted_files'] = files ret['changes']['extracted_files'] = files
ret['comment'] = '{0} extracted to {1}'.format(source_match, name) ret['comment'] = '{0} extracted to {1}'.format(
salt.utils.url.redact_http_basic_auth(source_match),
name,
)
_add_explanation(ret, source_hash_trigger, contents_missing) _add_explanation(ret, source_hash_trigger, contents_missing)
ret['result'] = True ret['result'] = True
else: else:
ret['result'] = False ret['result'] = False
ret['comment'] = 'Can\'t extract content of {0}'.format(source_match) ret['comment'] = 'No files were extracted from {0}'.format(
salt.utils.url.redact_http_basic_auth(source_match)
)
else: else:
ret['result'] = True ret['result'] = True
if if_missing_path_exists: if if_missing_path_exists:

View File

@ -656,6 +656,7 @@ def endpoint_present(name,
endpoint = __salt__['keystone.endpoint_get'](name, region, endpoint = __salt__['keystone.endpoint_get'](name, region,
profile=profile, profile=profile,
interface=interface,
**connection_args) **connection_args)
def _changes(desc): def _changes(desc):
@ -750,7 +751,7 @@ def endpoint_present(name,
ret['changes']['internalurl'] = internalurl ret['changes']['internalurl'] = internalurl
if ret['comment']: # changed if ret['comment']: # changed
__salt__['keystone.endpoint_delete'](name, region, profile=profile, **connection_args) __salt__['keystone.endpoint_delete'](name, region, profile=profile, interface=interface, **connection_args)
_create_endpoint() _create_endpoint()
ret['comment'] += 'Endpoint for service "{0}" has been updated'.format(name) ret['comment'] += 'Endpoint for service "{0}" has been updated'.format(name)
@ -765,26 +766,34 @@ def endpoint_present(name,
ret['comment'] = 'Endpoint for service "{0}" has been added'.format(name) ret['comment'] = 'Endpoint for service "{0}" has been added'.format(name)
if ret['comment'] == '': # => no changes if ret['comment'] == '': # => no changes
ret['result'] = None
ret['comment'] = 'Endpoint for service "{0}" already exists'.format(name) ret['comment'] = 'Endpoint for service "{0}" already exists'.format(name)
return ret return ret
def endpoint_absent(name, region=None, profile=None, **connection_args): def endpoint_absent(name, region=None, profile=None, interface=None, **connection_args):
''' '''
Ensure that the endpoint for a service doesn't exist in Keystone catalog Ensure that the endpoint for a service doesn't exist in Keystone catalog
name name
The name of the service whose endpoints should not exist The name of the service whose endpoints should not exist
region (optional)
The region of the endpoint. Defaults to ``RegionOne``.
interface
The interface type, which describes the visibility
of the endpoint. (for V3 API)
''' '''
ret = {'name': name, ret = {'name': name,
'changes': {}, 'changes': {},
'result': True, 'result': True,
'comment': 'Endpoint for service "{0}" is already absent'.format(name)} 'comment': 'Endpoint for service "{0}"{1} is already absent'.format(name,
', interface "{0}",'.format(interface) if interface is not None else '')}
# Check if service is present # Check if service is present
endpoint = __salt__['keystone.endpoint_get'](name, region, endpoint = __salt__['keystone.endpoint_get'](name, region,
profile=profile, profile=profile,
interface=interface,
**connection_args) **connection_args)
if not endpoint: if not endpoint:
return ret return ret
@ -796,7 +805,9 @@ def endpoint_absent(name, region=None, profile=None, **connection_args):
# Delete service # Delete service
__salt__['keystone.endpoint_delete'](name, region, __salt__['keystone.endpoint_delete'](name, region,
profile=profile, profile=profile,
interface=interface,
**connection_args) **connection_args)
ret['comment'] = 'Endpoint for service "{0}" has been deleted'.format(name) ret['comment'] = 'Endpoint for service "{0}"{1} has been deleted'.format(name,
', interface "{0}",'.format(interface) if interface is not None else '')
ret['changes']['endpoint'] = 'Deleted' ret['changes']['endpoint'] = 'Deleted'
return ret return ret

View File

@ -784,10 +784,13 @@ def _find_install_targets(name=None,
' and are at the desired version' if version_spec and not sources ' and are at the desired version' if version_spec and not sources
else '' else ''
) )
return {'name': name, ret = {'name': name,
'changes': {}, 'changes': {},
'result': True, 'result': True,
'comment': msg} 'comment': msg}
if warnings:
ret.setdefault('warnings', []).extend(warnings)
return ret
return (desired, targets, to_unpurge, to_reinstall, altered_files, return (desired, targets, to_unpurge, to_reinstall, altered_files,
warnings, was_refreshed) warnings, was_refreshed)
@ -1529,7 +1532,7 @@ def installed(
'result': False, 'result': False,
'comment': 'lowpkg.unpurge not implemented'} 'comment': 'lowpkg.unpurge not implemented'}
if warnings: if warnings:
ret['comment'] += '.' + '. '.join(warnings) + '.' ret.setdefault('warnings', []).extend(warnings)
return ret return ret
# Remove any targets not returned by _find_install_targets # Remove any targets not returned by _find_install_targets
@ -1592,7 +1595,7 @@ def installed(
'result': None, 'result': None,
'comment': '\n'.join(comment)} 'comment': '\n'.join(comment)}
if warnings: if warnings:
ret['comment'] += '\n' + '. '.join(warnings) + '.' ret.setdefault('warnings', []).extend(warnings)
return ret return ret
changes = {'installed': {}} changes = {'installed': {}}
@ -1627,7 +1630,7 @@ def installed(
ret['comment'] = ('An error was encountered while installing ' ret['comment'] = ('An error was encountered while installing '
'package(s): {0}'.format(exc)) 'package(s): {0}'.format(exc))
if warnings: if warnings:
ret['comment'] += '\n\n' + '. '.join(warnings) + '.' ret.setdefault('warnings', []).extend(warnings)
return ret return ret
if refresh: if refresh:
@ -1662,7 +1665,7 @@ def installed(
'result': False, 'result': False,
'comment': '\n'.join(comment)} 'comment': '\n'.join(comment)}
if warnings: if warnings:
ret['comment'] += '.' + '. '.join(warnings) + '.' ret.setdefault('warnings', []).extend(warnings)
return ret return ret
else: else:
if 'result' in hold_ret and not hold_ret['result']: if 'result' in hold_ret and not hold_ret['result']:
@ -1673,7 +1676,7 @@ def installed(
'holding/unholding package(s): {0}' 'holding/unholding package(s): {0}'
.format(hold_ret['comment'])} .format(hold_ret['comment'])}
if warnings: if warnings:
ret['comment'] += '.' + '. '.join(warnings) + '.' ret.setdefault('warnings', []).extend(warnings)
return ret return ret
else: else:
modified_hold = [hold_ret[x] for x in hold_ret modified_hold = [hold_ret[x] for x in hold_ret
@ -1863,7 +1866,7 @@ def installed(
'result': result, 'result': result,
'comment': '\n'.join(comment)} 'comment': '\n'.join(comment)}
if warnings: if warnings:
ret['comment'] += '\n' + '. '.join(warnings) + '.' ret.setdefault('warnings', []).extend(warnings)
return ret return ret

View File

@ -14,7 +14,6 @@ import logging
import os.path import os.path
import pprint import pprint
import socket import socket
import urllib
import yaml import yaml
import ssl import ssl
@ -55,6 +54,7 @@ import salt.ext.six.moves.http_cookiejar
import salt.ext.six.moves.urllib.request as urllib_request import salt.ext.six.moves.urllib.request as urllib_request
from salt.ext.six.moves.urllib.error import URLError from salt.ext.six.moves.urllib.error import URLError
from salt.ext.six.moves.urllib.parse import splitquery from salt.ext.six.moves.urllib.parse import splitquery
from salt.ext.six.moves.urllib.parse import urlencode as _urlencode
# pylint: enable=import-error,no-name-in-module # pylint: enable=import-error,no-name-in-module
# Don't need a try/except block, since Salt depends on tornado # Don't need a try/except block, since Salt depends on tornado
@ -438,7 +438,7 @@ def query(url,
'not valid: {0}'.format(cert)) 'not valid: {0}'.format(cert))
if isinstance(data, dict): if isinstance(data, dict):
data = urllib.urlencode(data) data = _urlencode(data)
if verify_ssl: if verify_ssl:
req_kwargs['ca_certs'] = ca_bundle req_kwargs['ca_certs'] = ca_bundle

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
:copyright: © 2017 by the SaltStack Team, see AUTHORS for more details. :copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details.
:license: Apache 2.0, see LICENSE for more details. :license: Apache 2.0, see LICENSE for more details.

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)` :codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
:copyright: © 2015 by the SaltStack Team, see AUTHORS for more details. :copyright: Copyright 2015 by the SaltStack Team, see AUTHORS for more details.
:license: Apache 2.0, see LICENSE for more details. :license: Apache 2.0, see LICENSE for more details.

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)` :codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
:copyright: © 2016 by the SaltStack Team, see AUTHORS for more details. :copyright: Copyright 2016 by the SaltStack Team, see AUTHORS for more details.
:license: Apache 2.0, see LICENSE for more details. :license: Apache 2.0, see LICENSE for more details.

View File

@ -6,15 +6,25 @@ import os
import uuid import uuid
import shutil import shutil
import hashlib import hashlib
import logging
import psutil
import shutil
import signal
import tempfile
import textwrap
# Import Salt Testing libs # Import Salt Testing libs
from tests.support.case import ModuleCase from tests.support.case import ModuleCase
from tests.support.helpers import get_unused_localhost_port, skip_if_not_root
from tests.support.unit import skipIf
import tests.support.paths as paths import tests.support.paths as paths
# Import salt libs # Import salt libs
import salt.ext.six as six import salt.ext.six as six
import salt.utils import salt.utils
log = logging.getLogger(__name__)
class CPModuleTest(ModuleCase): class CPModuleTest(ModuleCase):
''' '''
@ -415,6 +425,83 @@ class CPModuleTest(ModuleCase):
with salt.utils.fopen(ret, 'r') as cp_: with salt.utils.fopen(ret, 'r') as cp_:
self.assertEqual(cp_.read(), 'foo') self.assertEqual(cp_.read(), 'foo')
@skipIf(not salt.utils.which('nginx'), 'nginx not installed')
@skip_if_not_root
def test_cache_remote_file(self):
'''
cp.cache_file
'''
nginx_port = get_unused_localhost_port()
url_prefix = 'http://localhost:{0}/'.format(nginx_port)
temp_dir = tempfile.mkdtemp(dir=paths.TMP)
self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True)
nginx_root_dir = os.path.join(temp_dir, 'root')
nginx_conf_dir = os.path.join(temp_dir, 'conf')
nginx_conf = os.path.join(nginx_conf_dir, 'nginx.conf')
nginx_pidfile = os.path.join(nginx_conf_dir, 'nginx.pid')
file_contents = 'Hello world!'
for dirname in (nginx_root_dir, nginx_conf_dir):
os.mkdir(dirname)
# Write the temp file
with salt.utils.fopen(os.path.join(nginx_root_dir, 'actual_file'), 'w') as fp_:
fp_.write(file_contents)
# Write the nginx config
with salt.utils.fopen(nginx_conf, 'w') as fp_:
fp_.write(textwrap.dedent(
'''\
user root;
worker_processes 1;
error_log {nginx_conf_dir}/server_error.log;
pid {nginx_pidfile};
events {{
worker_connections 1024;
}}
http {{
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log {nginx_conf_dir}/access.log;
error_log {nginx_conf_dir}/error.log;
server {{
listen {nginx_port} default_server;
server_name cachefile.local;
root {nginx_root_dir};
location ~ ^/301$ {{
return 301 /actual_file;
}}
location ~ ^/302$ {{
return 302 /actual_file;
}}
}}
}}'''.format(**locals())
))
self.run_function(
'cmd.run',
[['nginx', '-c', nginx_conf]],
python_shell=False
)
with salt.utils.fopen(nginx_pidfile) as fp_:
nginx_pid = int(fp_.read().strip())
nginx_proc = psutil.Process(pid=nginx_pid)
self.addCleanup(nginx_proc.send_signal, signal.SIGQUIT)
for code in ('', '301', '302'):
url = url_prefix + (code or 'actual_file')
log.debug('attempting to cache %s', url)
ret = self.run_function('cp.cache_file', [url])
with salt.utils.fopen(ret) as fp_:
cached_contents = fp_.read()
self.assertEqual(cached_contents, file_contents)
def test_list_states(self): def test_list_states(self):
''' '''
cp.list_states cp.list_states

View File

@ -22,6 +22,7 @@ def _find_new_locale(current_locale):
@skipIf(salt.utils.is_windows(), 'minion is windows') @skipIf(salt.utils.is_windows(), 'minion is windows')
@skipIf(salt.utils.is_darwin(), 'locale method is not supported on mac')
@requires_salt_modules('locale') @requires_salt_modules('locale')
class LocaleModuleTest(ModuleCase): class LocaleModuleTest(ModuleCase):
def test_get_locale(self): def test_get_locale(self):

View File

@ -99,10 +99,13 @@ class ArchiveTest(ModuleCase, SaltReturnAssertsMixin):
''' '''
test archive.extracted with user and group set to "root" test archive.extracted with user and group set to "root"
''' '''
r_group = 'root'
if salt.utils.is_darwin():
r_group = 'wheel'
ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, ret = self.run_state('archive.extracted', name=ARCHIVE_DIR,
source=self.archive_tar_source, archive_format='tar', source=self.archive_tar_source, archive_format='tar',
source_hash=ARCHIVE_TAR_HASH, source_hash=ARCHIVE_TAR_HASH,
user='root', group='root') user='root', group=r_group)
log.debug('ret = %s', ret) log.debug('ret = %s', ret)
if 'Timeout' in ret: if 'Timeout' in ret:
self.skipTest('Timeout talking to local tornado server.') self.skipTest('Timeout talking to local tornado server.')

View File

@ -545,6 +545,9 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
''' '''
Test file.managed passing a basic check_cmd kwarg. See Issue #38111. Test file.managed passing a basic check_cmd kwarg. See Issue #38111.
''' '''
r_group = 'root'
if salt.utils.is_darwin():
r_group = 'wheel'
if not salt.utils.which('visudo'): if not salt.utils.which('visudo'):
self.fail('sudo is missing') self.fail('sudo is missing')
try: try:
@ -552,7 +555,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
'file.managed', 'file.managed',
name='/tmp/sudoers', name='/tmp/sudoers',
user='root', user='root',
group='root', group=r_group,
mode=440, mode=440,
check_cmd='visudo -c -s -f' check_cmd='visudo -c -s -f'
) )

View File

@ -104,7 +104,8 @@ class UserTest(ModuleCase, SaltReturnAssertsMixin):
ret = self.run_state('user.present', name=self.user_name, ret = self.run_state('user.present', name=self.user_name,
home=self.user_home) home=self.user_home)
self.assertSaltTrueReturn(ret) self.assertSaltTrueReturn(ret)
self.assertTrue(os.path.isdir(self.user_home)) if not salt.utils.is_darwin():
self.assertTrue(os.path.isdir(self.user_home))
@requires_system_grains @requires_system_grains
def test_user_present_gid_from_name_default(self, grains=None): def test_user_present_gid_from_name_default(self, grains=None):
@ -127,7 +128,8 @@ class UserTest(ModuleCase, SaltReturnAssertsMixin):
self.assertReturnNonEmptySaltType(ret) self.assertReturnNonEmptySaltType(ret)
group_name = grp.getgrgid(ret['gid']).gr_name group_name = grp.getgrgid(ret['gid']).gr_name
self.assertTrue(os.path.isdir(self.user_home)) if not salt.utils.is_darwin():
self.assertTrue(os.path.isdir(self.user_home))
if grains['os_family'] in ('Suse',): if grains['os_family'] in ('Suse',):
self.assertEqual(group_name, 'users') self.assertEqual(group_name, 'users')
elif grains['os_family'] == 'MacOS': elif grains['os_family'] == 'MacOS':
@ -151,7 +153,8 @@ class UserTest(ModuleCase, SaltReturnAssertsMixin):
self.assertReturnNonEmptySaltType(ret) self.assertReturnNonEmptySaltType(ret)
group_name = grp.getgrgid(ret['gid']).gr_name group_name = grp.getgrgid(ret['gid']).gr_name
self.assertTrue(os.path.isdir(self.user_home)) if not salt.utils.is_darwin():
self.assertTrue(os.path.isdir(self.user_home))
self.assertEqual(group_name, self.user_name) self.assertEqual(group_name, self.user_name)
ret = self.run_state('user.absent', name=self.user_name) ret = self.run_state('user.absent', name=self.user_name)
self.assertSaltTrueReturn(ret) self.assertSaltTrueReturn(ret)

View File

@ -771,11 +771,11 @@ class TestDaemon(TestProgram):
if not self._shutdown: if not self._shutdown:
try: try:
pid = self.wait_for_daemon_pid(timeout) pid = self.wait_for_daemon_pid(timeout)
terminate_process(pid=pid) terminate_process(pid=pid, kill_children=True)
except TimeoutError: except TimeoutError:
pass pass
if self.process: if self.process:
terminate_process(pid=self.process.pid) terminate_process(pid=self.process.pid, kill_children=True)
self.process.wait() self.process.wait()
self.process = None self.process = None
self._shutdown = True self._shutdown = True

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
:copyright: © 2013-2017 by the SaltStack Team, see AUTHORS for more details. :copyright: Copyright 2013-2017 by the SaltStack Team, see AUTHORS for more details.
:license: Apache 2.0, see LICENSE for more details. :license: Apache 2.0, see LICENSE for more details.

View File

@ -6,7 +6,7 @@
Salt Tests CLI access classes Salt Tests CLI access classes
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)` :codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
:copyright: © 2013-2017 by the SaltStack Team, see AUTHORS for more details :copyright: Copyright 2013-2017 by the SaltStack Team, see AUTHORS for more details
:license: Apache 2.0, see LICENSE for more details. :license: Apache 2.0, see LICENSE for more details.
''' '''
# pylint: disable=repr-flag-used-in-string # pylint: disable=repr-flag-used-in-string

View File

@ -6,7 +6,7 @@
Code coverage aware testing parser Code coverage aware testing parser
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)` :codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
:copyright: © 2013 by the SaltStack Team, see AUTHORS for more details. :copyright: Copyright 2013 by the SaltStack Team, see AUTHORS for more details.
:license: Apache 2.0, see LICENSE for more details. :license: Apache 2.0, see LICENSE for more details.
''' '''
# pylint: disable=repr-flag-used-in-string # pylint: disable=repr-flag-used-in-string

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)` :codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
:copyright: © 2017 by the SaltStack Team, see AUTHORS for more details. :copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details.
:license: Apache 2.0, see LICENSE for more details. :license: Apache 2.0, see LICENSE for more details.

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
:copyright: © 2017 by the SaltStack Team, see AUTHORS for more details. :copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details.
:license: Apache 2.0, see LICENSE for more details. :license: Apache 2.0, see LICENSE for more details.

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)` :codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
:copyright: © 2014 by the SaltStack Team, see AUTHORS for more details. :copyright: Copyright 2014 by the SaltStack Team, see AUTHORS for more details.
:license: Apache 2.0, see LICENSE for more details. :license: Apache 2.0, see LICENSE for more details.

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
''' '''
:codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)` :codeauthor: :email:`Pedro Algarvio (pedro@algarvio.me)`
:copyright: © 2017 by the SaltStack Team, see AUTHORS for more details. :copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details.
tests.unit.beacons.test_status tests.unit.beacons.test_status

View File

@ -314,7 +314,7 @@ class KeystoneTestCase(TestCase, LoaderModuleMockMixin):
'keystone.endpoint_create': mock}): 'keystone.endpoint_create': mock}):
comt = ('Endpoint for service "{0}" already exists'.format(name)) comt = ('Endpoint for service "{0}" already exists'.format(name))
ret.update({'comment': comt, 'result': None, 'changes': {}}) ret.update({'comment': comt, 'result': True, 'changes': {}})
self.assertDictEqual(keystone.endpoint_present(name), ret) self.assertDictEqual(keystone.endpoint_present(name), ret)
with patch.dict(keystone.__opts__, {'test': True}): with patch.dict(keystone.__opts__, {'test': True}):
@ -323,7 +323,7 @@ class KeystoneTestCase(TestCase, LoaderModuleMockMixin):
self.assertDictEqual(keystone.endpoint_present(name), ret) self.assertDictEqual(keystone.endpoint_present(name), ret)
comt = ('Endpoint for service "{0}" already exists'.format(name)) comt = ('Endpoint for service "{0}" already exists'.format(name))
ret.update({'comment': comt, 'result': None, 'changes': {}}) ret.update({'comment': comt, 'result': True, 'changes': {}})
self.assertDictEqual(keystone.endpoint_present(name), ret) self.assertDictEqual(keystone.endpoint_present(name), ret)
with patch.dict(keystone.__opts__, {'test': False}): with patch.dict(keystone.__opts__, {'test': False}):