Merge branch 'develop' into add-aptly-exec-module

This commit is contained in:
Nicole Thomas 2017-08-29 11:24:37 -04:00 committed by GitHub
commit e6aba8a7c2
19 changed files with 469 additions and 201 deletions

4
.github/stale.yml vendored
View File

@ -1,8 +1,8 @@
# Probot Stale configuration file
# Number of days of inactivity before an issue becomes stale
# 1100 is approximately 3 years
daysUntilStale: 1100
# 1075 is approximately 2 years and 11 months
daysUntilStale: 1075
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7

View File

@ -4,6 +4,11 @@
"name": "ryan-lane",
"files": ["salt/**/*boto*.py"],
"skipTeamPrs": false
},
{
"name": "tkwilliams",
"files": ["salt/**/*boto*.py"],
"skipTeamPrs": false
}
],
"skipTitle": "Merge forward",

View File

@ -67,10 +67,11 @@ Engage SaltStack
`SaltConf`_, **User Groups and Meetups** - SaltStack has a vibrant and `global
community`_ of customers, users, developers and enthusiasts. Connect with other
Salted folks in your area of the world, or join `SaltConf16`_, the SaltStack
annual user conference, April 19-21 in Salt Lake City. Please let us know if
you would like to start a user group or if we should add your existing
SaltStack user group to this list by emailing: info@saltstack.com
Salted folks in your area of the world, or join `SaltConf`_, the SaltStack
annual user conference held in Salt Lake City. Please visit the `SaltConf`_ site
for details of our next conference. Also, please let us know if you would like
to start a user group or if we should add your existing SaltStack user group to
this list by emailing: info@saltstack.com
**SaltStack Training** - Get access to proprietary `SaltStack education
offerings`_ through instructor-led training offered on-site, virtually or at
@ -89,9 +90,8 @@ services`_ offerings.
* LinkedIn Group - `<https://www.linkedin.com/groups/4877160>`_
* Google+ - `<https://plus.google.com/b/112856352920437801867/+SaltStackInc/posts>`_
.. _SaltConf: http://www.youtube.com/user/saltstack
.. _global community: http://www.meetup.com/pro/saltstack/
.. _SaltConf16: http://saltconf.com/
.. _SaltConf: http://saltconf.com/
.. _SaltStack education offerings: http://saltstack.com/training/
.. _SaltStack Certified Engineer (SSCE): http://saltstack.com/certification/
.. _SaltStack professional services: http://saltstack.com/services/

View File

@ -260,6 +260,13 @@ The Salt development team will back-port bug fixes made to ``develop`` to the
current release branch if the contributor cannot create the pull request
against that branch.
Release Branches
----------------
For each release a branch will be created when we are ready to tag. The branch will be the same name as the tag minus the v. For example, the v2017.7.1 release was created from the 2017.7.1 branch. This branching strategy will allow for more stability when there is a need for a re-tag during the testing phase of our releases.
Once the branch is created, the fixes required for a given release, as determined by the SaltStack release team, will be added to this branch. All commits in this branch will be merged forward into the parent branch as well.
Keeping Salt Forks in Sync
==========================

View File

@ -143,19 +143,6 @@ def _reconstruct_ppa_name(owner_name, ppa_name):
return 'ppa:{0}/{1}'.format(owner_name, ppa_name)
def _get_repo(**kwargs):
'''
Check the kwargs for either 'fromrepo' or 'repo' and return the value.
'fromrepo' takes precedence over 'repo'.
'''
for key in ('fromrepo', 'repo'):
try:
return kwargs[key]
except KeyError:
pass
return ''
def _check_apt():
'''
Abort if python-apt is not installed
@ -250,18 +237,11 @@ def latest_version(*names, **kwargs):
'''
refresh = salt.utils.is_true(kwargs.pop('refresh', True))
show_installed = salt.utils.is_true(kwargs.pop('show_installed', False))
if 'repo' in kwargs:
# Remember to kill _get_repo() too when removing this warning.
salt.utils.versions.warn_until(
'Hydrogen',
'The \'repo\' argument to apt.latest_version is deprecated, and '
'will be removed in Salt {version}. Please use \'fromrepo\' '
'instead.'
raise SaltInvocationError(
'The \'repo\' argument is invalid, use \'fromrepo\' instead'
)
fromrepo = _get_repo(**kwargs)
kwargs.pop('fromrepo', None)
kwargs.pop('repo', None)
fromrepo = kwargs.pop('fromrepo', None)
cache_valid_time = kwargs.pop('cache_valid_time', 0)
if len(names) == 0:
@ -1453,9 +1433,10 @@ def _get_upgradable(dist_upgrade=True, **kwargs):
cmd.append('dist-upgrade')
else:
cmd.append('upgrade')
fromrepo = _get_repo(**kwargs)
if fromrepo:
cmd.extend(['-o', 'APT::Default-Release={0}'.format(fromrepo)])
try:
cmd.extend(['-o', 'APT::Default-Release={0}'.format(kwargs['fromrepo'])])
except KeyError:
pass
call = __salt__['cmd.run_all'](cmd,
python_shell=False,

View File

@ -202,45 +202,48 @@ def _get_snapshot_url(artifactory_url, repository, group_id, artifact_id, versio
has_classifier = classifier is not None and classifier != ""
if snapshot_version is None:
snapshot_version_metadata = _get_snapshot_version_metadata(artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, headers=headers)
try:
snapshot_version_metadata = _get_snapshot_version_metadata(artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, headers=headers)
if packaging not in snapshot_version_metadata['snapshot_versions']:
error_message = '''Cannot find requested packaging '{packaging}' in the snapshot version metadata.
artifactory_url: {artifactory_url}
repository: {repository}
group_id: {group_id}
artifact_id: {artifact_id}
packaging: {packaging}
classifier: {classifier}
version: {version}'''.format(
artifactory_url=artifactory_url,
repository=repository,
group_id=group_id,
artifact_id=artifact_id,
packaging=packaging,
classifier=classifier,
version=version)
raise ArtifactoryError(error_message)
if packaging not in snapshot_version_metadata['snapshot_versions']:
error_message = '''Cannot find requested packaging '{packaging}' in the snapshot version metadata.
artifactory_url: {artifactory_url}
repository: {repository}
group_id: {group_id}
artifact_id: {artifact_id}
packaging: {packaging}
classifier: {classifier}
version: {version}'''.format(
artifactory_url=artifactory_url,
repository=repository,
group_id=group_id,
artifact_id=artifact_id,
packaging=packaging,
classifier=classifier,
version=version)
raise ArtifactoryError(error_message)
if has_classifier and classifier not in snapshot_version_metadata['snapshot_versions']:
error_message = '''Cannot find requested classifier '{classifier}' in the snapshot version metadata.
artifactory_url: {artifactory_url}
repository: {repository}
group_id: {group_id}
artifact_id: {artifact_id}
packaging: {packaging}
classifier: {classifier}
version: {version}'''.format(
artifactory_url=artifactory_url,
repository=repository,
group_id=group_id,
artifact_id=artifact_id,
packaging=packaging,
classifier=classifier,
version=version)
raise ArtifactoryError(error_message)
if has_classifier and classifier not in snapshot_version_metadata['snapshot_versions']:
error_message = '''Cannot find requested classifier '{classifier}' in the snapshot version metadata.
artifactory_url: {artifactory_url}
repository: {repository}
group_id: {group_id}
artifact_id: {artifact_id}
packaging: {packaging}
classifier: {classifier}
version: {version}'''.format(
artifactory_url=artifactory_url,
repository=repository,
group_id=group_id,
artifact_id=artifact_id,
packaging=packaging,
classifier=classifier,
version=version)
raise ArtifactoryError(error_message)
snapshot_version = snapshot_version_metadata['snapshot_versions'][packaging]
snapshot_version = snapshot_version_metadata['snapshot_versions'][packaging]
except CommandExecutionError as err:
log.error('Could not fetch maven-metadata.xml. Assuming snapshot_version=%s.', version)
snapshot_version = version
group_url = __get_group_id_subpath(group_id, use_literal_group_id)

View File

@ -72,11 +72,20 @@ def __virtual__():
return True
def create_target_group(name, protocol, port, vpc_id,
region=None, key=None, keyid=None, profile=None,
health_check_protocol='HTTP', health_check_port='traffic-port',
health_check_path='/', health_check_interval_seconds=30,
health_check_timeout_seconds=5, healthy_threshold_count=5,
def create_target_group(name,
protocol,
port,
vpc_id,
region=None,
key=None,
keyid=None,
profile=None,
health_check_protocol='HTTP',
health_check_port='traffic-port',
health_check_path='/',
health_check_interval_seconds=30,
health_check_timeout_seconds=5,
healthy_threshold_count=5,
unhealthy_threshold_count=2):
'''
Create target group if not present.
@ -125,36 +134,40 @@ def create_target_group(name, protocol, port, vpc_id,
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
if target_group_exists(name, region, key, keyid, profile):
return True
else:
try:
lb = conn.create_target_group(Name=name, Protocol=protocol, Port=port,
VpcId=vpc_id, HealthCheckProtocol=health_check_protocol,
HealthCheckPort=health_check_port,
HealthCheckPath=health_check_path,
HealthCheckIntervalSeconds=health_check_interval_seconds,
HealthCheckTimeoutSeconds=health_check_timeout_seconds,
HealthyThresholdCount=healthy_threshold_count,
UnhealthyThresholdCount=unhealthy_threshold_count)
if lb:
log.info('Created ALB {0}: {1}'.format(name,
lb['TargetGroups'][0]['TargetGroupArn']))
return True
else:
log.error('Failed to create ALB {0}'.format(name))
return False
except ClientError as error:
log.debug(error)
log.error('Failed to create ALB {0}: {1}: {2}'.format(name,
error.response['Error']['Code'],
error.response['Error']['Message']))
try:
alb = conn.create_target_group(Name=name, Protocol=protocol, Port=port,
VpcId=vpc_id, HealthCheckProtocol=health_check_protocol,
HealthCheckPort=health_check_port,
HealthCheckPath=health_check_path,
HealthCheckIntervalSeconds=health_check_interval_seconds,
HealthCheckTimeoutSeconds=health_check_timeout_seconds,
HealthyThresholdCount=healthy_threshold_count,
UnhealthyThresholdCount=unhealthy_threshold_count)
if alb:
log.info('Created ALB {0}: {1}'.format(name,
alb['TargetGroups'][0]['TargetGroupArn']))
return True
else:
log.error('Failed to create ALB {0}'.format(name))
return False
except ClientError as error:
log.debug(error)
log.error('Failed to create ALB {0}: {1}: {2}'.format(name,
error.response['Error']['Code'],
error.response['Error']['Message']))
def delete_target_group(name, region=None, key=None, keyid=None, profile=None):
def delete_target_group(name,
region=None,
key=None,
keyid=None,
profile=None):
'''
Delete target group.
name
(string) - The Amazon Resource Name (ARN) of the resource.
(string) - Target Group Name or Amazon Resource Name (ARN).
returns
(bool) - True on success, False on failure.
@ -167,9 +180,20 @@ def delete_target_group(name, region=None, key=None, keyid=None, profile=None):
'''
conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile)
if not target_group_exists(name, region, key, keyid, profile):
return True
try:
conn.delete_target_group(TargetGroupArn=name)
log.info('Deleted target group {0}'.format(name))
if name.startswith('arn:aws:elasticloadbalancing'):
conn.delete_target_group(TargetGroupArn=name)
log.info('Deleted target group {0}'.format(name))
else:
tg_info = conn.describe_target_groups(Names=[name])
if len(tg_info['TargetGroups']) != 1:
return False
arn = tg_info['TargetGroups'][0]['TargetGroupArn']
conn.delete_target_group(TargetGroupArn=arn)
log.info('Deleted target group {0} ARN {1}'.format(name, arn))
return True
except ClientError as error:
log.debug(error)
@ -177,7 +201,11 @@ def delete_target_group(name, region=None, key=None, keyid=None, profile=None):
return False
def target_group_exists(name, region=None, key=None, keyid=None, profile=None):
def target_group_exists(name,
region=None,
key=None,
keyid=None,
profile=None):
'''
Check to see if an target group exists.
@ -200,11 +228,16 @@ def target_group_exists(name, region=None, key=None, keyid=None, profile=None):
log.warning('The target group does not exist in region {0}'.format(region))
return False
except ClientError as error:
log.warning(error)
log.warning('target_group_exists check for {0} returned: {1}'.format(name, error))
return False
def describe_target_health(name, targets=None, region=None, key=None, keyid=None, profile=None):
def describe_target_health(name,
targets=None,
region=None,
key=None,
keyid=None,
profile=None):
'''
Get the curret health check status for targets in a target group.
@ -234,8 +267,12 @@ def describe_target_health(name, targets=None, region=None, key=None, keyid=None
return {}
def register_targets(name, targets, region=None, key=None, keyid=None,
profile=None):
def register_targets(name,
targets,
region=None,
key=None,
keyid=None,
profile=None):
'''
Register targets to a target froup of an ALB. ``targets`` is either a
instance id string or a list of instance id's.
@ -264,15 +301,18 @@ def register_targets(name, targets, region=None, key=None, keyid=None,
registered_targets = conn.register_targets(TargetGroupArn=name, Targets=targetsdict)
if registered_targets:
return True
else:
return False
return False
except ClientError as error:
log.warning(error)
return False
def deregister_targets(name, targets, region=None, key=None, keyid=None,
profile=None):
def deregister_targets(name,
targets,
region=None,
key=None,
keyid=None,
profile=None):
'''
Deregister targets to a target froup of an ALB. ``targets`` is either a
instance id string or a list of instance id's.
@ -301,8 +341,7 @@ def deregister_targets(name, targets, region=None, key=None, keyid=None,
registered_targets = conn.deregister_targets(TargetGroupArn=name, Targets=targetsdict)
if registered_targets:
return True
else:
return False
return False
except ClientError as error:
log.warning(error)
return False

View File

@ -23,6 +23,10 @@ import salt.utils.path
import salt.utils.platform
from salt.exceptions import CommandExecutionError
__func_alias__ = {
'format_': 'format'
}
log = logging.getLogger(__name__)
HAS_HDPARM = salt.utils.path.which('hdparm') is not None

View File

@ -902,9 +902,14 @@ def compare_container(first, second, ignore=None):
continue
val1 = result1[conf_dict][item]
val2 = result2[conf_dict].get(item)
if item in ('OomKillDisable',):
if item in ('OomKillDisable',) or (val1 is None or val2 is None):
if bool(val1) != bool(val2):
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
elif item == 'Image':
image1 = inspect_image(val1)['Id']
image2 = inspect_image(val2)['Id']
if image1 != image2:
ret.setdefault(conf_dict, {})[item] = {'old': image1, 'new': image2}
else:
if item == 'Links':
val1 = _scrub_links(val1, first)
@ -920,9 +925,14 @@ def compare_container(first, second, ignore=None):
continue
val1 = result1[conf_dict].get(item)
val2 = result2[conf_dict][item]
if item in ('OomKillDisable',):
if item in ('OomKillDisable',) or (val1 is None or val2 is None):
if bool(val1) != bool(val2):
ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2}
elif item == 'Image':
image1 = inspect_image(val1)['Id']
image2 = inspect_image(val2)['Id']
if image1 != image2:
ret.setdefault(conf_dict, {})[item] = {'old': image1, 'new': image2}
else:
if item == 'Links':
val1 = _scrub_links(val1, first)

View File

@ -50,7 +50,152 @@ def __virtual__():
'''
Only load if boto is available.
'''
return 'boto_elbv2' if 'boto_elbv2.target_group_exists' in __salt__ else False
if 'boto_elbv2.target_group_exists' in __salt__:
return 'boto_elbv2'
return (False, "The boto_elbv2 module cannot be loaded: boto3 library not found")
def create_target_group(name, protocol, port, vpc_id,
region=None, key=None, keyid=None, profile=None,
health_check_protocol='HTTP', health_check_port='traffic-port',
health_check_path='/', health_check_interval_seconds=30,
health_check_timeout_seconds=5, healthy_threshold_count=5,
unhealthy_threshold_count=2, **kwargs):
'''
.. versionadded:: 2017.11.0
Create target group if not present.
name
(string) - The name of the target group.
protocol
(string) - The protocol to use for routing traffic to the targets
port
(int) - The port on which the targets receive traffic. This port is used unless
you specify a port override when registering the traffic.
vpc_id
(string) - The identifier of the virtual private cloud (VPC).
health_check_protocol
(string) - The protocol the load balancer uses when performing health check on
targets. The default is the HTTP protocol.
health_check_port
(string) - The port the load balancer uses when performing health checks on
targets. The default is 'traffic-port', which indicates the port on which each
target receives traffic from the load balancer.
health_check_path
(string) - The ping path that is the destination on the targets for health
checks. The default is /.
health_check_interval_seconds
(integer) - The approximate amount of time, in seconds, between health checks
of an individual target. The default is 30 seconds.
health_check_timeout_seconds
(integer) - The amount of time, in seconds, during which no response from a
target means a failed health check. The default is 5 seconds.
healthy_threshold_count
(integer) - The number of consecutive health checks successes required before
considering an unhealthy target healthy. The default is 5.
unhealthy_threshold_count
(integer) - The number of consecutive health check failures required before
considering a target unhealthy. The default is 2.
returns
(bool) - True on success, False on failure.
CLI example:
.. code-block:: yaml
create-target:
boto_elb2.create_targets_group:
- name: myALB
- protocol: https
- port: 443
- vpc_id: myVPC
'''
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
if __salt__['boto_elbv2.target_group_exists'](name, region, key, keyid, profile):
ret['result'] = True
ret['comment'] = 'Target Group {0} already exists'.format(name)
return ret
if __opts__['test']:
ret['comment'] = 'Target Group {0} will be created'.format(name)
return ret
state = __salt__['boto_elbv2.create_target_group'](name,
protocol,
port,
vpc_id,
region=region,
key=key,
keyid=keyid,
profile=profile,
health_check_protocol=health_check_protocol,
health_check_port=health_check_port,
health_check_path=health_check_path,
health_check_interval_seconds=health_check_interval_seconds,
health_check_timeout_seconds=health_check_timeout_seconds,
healthy_threshold_count=healthy_threshold_count,
unhealthy_threshold_count=unhealthy_threshold_count,
**kwargs)
if state:
ret['changes']['target_group'] = name
ret['result'] = True
ret['comment'] = 'Target Group {0} created'.format(name)
else:
ret['result'] = False
ret['comment'] = 'Target Group {0} creation failed'.format(name)
return ret
def delete_target_group(name, region=None, key=None, keyid=None, profile=None):
'''
Delete target group.
name
(string) - The Amazon Resource Name (ARN) of the resource.
returns
(bool) - True on success, False on failure.
CLI example:
.. code-block:: bash
check-target:
boto_elb2.delete_targets_group:
- name: myALB
- protocol: https
- port: 443
- vpc_id: myVPC
'''
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
if not __salt__['boto_elbv2.target_group_exists'](name, region, key, keyid, profile):
ret['result'] = True
ret['comment'] = 'Target Group {0} does not exists'.format(name)
return ret
if __opts__['test']:
ret['comment'] = 'Target Group {0} will be deleted'.format(name)
return ret
state = __salt__['boto_elbv2.delete_target_group'](name,
region=region,
key=key,
keyid=keyid,
profile=profile)
if state:
ret['result'] = True
ret['changes']['target_group'] = name
ret['comment'] = 'Target Group {0} deleted'.format(name)
else:
ret['result'] = False
ret['comment'] = 'Target Group {0} deletion failed'.format(name)
return ret
def targets_registered(name, targets, region=None, key=None, keyid=None,
@ -77,10 +222,13 @@ def targets_registered(name, targets, region=None, key=None, keyid=None,
- instance-id2
'''
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
tg = __salt__['boto_elbv2.target_group_exists'](name, region, key, keyid, profile)
if tg:
health = __salt__['boto_elbv2.describe_target_health'](name, region=region, key=key, keyid=keyid, profile=profile)
if __salt__['boto_elbv2.target_group_exists'](name, region, key, keyid, profile):
health = __salt__['boto_elbv2.describe_target_health'](name,
region=region,
key=key,
keyid=keyid,
profile=profile)
failure = False
changes = False
newhealth_mock = copy.copy(health)
@ -99,10 +247,10 @@ def targets_registered(name, targets, region=None, key=None, keyid=None,
else:
state = __salt__['boto_elbv2.register_targets'](name,
targets,
region,
key,
keyid,
profile)
region=region,
key=key,
keyid=keyid,
profile=profile)
if state:
changes = True
ret['result'] = True
@ -119,7 +267,11 @@ def targets_registered(name, targets, region=None, key=None, keyid=None,
ret['changes']['new'] = newhealth_mock
else:
ret['comment'] = 'Target Group {0} has been changed'.format(name)
newhealth = __salt__['boto_elbv2.describe_target_health'](name, region=region, key=key, keyid=keyid, profile=profile)
newhealth = __salt__['boto_elbv2.describe_target_health'](name,
region=region,
key=key,
keyid=keyid,
profile=profile)
ret['changes']['new'] = newhealth
return ret
else:
@ -128,7 +280,7 @@ def targets_registered(name, targets, region=None, key=None, keyid=None,
def targets_deregistered(name, targets, region=None, key=None, keyid=None,
profile=None, **kwargs):
profile=None, **kwargs):
'''
Remove targets to an Application Load Balancer target group.
@ -150,9 +302,12 @@ def targets_deregistered(name, targets, region=None, key=None, keyid=None,
- instance-id2
'''
ret = {'name': name, 'result': None, 'comment': '', 'changes': {}}
tg = __salt__['boto_elbv2.target_group_exists'](name, region, key, keyid, profile)
if tg:
health = __salt__['boto_elbv2.describe_target_health'](name, region=region, key=key, keyid=keyid, profile=profile)
if __salt__['boto_elbv2.target_group_exists'](name, region, key, keyid, profile):
health = __salt__['boto_elbv2.describe_target_health'](name,
region=region,
key=key,
keyid=keyid,
profile=profile)
failure = False
changes = False
newhealth_mock = copy.copy(health)
@ -168,11 +323,11 @@ def targets_deregistered(name, targets, region=None, key=None, keyid=None,
newhealth_mock.update({target: "draining"})
else:
state = __salt__['boto_elbv2.deregister_targets'](name,
targets,
region,
key,
keyid,
profile)
targets,
region=region,
key=key,
keyid=keyid,
profile=profile)
if state:
changes = True
ret['result'] = True
@ -189,7 +344,11 @@ def targets_deregistered(name, targets, region=None, key=None, keyid=None,
ret['changes']['new'] = newhealth_mock
else:
ret['comment'] = 'Target Group {0} has been changed'.format(name)
newhealth = __salt__['boto_elbv2.describe_target_health'](name, region, key, keyid, profile)
newhealth = __salt__['boto_elbv2.describe_target_health'](name,
region=region,
key=key,
keyid=keyid,
profile=profile)
ret['changes']['new'] = newhealth
return ret
else:

View File

@ -627,7 +627,11 @@ def _clean_dir(root, keep, exclude_pat):
while True:
fn_ = os.path.dirname(fn_)
real_keep.add(fn_)
if fn_ in ['/', ''.join([os.path.splitdrive(fn_)[0], '\\\\'])]:
if fn_ in [
os.sep,
''.join([os.path.splitdrive(fn_)[0], os.sep]),
''.join([os.path.splitdrive(fn_)[0], os.sep, os.sep])
]:
break
def _delete_not_kept(nfn):

View File

@ -2238,13 +2238,18 @@ def detached(name,
local_commit_id = _get_local_rev_and_branch(target, user, password)[0]
if remote_rev_type is 'hash' \
and __salt__['git.describe'](target,
rev,
user=user,
password=password):
# The rev is a hash and it exists locally so skip to checkout
hash_exists_locally = True
if remote_rev_type is 'hash':
try:
__salt__['git.describe'](target,
rev,
user=user,
password=password,
ignore_retcode=True)
except CommandExecutionError:
hash_exists_locally = False
else:
# The rev is a hash and it exists locally so skip to checkout
hash_exists_locally = True
else:
# Check that remote is present and set to correct url
remotes = __salt__['git.remotes'](target,

View File

@ -92,6 +92,13 @@ def managed(name,
Use certain profiles to generate the config.
If not specified, will use the platform default profile(s).
compliance_report: ``False``
Return the compliance report in the comment.
The compliance report structured object can be found however
in the ``pchanges`` field of the output (not displayed on the CLI).
.. versionadded:: 2017.7.3
test: ``False``
Dry run? If set as ``True``, will apply the config, discard
and return the changes. Default: ``False`` and will commit
@ -140,6 +147,7 @@ def managed(name,
debug = kwargs.get('debug', False) or __opts__.get('debug', False)
commit = kwargs.get('commit', True) or __opts__.get('commit', True)
replace = kwargs.get('replace', False) or __opts__.get('replace', False)
return_compliance_report = kwargs.get('compliance_report', False) or __opts__.get('compliance_report', False)
profiles = kwargs.get('profiles', [])
temp_file = __salt__['temp.file']()
log.debug('Creating temp file: {0}'.format(temp_file))
@ -180,7 +188,13 @@ def managed(name,
log.debug('Loaded config result:')
log.debug(loaded_changes)
__salt__['file.remove'](temp_file)
return salt.utils.napalm.loaded_ret(ret, loaded_changes, test, debug)
loaded_changes['compliance_report'] = compliance_report
return salt.utils.napalm.loaded_ret(ret,
loaded_changes,
test,
debug,
opts=__opts__,
compliance_report=return_compliance_report)
def configured(name,

View File

@ -1,6 +1,11 @@
# -*- coding: utf-8 -*-
'''
Some of the utils used by salt
NOTE: The dev team is working on splitting up this file for the Oxygen release.
Please do not add any new functions to this file. New functions should be
organized in other files under salt/utils/. Please consult the dev team if you
are unsure where a new function should go.
'''
# Import python libs

View File

@ -22,6 +22,7 @@ import importlib
from functools import wraps
# Import Salt libs
import salt.output
import salt.utils.platform
# Import 3rd-party libs
@ -432,58 +433,58 @@ def default_ret(name):
return ret
def loaded_ret(ret, loaded, test, debug):
def loaded_ret(ret, loaded, test, debug, compliance_report=False, opts=None):
'''
Return the final state output.
ret
The initial state output structure.
loaded
The loaded dictionary.
'''
# Always get the comment
ret.update({
'comment': loaded.get('comment', '')
})
changes = {}
pchanges = {}
ret['comment'] = loaded['comment']
if 'diff' in loaded:
changes['diff'] = loaded['diff']
pchanges['diff'] = loaded['diff']
if 'compliance_report' in loaded:
if compliance_report:
changes['compliance_report'] = loaded['compliance_report']
pchanges['compliance_report'] = loaded['compliance_report']
if debug and 'loaded_config' in loaded:
changes['loaded_config'] = loaded['loaded_config']
pchanges['loaded_config'] = loaded['loaded_config']
ret['pchanges'] = pchanges
if changes.get('diff'):
ret['comment'] = '{comment_base}\n\nConfiguration diff:\n\n{diff}'.format(comment_base=ret['comment'],
diff=changes['diff'])
if changes.get('loaded_config'):
ret['comment'] = '{comment_base}\n\nLoaded config:\n\n{loaded_cfg}'.format(
comment_base=ret['comment'],
loaded_cfg=changes['loaded_config'])
if changes.get('compliance_report'):
ret['comment'] = '{comment_base}\n\nCompliance report:\n\n{compliance}'.format(
comment_base=ret['comment'],
compliance=salt.output.string_format(changes['compliance_report'], 'nested', opts=opts))
if not loaded.get('result', False):
# Failure of some sort
return ret
if debug:
# Always check for debug
pchanges.update({
'loaded_config': loaded.get('loaded_config', '')
})
ret.update({
"pchanges": pchanges
})
if not loaded.get('already_configured', True):
# We're making changes
pchanges.update({
"diff": loaded.get('diff', '')
})
ret.update({
'pchanges': pchanges
})
if test:
for k, v in pchanges.items():
ret.update({
"comment": "{}:\n{}\n\n{}".format(k, v, ret.get("comment", ''))
})
ret.update({
'result': None,
})
ret['result'] = None
return ret
# Not test, changes were applied
ret.update({
'result': True,
'changes': pchanges,
'comment': "Configuration changed!\n{}".format(ret.get('comment', ''))
'changes': changes,
'comment': "Configuration changed!\n{}".format(loaded['comment'])
})
return ret
# No changes
ret.update({
'result': True
'result': True,
'changes': {}
})
return ret

View File

@ -104,8 +104,12 @@ def parse_pkginfo(line, osarch=None):
if epoch not in ('(none)', '0'):
version = ':'.join((epoch, version))
install_date = datetime.datetime.utcfromtimestamp(int(install_time)).isoformat() + "Z"
install_date_time_t = int(install_time)
if install_time:
install_date = datetime.datetime.utcfromtimestamp(int(install_time)).isoformat() + "Z"
install_date_time_t = int(install_time)
else:
install_date = None
install_date_time_t = None
return pkginfo(name, version, arch, repoid, install_date, install_date_time_t)

View File

@ -42,7 +42,7 @@ class NpmStateTest(ModuleCase, SaltReturnAssertsMixin):
'''
Determine if URL-referenced NPM module can be successfully installed.
'''
ret = self.run_state('npm.installed', name='git://github.com/request/request')
ret = self.run_state('npm.installed', name='request/request#v2.81.1')
self.assertSaltTrueReturn(ret)
ret = self.run_state('npm.removed', name='git://github.com/request/request')
self.assertSaltTrueReturn(ret)

View File

@ -722,3 +722,30 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin):
result = docker_mod.images()
self.assertEqual(result,
{'sha256:abcdefg': {'RepoTags': ['image:latest']}})
def test_compare_container_image_id_resolution(self):
'''
Test comparing two containers when one's inspect output is an ID and
not formatted in image:tag notation.
'''
def _inspect_container_effect(id_):
return {
'container1': {'Config': {'Image': 'realimage:latest'},
'HostConfig': {}},
'container2': {'Config': {'Image': 'image_id'},
'HostConfig': {}},
}[id_]
def _inspect_image_effect(id_):
return {
'realimage:latest': {'Id': 'image_id'},
'image_id': {'Id': 'image_id'},
}[id_]
inspect_container_mock = MagicMock(side_effect=_inspect_container_effect)
inspect_image_mock = MagicMock(side_effect=_inspect_image_effect)
with patch.object(docker_mod, 'inspect_container', inspect_container_mock):
with patch.object(docker_mod, 'inspect_image', inspect_image_mock):
ret = docker_mod.compare_container('container1', 'container2')
self.assertEqual(ret, {})

View File

@ -13,33 +13,33 @@ from tests.support.unit import TestCase
from tests.support.paths import CODE_DIR
EXCLUDED_DIRS = [
'tests/pkg',
'tests/perf',
'tests/support',
'tests/unit/utils/cache_mods',
'tests/unit/modules/inspectlib',
'tests/unit/modules/zypp/',
'tests/unit/templates/files',
'tests/integration/files/',
'tests/integration/cloud/helpers',
os.path.join('tests', 'pkg'),
os.path.join('tests', 'perf'),
os.path.join('tests', 'support'),
os.path.join('tests', 'unit', 'utils', 'cache_mods'),
os.path.join('tests', 'unit', 'modules', 'inspectlib'),
os.path.join('tests', 'unit', 'modules', 'zypp'),
os.path.join('tests', 'unit', 'templates', 'files'),
os.path.join('tests', 'integration', 'files'),
os.path.join('tests', 'integration', 'cloud', 'helpers'),
]
EXCLUDED_FILES = [
'tests/eventlisten.py',
'tests/buildpackage.py',
'tests/saltsh.py',
'tests/minionswarm.py',
'tests/wheeltest.py',
'tests/runtests.py',
'tests/jenkins.py',
'tests/salt-tcpdump.py',
'tests/conftest.py',
'tests/packdump.py',
'tests/consist.py',
'tests/modparser.py',
'tests/committer_parser.py',
'tests/zypp_plugin.py',
'tests/unit/transport/mixins.py',
'tests/integration/utils/testprogram.py',
os.path.join('tests', 'eventlisten.py'),
os.path.join('tests', 'buildpackage.py'),
os.path.join('tests', 'saltsh.py'),
os.path.join('tests', 'minionswarm.py'),
os.path.join('tests', 'wheeltest.py'),
os.path.join('tests', 'runtests.py'),
os.path.join('tests', 'jenkins.py'),
os.path.join('tests', 'salt-tcpdump.py'),
os.path.join('tests', 'conftest.py'),
os.path.join('tests', 'packdump.py'),
os.path.join('tests', 'consist.py'),
os.path.join('tests', 'modparser.py'),
os.path.join('tests', 'committer_parser.py'),
os.path.join('tests', 'zypp_plugin.py'),
os.path.join('tests', 'unit', 'transport', 'mixins.py'),
os.path.join('tests', 'integration', 'utils', 'testprogram.py'),
]