mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2017.7' into bp-43018
This commit is contained in:
commit
971b4c0890
19 changed files with 782 additions and 543 deletions
|
@ -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
|
||||
==========================
|
||||
|
||||
|
|
|
@ -139,19 +139,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
|
||||
|
@ -246,18 +233,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.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:
|
||||
|
@ -1402,9 +1382,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,
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ def execute(context=None, lens=None, commands=(), load_path=None):
|
|||
method = METHOD_MAP[cmd]
|
||||
nargs = arg_map[method]
|
||||
|
||||
parts = salt.utils.shlex_split(arg, posix=False)
|
||||
parts = salt.utils.shlex_split(arg)
|
||||
|
||||
if len(parts) not in nargs:
|
||||
err = '{0} takes {1} args: {2}'.format(method, nargs, parts)
|
||||
|
|
|
@ -22,6 +22,10 @@ import salt.utils.decorators as decorators
|
|||
from salt.utils.decorators import depends
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
__func_alias__ = {
|
||||
'format_': 'format'
|
||||
}
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
HAS_HDPARM = salt.utils.which('hdparm') is not None
|
||||
|
|
|
@ -899,9 +899,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)
|
||||
|
@ -917,9 +922,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)
|
||||
|
@ -3880,8 +3890,9 @@ def save(name,
|
|||
saved_path = salt.utils.files.mkstemp()
|
||||
else:
|
||||
saved_path = path
|
||||
|
||||
cmd = ['docker', 'save', '-o', saved_path, inspect_image(name)['Id']]
|
||||
# use the image name if its valid if not use the image id
|
||||
image_to_save = name if name in inspect_image(name)['RepoTags'] else inspect_image(name)['Id']
|
||||
cmd = ['docker', 'save', '-o', saved_path, image_to_save]
|
||||
time_started = time.time()
|
||||
result = __salt__['cmd.run_all'](cmd, python_shell=False)
|
||||
if result['retcode'] != 0:
|
||||
|
|
|
@ -24,6 +24,8 @@ import salt.utils.kickstart
|
|||
import salt.syspaths
|
||||
from salt.exceptions import SaltInvocationError
|
||||
|
||||
# Import 3rd-party libs
|
||||
from salt.ext import six
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -325,6 +327,8 @@ def _bootstrap_yum(
|
|||
'''
|
||||
if pkgs is None:
|
||||
pkgs = []
|
||||
elif isinstance(pkgs, six.string_types):
|
||||
pkgs = pkgs.split(',')
|
||||
|
||||
default_pkgs = ('yum', 'centos-release', 'iputils')
|
||||
for pkg in default_pkgs:
|
||||
|
@ -333,6 +337,8 @@ def _bootstrap_yum(
|
|||
|
||||
if exclude_pkgs is None:
|
||||
exclude_pkgs = []
|
||||
elif isinstance(exclude_pkgs, six.string_types):
|
||||
exclude_pkgs = exclude_pkgs.split(',')
|
||||
|
||||
for pkg in exclude_pkgs:
|
||||
pkgs.remove(pkg)
|
||||
|
@ -393,15 +399,27 @@ def _bootstrap_deb(
|
|||
if repo_url is None:
|
||||
repo_url = 'http://ftp.debian.org/debian/'
|
||||
|
||||
if not salt.utils.which('debootstrap'):
|
||||
log.error('Required tool debootstrap is not installed.')
|
||||
return False
|
||||
|
||||
if isinstance(pkgs, (list, tuple)):
|
||||
pkgs = ','.join(pkgs)
|
||||
if isinstance(exclude_pkgs, (list, tuple)):
|
||||
exclude_pkgs = ','.join(exclude_pkgs)
|
||||
|
||||
deb_args = [
|
||||
'debootstrap',
|
||||
'--foreign',
|
||||
'--arch',
|
||||
_cmd_quote(arch),
|
||||
'--include',
|
||||
] + pkgs + [
|
||||
'--exclude',
|
||||
] + exclude_pkgs + [
|
||||
_cmd_quote(arch)]
|
||||
|
||||
if pkgs:
|
||||
deb_args += ['--include', _cmd_quote(pkgs)]
|
||||
if exclude_pkgs:
|
||||
deb_args += ['--exclude', _cmd_quote(exclude_pkgs)]
|
||||
|
||||
deb_args += [
|
||||
_cmd_quote(flavor),
|
||||
_cmd_quote(root),
|
||||
_cmd_quote(repo_url),
|
||||
|
@ -469,6 +487,8 @@ def _bootstrap_pacman(
|
|||
|
||||
if pkgs is None:
|
||||
pkgs = []
|
||||
elif isinstance(pkgs, six.string_types):
|
||||
pkgs = pkgs.split(',')
|
||||
|
||||
default_pkgs = ('pacman', 'linux', 'systemd-sysvcompat', 'grub')
|
||||
for pkg in default_pkgs:
|
||||
|
@ -477,6 +497,8 @@ def _bootstrap_pacman(
|
|||
|
||||
if exclude_pkgs is None:
|
||||
exclude_pkgs = []
|
||||
elif isinstance(exclude_pkgs, six.string_types):
|
||||
exclude_pkgs = exclude_pkgs.split(',')
|
||||
|
||||
for pkg in exclude_pkgs:
|
||||
pkgs.remove(pkg)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -561,7 +561,7 @@ def ext_pillar(minion_id, repo, pillar_dirs):
|
|||
)
|
||||
for pillar_dir, env in six.iteritems(pillar.pillar_dirs):
|
||||
# If pillarenv is set, only grab pillars with that match pillarenv
|
||||
if opts['pillarenv'] and env != opts['pillarenv']:
|
||||
if opts['pillarenv'] and env != opts['pillarenv'] and env != '__env__':
|
||||
log.debug(
|
||||
'env \'%s\' for pillar dir \'%s\' does not match '
|
||||
'pillarenv \'%s\', skipping',
|
||||
|
|
|
@ -622,7 +622,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):
|
||||
|
|
|
@ -1301,6 +1301,23 @@ def latest(name,
|
|||
'if it does not already exist).',
|
||||
comments
|
||||
)
|
||||
remote_tags = set([
|
||||
x.replace('refs/tags/', '') for x in __salt__['git.ls_remote'](
|
||||
cwd=target,
|
||||
remote=remote,
|
||||
opts="--tags",
|
||||
user=user,
|
||||
password=password,
|
||||
identity=identity,
|
||||
saltenv=__env__,
|
||||
ignore_retcode=True,
|
||||
).keys() if '^{}' not in x
|
||||
])
|
||||
if set(all_local_tags) != remote_tags:
|
||||
has_remote_rev = False
|
||||
ret['changes']['new_tags'] = list(remote_tags.symmetric_difference(
|
||||
all_local_tags
|
||||
))
|
||||
|
||||
if not has_remote_rev:
|
||||
try:
|
||||
|
@ -2236,13 +2253,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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
@ -16,7 +21,6 @@ import json
|
|||
import logging
|
||||
import numbers
|
||||
import os
|
||||
import os.path
|
||||
import posixpath
|
||||
import random
|
||||
import re
|
||||
|
@ -33,7 +37,6 @@ import warnings
|
|||
import string
|
||||
import subprocess
|
||||
import getpass
|
||||
import urllib
|
||||
|
||||
# Import 3rd-party libs
|
||||
from salt.ext import six
|
||||
|
@ -165,38 +168,6 @@ def is_empty(filename):
|
|||
return False
|
||||
|
||||
|
||||
def safe_filename_leaf(file_basename):
|
||||
'''
|
||||
input the basename of a file, without the directory tree, and returns a safe name to use
|
||||
i.e. only the required characters are converted by urllib.quote
|
||||
If the input is a PY2 String, output a PY2 String. If input is Unicode output Unicode.
|
||||
For consistency all platforms are treated the same. Hard coded to utf8 as its ascii compatible
|
||||
windows is \\ / : * ? " < > | posix is /
|
||||
'''
|
||||
def _replace(re_obj):
|
||||
return urllib.quote(re_obj.group(0), safe=u'')
|
||||
if not isinstance(file_basename, six.text_type):
|
||||
# the following string is not prefixed with u
|
||||
return re.sub('[\\\\:/*?"<>|]',
|
||||
_replace,
|
||||
six.text_type(file_basename, 'utf8').encode('ascii', 'backslashreplace'))
|
||||
# the following string is prefixed with u
|
||||
return re.sub(u'[\\\\:/*?"<>|]', _replace, file_basename, flags=re.UNICODE)
|
||||
|
||||
|
||||
def safe_filepath(file_path_name):
|
||||
'''
|
||||
input the full path and filename, splits on directory separator and calls safe_filename_leaf for
|
||||
each part of the path.
|
||||
'''
|
||||
(drive, path) = os.path.splitdrive(file_path_name)
|
||||
path = os.sep.join([safe_filename_leaf(file_section) for file_section in file_path_name.rsplit(os.sep)])
|
||||
if drive:
|
||||
return os.sep.join([drive, path])
|
||||
else:
|
||||
return path
|
||||
|
||||
|
||||
def is_hex(value):
|
||||
'''
|
||||
Returns True if value is a hexidecimal string, otherwise returns False
|
||||
|
|
|
@ -7,9 +7,11 @@ import contextlib
|
|||
import errno
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
import urllib
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
|
@ -258,3 +260,39 @@ def set_umask(mask):
|
|||
yield
|
||||
finally:
|
||||
os.umask(orig_mask)
|
||||
|
||||
|
||||
def safe_filename_leaf(file_basename):
|
||||
'''
|
||||
Input the basename of a file, without the directory tree, and returns a safe name to use
|
||||
i.e. only the required characters are converted by urllib.quote
|
||||
If the input is a PY2 String, output a PY2 String. If input is Unicode output Unicode.
|
||||
For consistency all platforms are treated the same. Hard coded to utf8 as its ascii compatible
|
||||
windows is \\ / : * ? " < > | posix is /
|
||||
|
||||
.. versionadded:: 2017.7.2
|
||||
'''
|
||||
def _replace(re_obj):
|
||||
return urllib.quote(re_obj.group(0), safe=u'')
|
||||
if not isinstance(file_basename, six.text_type):
|
||||
# the following string is not prefixed with u
|
||||
return re.sub('[\\\\:/*?"<>|]',
|
||||
_replace,
|
||||
six.text_type(file_basename, 'utf8').encode('ascii', 'backslashreplace'))
|
||||
# the following string is prefixed with u
|
||||
return re.sub(u'[\\\\:/*?"<>|]', _replace, file_basename, flags=re.UNICODE)
|
||||
|
||||
|
||||
def safe_filepath(file_path_name):
|
||||
'''
|
||||
Input the full path and filename, splits on directory separator and calls safe_filename_leaf for
|
||||
each part of the path.
|
||||
|
||||
.. versionadded:: 2017.7.2
|
||||
'''
|
||||
(drive, path) = os.path.splitdrive(file_path_name)
|
||||
path = os.sep.join([safe_filename_leaf(file_section) for file_section in file_path_name.rsplit(os.sep)])
|
||||
if drive:
|
||||
return os.sep.join([drive, path])
|
||||
else:
|
||||
return path
|
||||
|
|
|
@ -24,6 +24,7 @@ from functools import wraps
|
|||
log = logging.getLogger(__file__)
|
||||
|
||||
import salt.utils
|
||||
import salt.output
|
||||
|
||||
# Import third party lib
|
||||
try:
|
||||
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -697,3 +697,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, {})
|
||||
|
|
|
@ -46,12 +46,57 @@ class GenesisTestCase(TestCase, LoaderModuleMockMixin):
|
|||
with patch.dict(genesis.__salt__, {'disk.blkid': MagicMock(return_value={})}):
|
||||
self.assertEqual(genesis.bootstrap('rpm', 'root', 'dir'), None)
|
||||
|
||||
with patch.object(genesis, '_bootstrap_deb', return_value='A'):
|
||||
common_parms = {'platform': 'deb',
|
||||
'root': 'root',
|
||||
'img_format': 'dir',
|
||||
'arch': 'amd64',
|
||||
'flavor': 'stable',
|
||||
'static_qemu': 'qemu'}
|
||||
|
||||
param_sets = [
|
||||
|
||||
{'params': {},
|
||||
'cmd': ['debootstrap', '--foreign', '--arch', 'amd64',
|
||||
'stable', 'root', 'http://ftp.debian.org/debian/']
|
||||
},
|
||||
|
||||
{'params': {'pkgs': 'vim'},
|
||||
'cmd': ['debootstrap', '--foreign', '--arch', 'amd64',
|
||||
'--include', 'vim',
|
||||
'stable', 'root', 'http://ftp.debian.org/debian/']
|
||||
},
|
||||
|
||||
{'params': {'pkgs': 'vim,emacs'},
|
||||
'cmd': ['debootstrap', '--foreign', '--arch', 'amd64',
|
||||
'--include', 'vim,emacs',
|
||||
'stable', 'root', 'http://ftp.debian.org/debian/']
|
||||
},
|
||||
|
||||
{'params': {'pkgs': ['vim', 'emacs']},
|
||||
'cmd': ['debootstrap', '--foreign', '--arch', 'amd64',
|
||||
'--include', 'vim,emacs',
|
||||
'stable', 'root', 'http://ftp.debian.org/debian/']
|
||||
},
|
||||
|
||||
{'params': {'pkgs': ['vim', 'emacs'], 'exclude_pkgs': ['vim', 'foo']},
|
||||
'cmd': ['debootstrap', '--foreign', '--arch', 'amd64',
|
||||
'--include', 'vim,emacs', '--exclude', 'vim,foo',
|
||||
'stable', 'root', 'http://ftp.debian.org/debian/']
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
for param_set in param_sets:
|
||||
|
||||
with patch.dict(genesis.__salt__, {'mount.umount': MagicMock(),
|
||||
'file.rmdir': MagicMock(),
|
||||
'file.directory_exists': MagicMock()}):
|
||||
with patch.dict(genesis.__salt__, {'disk.blkid': MagicMock(return_value={})}):
|
||||
self.assertEqual(genesis.bootstrap('deb', 'root', 'dir'), None)
|
||||
'file.directory_exists': MagicMock(),
|
||||
'cmd.run': MagicMock(),
|
||||
'disk.blkid': MagicMock(return_value={})}):
|
||||
with patch('salt.modules.genesis.salt.utils.which', return_value=True):
|
||||
param_set['params'].update(common_parms)
|
||||
self.assertEqual(genesis.bootstrap(**param_set['params']), None)
|
||||
genesis.__salt__['cmd.run'].assert_any_call(param_set['cmd'], python_shell=False)
|
||||
|
||||
with patch.object(genesis, '_bootstrap_pacman', return_value='A') as pacman_patch:
|
||||
with patch.dict(genesis.__salt__, {'mount.umount': MagicMock(),
|
||||
|
|
|
@ -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'),
|
||||
]
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue