Merge branch '2018.3' into fix-etcd-cache

This commit is contained in:
Anthony Arnaud 2018-08-27 09:42:12 -04:00 committed by GitHub
commit 13e1a1730c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 934 additions and 376 deletions

View file

@ -125,7 +125,7 @@
# The master can take a while to start up when lspci and/or dmidecode is used
# to populate the grains for the master. Enable if you want to see GPU hardware
# data for your master.
# enable_gpu_grains: False
# enable_gpu_grains: True
# The master maintains a job cache. While this is a great addition, it can be
# a burden on the master for larger deployments (over 5000 minions).
@ -574,12 +574,6 @@
#
#master_tops: {}
# The external_nodes option allows Salt to gather data that would normally be
# placed in a top file. The external_nodes option is the executable that will
# return the ENC data. Remember that Salt will look for external nodes AND top
# files and combine the results if both are enabled!
#external_nodes: None
# The renderer to use on the minions to render the state data
#renderer: yaml_jinja

View file

@ -127,7 +127,7 @@ syndic_user: salt
# The master can take a while to start up when lspci and/or dmidecode is used
# to populate the grains for the master. Enable if you want to see GPU hardware
# data for your master.
# enable_gpu_grains: False
# enable_gpu_grains: True
# The master maintains a job cache. While this is a great addition, it can be
# a burden on the master for larger deployments (over 5000 minions).
@ -536,12 +536,6 @@ syndic_user: salt
#
#master_tops: {}
# The external_nodes option allows Salt to gather data that would normally be
# placed in a top file. The external_nodes option is the executable that will
# return the ENC data. Remember that Salt will look for external nodes AND top
# files and combine the results if both are enabled!
#external_nodes: None
# The renderer to use on the minions to render the state data
#renderer: yaml_jinja

View file

@ -5256,7 +5256,7 @@ sock_dir: /var/run/salt/master
.UNINDENT
.SS \fBenable_gpu_grains\fP
.sp
Default: \fBTrue\fP
Default: \fBFalse\fP
.sp
Enable GPU hardware data for your master. Be aware that the master can
take a while to start up when lspci and/or dmidecode is used to populate the
@ -6993,25 +6993,6 @@ master_tops:
.fi
.UNINDENT
.UNINDENT
.SS \fBexternal_nodes\fP
.sp
Default: None
.sp
The external_nodes option allows Salt to gather data that would normally be
placed in a top file from and external node controller. The external_nodes
option is the executable that will return the ENC data. Remember that Salt
will look for external nodes AND top files and combine the results if both
are enabled and available!
.INDENT 0.0
.INDENT 3.5
.sp
.nf
.ft C
external_nodes: cobbler\-ext\-nodes
.ft P
.fi
.UNINDENT
.UNINDENT
.SS \fBrenderer\fP
.sp
Default: \fByaml_jinja\fP
@ -15229,7 +15210,7 @@ and \fBmine_functions\fP\&.
# The master can take a while to start up when lspci and/or dmidecode is used
# to populate the grains for the master. Enable if you want to see GPU hardware
# data for your master.
# enable_gpu_grains: False
# enable_gpu_grains: True
# The master maintains a job cache. While this is a great addition, it can be
# a burden on the master for larger deployments (over 5000 minions).
@ -15678,12 +15659,6 @@ and \fBmine_functions\fP\&.
#
#master_tops: {}
# The external_nodes option allows Salt to gather data that would normally be
# placed in a top file. The external_nodes option is the executable that will
# return the ENC data. Remember that Salt will look for external nodes AND top
# files and combine the results if both are enabled!
#external_nodes: None
# The renderer to use on the minions to render the state data
#renderer: yaml_jinja

View file

@ -472,7 +472,7 @@ communication.
``enable_gpu_grains``
---------------------
Default: ``True``
Default: ``False``
Enable GPU hardware data for your master. Be aware that the master can
take a while to start up when lspci and/or dmidecode is used to populate the
@ -2059,23 +2059,6 @@ following configuration:
master_tops:
ext_nodes: <Shell command which returns yaml>
.. conf_master:: external_nodes
``external_nodes``
------------------
Default: None
The external_nodes option allows Salt to gather data that would normally be
placed in a top file from and external node controller. The external_nodes
option is the executable that will return the ENC data. Remember that Salt
will look for external nodes AND top files and combine the results if both
are enabled and available!
.. code-block:: yaml
external_nodes: cobbler-ext-nodes
.. conf_master:: renderer
``renderer``

View file

@ -6,6 +6,7 @@ cffi==1.11.2
CherryPy==13.0.0
click==6.7
enum34==1.1.6
futures==3.1.1
gitdb==0.6.4
GitPython==2.1.7
idna==2.6

View file

@ -68,11 +68,11 @@ If not Exist "%PyDir%\python.exe" (
)
Set "CurDir=%~dp0"
Set "BldDir=%CurDir%\buildenv"
Set "BinDir=%CurDir%\buildenv\bin"
Set "CnfDir=%CurDir%\buildenv\conf"
Set "InsDir=%CurDir%\installer"
Set "PreDir=%CurDir%\prereqs"
Set "BldDir=%CurDir%buildenv"
Set "BinDir=%CurDir%buildenv\bin"
Set "CnfDir=%CurDir%buildenv\conf"
Set "InsDir=%CurDir%installer"
Set "PreDir=%CurDir%prereqs"
for /f "delims=" %%a in ('git rev-parse --show-toplevel') do @set "SrcDir=%%a"
:: Find the NSIS Installer
@ -113,10 +113,8 @@ xcopy /Q /Y "%SrcDir%\conf\master" "%CnfDir%\"
xcopy /Q /Y "%SrcDir%\conf\minion" "%CnfDir%\"
@echo.
@echo Copying NSSM to buildenv
@echo Copying SSM to buildenv
@echo ----------------------------------------------------------------------
:: Make sure the "prereq" directory exists
If NOT Exist "%PreDir%" mkdir "%PreDir%"
:: Set the location of the ssm to download
Set Url64="https://repo.saltstack.com/windows/dependencies/64/ssm-2.24-103-gdee49fc.exe"
@ -130,12 +128,62 @@ If Defined ProgramFiles(x86) (
)
@echo.
:: Make sure the "prereq" directory exists
If NOT Exist "%PreDir%" mkdir "%PreDir%"
:: Make sure the "prereq" directory exists and is empty
If Exist "%PreDir%" rd /s /q "%PreDir%"
mkdir "%PreDir%"
:: Don't include the vcredist for Py3 installations
If %Python%==3 goto :vcredist_end
:: Skip KB2999226 if on Py3
If %Python%==2 goto get_vcredist
:: For PY 3, include KB2999226
@echo Copying KB2999226 to Prerequisites
@echo ----------------------------------------------------------------------
:: 64 bit binaries required for AMD64 and x86
:: Copy down the 64 bit binaries
set Url60=http://repo.saltstack.com/windows/dependencies/64/ucrt/Windows6.0-KB2999226-x64.msu
set Name60=Windows6.0-KB2999226-x64.msu
set Url61=http://repo.saltstack.com/windows/dependencies/64/ucrt/Windows6.1-KB2999226-x64.msu
set Name61=Windows6.1-KB2999226-x64.msu
set Url80=http://repo.saltstack.com/windows/dependencies/64/ucrt/Windows8-RT-KB2999226-x64.msu
set Name80=Windows8-RT-KB2999226-x64.msu
set Url81=http://repo.saltstack.com/windows/dependencies/64/ucrt/Windows8.1-KB2999226-x64.msu
set Name81=Windows8.1-KB2999226-x64.msu
@echo - Downloading %Name60%
powershell -ExecutionPolicy RemoteSigned -File download_url_file.ps1 -url %Url60% -file "%PreDir%\%Name60%"
@echo - Downloading %Name61%
powershell -ExecutionPolicy RemoteSigned -File download_url_file.ps1 -url %Url61% -file "%PreDir%\%Name61%"
@echo - Downloading %Name80%
powershell -ExecutionPolicy RemoteSigned -File download_url_file.ps1 -url %Url80% -file "%PreDir%\%Name80%"
@echo - Downloading %Name81%
powershell -ExecutionPolicy RemoteSigned -File download_url_file.ps1 -url %Url81% -file "%PreDir%\%Name81%"
:: 32 bit binaries only needed for x86 installer
:: ProgramFiles(x86) is defined on AMD64 systems
:: If it's defined, skip the x86 binaries
If Defined ProgramFiles(x86) goto prereq_end
:: Copy down the 32 bit binaries
set Url60=http://repo.saltstack.com/windows/dependencies/32/ucrt/Windows6.0-KB2999226-x86.msu
set Name60=Windows6.0-KB2999226-x86.msu
set Url61=http://repo.saltstack.com/windows/dependencies/32/ucrt/Windows6.1-KB2999226-x86.msu
set Name61=Windows6.1-KB2999226-x86.msu
set Url80=http://repo.saltstack.com/windows/dependencies/32/ucrt/Windows8-RT-KB2999226-x86.msu
set Name80=Windows8-RT-KB2999226-x86.msu
set Url81=http://repo.saltstack.com/windows/dependencies/32/ucrt/Windows8.1-KB2999226-x86.msu
set Name81=Windows8.1-KB2999226-x86.msu
@echo - Downloading %Name60%
powershell -ExecutionPolicy RemoteSigned -File download_url_file.ps1 -url %Url60% -file "%PreDir%\%Name60%"
@echo - Downloading %Name61%
powershell -ExecutionPolicy RemoteSigned -File download_url_file.ps1 -url %Url61% -file "%PreDir%\%Name61%"
@echo - Downloading %Name80%
powershell -ExecutionPolicy RemoteSigned -File download_url_file.ps1 -url %Url80% -file "%PreDir%\%Name80%"
@echo - Downloading %Name81%
powershell -ExecutionPolicy RemoteSigned -File download_url_file.ps1 -url %Url81% -file "%PreDir%\%Name81%"
goto prereq_end
:: For PY 2, include VCRedist
:get_vcredist
@echo Copying VCRedist to Prerequisites
@echo ----------------------------------------------------------------------
@ -151,7 +199,7 @@ If Defined ProgramFiles(x86) (
)
@echo.
:vcredist_end
:prereq_end
:: Remove the fixed path in .exe files
@echo Removing fixed path from .exe files

View file

@ -404,15 +404,117 @@ InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
ShowInstDetails show
ShowUnInstDetails show
Section -copy_prereqs
# Copy prereqs to the Plugins Directory
# These files will be vcredist 2008 and KB2999226 for Win8.1 and below
# These files are downloaded by build_pkg.bat
# This directory gets removed upon completion
SetOutPath "$PLUGINSDIR\"
File /r "..\prereqs\"
SectionEnd
# Check and install the Windows 10 Universal C Runtime (KB2999226)
# ucrt is needed on Windows 8.1 and lower
# They are installed as a Microsoft Update package (.msu)
# ucrt for Windows 8.1 RT is only available via Windows Update
Section -install_ucrt
Var /GLOBAL MsuPrefix
Var /GLOBAL MsuFileName
# UCRT only needs to be installed for Python 3
StrCmp ${PYTHON_VERSION} 2 lbl_done
# Get the Major.Minor version Number
# Windows 10 introduced CurrentMajorVersionNumber
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" \
CurrentMajorVersionNumber
# Windows 10/2016 will return a value here, skip to the end if returned
StrCmp $R0 '' lbl_needs_ucrt 0
# Found Windows 10
detailPrint "KB2999226 does not apply to this machine"
goto lbl_done
lbl_needs_ucrt:
# UCRT only needed on Windows Server 2012R2/Windows 8.1 and below
# The first ReadRegStr command above should have skipped to lbl_done if on
# Windows 10 box
# Is the update already installed
ClearErrors
# Use WMI to check if it's installed
detailPrint "Checking for existing KB2999226 installation"
nsExec::ExecToStack 'cmd /q /c wmic qfe get hotfixid | findstr "^KB2999226"'
# Clean up the stack
Pop $R0 # Gets the ErrorCode
Pop $R1 # Gets the stdout, which should be KB2999226 if it's installed
# If it returned KB2999226 it's already installed
StrCmp $R1 'KB2999226' lbl_done
detailPrint "KB2999226 not found"
# All lower versions of Windows
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" \
CurrentVersion
# Get the name of the .msu file based on the value of $R0
${Switch} "$R0"
${Case} "6.3"
StrCpy $MsuPrefix "Windows8.1"
${break}
${Case} "6.2"
StrCpy $MsuPrefix "Windows8-RT"
${break}
${Case} "6.1"
StrCpy $MsuPrefix "Windows6.1"
${break}
${Case} "6.0"
StrCpy $MsuPrefix "Windows6.0"
${break}
${EndSwitch}
# Use RunningX64 here to get the Architecture for the system running the installer
# CPUARCH is defined when the installer is built and is based on the machine that
# built the installer, not the target system as we need here.
${If} ${RunningX64}
StrCpy $MsuFileName "$MsuPrefix-KB2999226-x64.msu"
${Else}
StrCpy $MsuFileName "$MsuPrefix-KB2999226-x86.msu"
${EndIf}
ClearErrors
detailPrint "Installing KB2999226 using file $MsuFileName"
nsExec::ExecToStack 'cmd /c wusa "$PLUGINSDIR\$MsuFileName" /quiet /norestart'
# Clean up the stack
Pop $R0 # Get Error
Pop $R1 # Get stdout
${IfNot} $R0 == 0
detailPrint "error: $R0"
detailPrint "output: $R2"
Sleep 3000
${Else}
detailPrint "KB2999226 installed successfully"
${EndIf}
lbl_done:
SectionEnd
# Check and install Visual C++ redist packages
# See http://blogs.msdn.com/b/astebner/archive/2009/01/29/9384143.aspx for more info
Section -Prerequisites
# Hidden section (-) to install VCRedist
Section -install_vcredist
Var /GLOBAL VcRedistName
Var /GLOBAL VcRedistGuid
Var /GLOBAL NeedVcRedist
Var /Global CheckVcRedist
Var /GLOBAL CheckVcRedist
StrCpy $CheckVcRedist "False"
# Visual C++ 2008 SP1 MFC Security Update redist packages
@ -446,19 +548,13 @@ Section -Prerequisites
"$VcRedistName is currently not installed. Would you like to install?" \
/SD IDYES IDNO endVcRedist
# The Correct version of VCRedist is copied over by "build_pkg.bat"
SetOutPath "$INSTDIR\"
File "..\prereqs\vcredist.exe"
# If an output variable is specified ($0 in the case below),
# ExecWait sets the variable with the exit code (and only sets the
# error flag if an error occurs; if an error occurs, the contents
# of the user variable are undefined).
# http://nsis.sourceforge.net/Reference/ExecWait
# /passive used by 2015 installer
# /qb! used by 2008 installer
# It just ignores the unrecognized switches...
ClearErrors
ExecWait '"$INSTDIR\vcredist.exe" /qb! /quiet /norestart' $0
ExecWait '"$PLUGINSDIR\vcredist.exe" /q' $0
IfErrors 0 CheckVcRedistErrorCode
MessageBox MB_OK \
"$VcRedistName failed to install. Try installing the package manually." \
@ -658,8 +754,6 @@ Section -Post
Push "C:\salt"
Call AddToPath
Delete "$INSTDIR\vcredist.exe"
SectionEnd

View file

@ -702,7 +702,7 @@ class SSH(object):
'''
Execute the overall routine, print results via outputters
'''
if self.opts['list_hosts']:
if self.opts.get('list_hosts'):
self._get_roster()
ret = {}
for roster_file in self.__parsed_rosters:

View file

@ -18,7 +18,7 @@
#======================================================================================================================
set -o nounset # Treat unset variables as an error
__ScriptVersion="2018.08.13"
__ScriptVersion="2018.08.15"
__ScriptName="bootstrap-salt.sh"
__ScriptFullName="$0"
@ -339,7 +339,7 @@ __usage() {
with -c and -k
-C Only run the configuration function. Implies -F (forced overwrite).
To overwrite Master or Syndic configs, -M or -S, respectively, must
also be specified. Salt installation will be omitted, but some of the
also be specified. Salt installation will be ommitted, but some of the
dependencies could be installed to write configuration with -j or -J.
-A Pass the salt-master DNS name or IP. This will be stored under
\${BS_SALT_ETC_DIR}/minion.d/99-master-address.conf
@ -3140,7 +3140,7 @@ install_debian_8_git_deps() {
__PACKAGES="libzmq3 libzmq3-dev lsb-release python-apt python-crypto python-jinja2"
__PACKAGES="${__PACKAGES} python-m2crypto python-msgpack python-requests python-systemd"
__PACKAGES="${__PACKAGES} python-yaml python-zmq"
__PACKAGES="${__PACKAGES} python-yaml python-zmq python-concurrent.futures"
if [ "$_INSTALL_CLOUD" -eq $BS_TRUE ]; then
# Install python-libcloud if asked to
@ -3211,7 +3211,7 @@ install_debian_9_git_deps() {
PY_PKG_VER=""
# These packages are PY2-ONLY
__PACKAGES="${__PACKAGES} python-backports-abc python-m2crypto"
__PACKAGES="${__PACKAGES} python-backports-abc python-m2crypto python-concurrent.futures"
fi
__PACKAGES="${__PACKAGES} python${PY_PKG_VER}-apt python${PY_PKG_VER}-crypto python${PY_PKG_VER}-jinja2"
@ -3429,7 +3429,7 @@ install_fedora_deps() {
fi
fi
__PACKAGES="${__PACKAGES} dnf-utils libyaml python${PY_PKG_VER}-crypto python${PY_PKG_VER}-jinja2"
__PACKAGES="${__PACKAGES} procps-ng dnf-utils libyaml python${PY_PKG_VER}-crypto python${PY_PKG_VER}-jinja2"
__PACKAGES="${__PACKAGES} python${PY_PKG_VER}-msgpack python${PY_PKG_VER}-requests python${PY_PKG_VER}-zmq"
# shellcheck disable=SC2086
@ -3800,7 +3800,7 @@ install_centos_git_deps() {
fi
fi
__PACKAGES="python${PY_PKG_VER}-crypto python${PY_PKG_VER}-jinja2"
__PACKAGES="${__PACKAGES} python${PY_PKG_VER}-crypto python${PY_PKG_VER}-jinja2"
__PACKAGES="${__PACKAGES} python${PY_PKG_VER}-msgpack python${PY_PKG_VER}-requests"
__PACKAGES="${__PACKAGES} python${PY_PKG_VER}-tornado python${PY_PKG_VER}-zmq"
@ -3815,7 +3815,7 @@ install_centos_git_deps() {
if [ "${_PY_EXE}" != "" ] && [ "$_PIP_ALLOWED" -eq "$BS_TRUE" ]; then
# If "-x" is defined, install dependencies with pip based on the Python version given.
_PIP_PACKAGES="m2crypto jinja2 msgpack-python pycrypto PyYAML tornado<5.0 zmq"
_PIP_PACKAGES="m2crypto jinja2 msgpack-python pycrypto PyYAML tornado<5.0 zmq futures>=2.0"
# install swig and openssl on cent6
if [ "$DISTRO_MAJOR_VERSION" -eq 6 ]; then
@ -4608,6 +4608,7 @@ _eof
# which is already installed
__PACKAGES="m2crypto ${pkg_append}-crypto ${pkg_append}-jinja2 ${pkg_append}-PyYAML"
__PACKAGES="${__PACKAGES} ${pkg_append}-msgpack ${pkg_append}-requests ${pkg_append}-zmq"
__PACKAGES="${__PACKAGES} ${pkg_append}-futures"
# shellcheck disable=SC2086
__yum_install_noinput ${__PACKAGES} || return 1
@ -4627,6 +4628,9 @@ install_amazon_linux_ami_git_deps() {
PIP_EXE='pip'
if __check_command_exists python2.7; then
if ! __check_command_exists pip2.7; then
if ! __check_command_exists easy_install-2.7; then
__yum_install_noinput python27-setuptools
fi
/usr/bin/easy_install-2.7 pip || return 1
fi
PIP_EXE='/usr/local/bin/pip2.7'
@ -4646,7 +4650,7 @@ install_amazon_linux_ami_git_deps() {
if [ "$_INSTALL_CLOUD" -eq $BS_TRUE ]; then
__check_pip_allowed "You need to allow pip based installations (-P) in order to install apache-libcloud"
__PACKAGES="${__PACKAGES} python-pip"
__PACKAGES="${__PACKAGES} python27-pip"
__PIP_PACKAGES="${__PIP_PACKAGES} apache-libcloud>=$_LIBCLOUD_MIN_VERSION"
fi
@ -4795,7 +4799,7 @@ install_arch_linux_stable() {
pacman -S --noconfirm --needed bash || return 1
pacman -Su --noconfirm || return 1
# We can now resume regular salt update
pacman -Syu --noconfirm salt || return 1
pacman -Syu --noconfirm salt python2-futures || return 1
return 0
}
@ -5649,7 +5653,7 @@ install_opensuse_git_deps() {
__git_clone_and_checkout || return 1
__PACKAGES="libzmq5 python-Jinja2 python-m2crypto python-msgpack-python python-pycrypto python-pyzmq python-xml"
__PACKAGES="libzmq5 python-Jinja2 python-m2crypto python-msgpack-python python-pycrypto python-pyzmq python-xml python-futures"
if [ -f "${_SALT_GIT_CHECKOUT_DIR}/requirements/base.txt" ]; then
# We're on the develop branch, install whichever tornado is on the requirements file

View file

@ -1212,9 +1212,17 @@ class SAuth(AsyncAuth):
creds = self.sign_in(channel=channel)
if creds == 'retry':
if self.opts.get('caller'):
print('Minion failed to authenticate with the master, '
'has the minion key been accepted?')
sys.exit(2)
# We have a list of masters, so we should break
# and try the next one in the list.
if self.opts.get('local_masters', None):
error = SaltClientError('Minion failed to authenticate'
' with the master, has the '
'minion key been accepted?')
break
else:
print('Minion failed to authenticate with the master, '
'has the minion key been accepted?')
sys.exit(2)
if acceptance_wait_time:
log.info('Waiting %s seconds before retry.', acceptance_wait_time)
time.sleep(acceptance_wait_time)

View file

@ -187,12 +187,14 @@ def generate_mtime_map(opts, path_map):
file_map = {}
for saltenv, path_list in six.iteritems(path_map):
for path in path_list:
for directory, dirnames, filenames in salt.utils.path.os_walk(path):
# Don't walk any directories that match file_ignore_regex or glob
dirnames[:] = [d for d in dirnames if not is_file_ignored(opts, d)]
for directory, _, filenames in salt.utils.path.os_walk(path):
for item in filenames:
try:
file_path = os.path.join(directory, item)
# Don't walk any directories that match
# file_ignore_regex or glob
if is_file_ignored(opts, file_path):
continue
file_map[file_path] = os.path.getmtime(file_path)
except (OSError, IOError):
# skip dangling symlinks

View file

@ -2948,9 +2948,9 @@ class SyndicManager(MinionBase):
if auth_wait < self.max_auth_wait:
auth_wait += self.auth_wait
yield tornado.gen.sleep(auth_wait) # TODO: log?
except KeyboardInterrupt:
except (KeyboardInterrupt, SystemExit):
raise
except: # pylint: disable=W0702
except Exception:
failed = True
log.critical(
'Unexpected error while connecting to %s',

View file

@ -4845,6 +4845,116 @@ def symbolic_ref(cwd,
output_encoding=output_encoding)['stdout']
def tag(cwd,
name,
ref='HEAD',
message=None,
opts='',
git_opts='',
user=None,
password=None,
ignore_retcode=False,
output_encoding=None):
'''
.. versionadded:: 2018.3.4
Interface to `git-tag(1)`_, adds and removes tags.
cwd
The path to the main git checkout or a linked worktree
name
Name of the tag
ref : HEAD
Which ref to tag (defaults to local clone's HEAD)
.. note::
This argument is ignored when either ``-d`` or ``--delete`` is
present in the ``opts`` passed to this function.
message
Optional message to include with the tag. If provided, an annotated tag
will be created.
opts
Any additional options to add to the command line, in a single string
.. note::
Additionally, on the Salt CLI, if the opts are preceded with a
dash, it is necessary to precede them with ``opts=`` (as in the CLI
examples below) to avoid causing errors with Salt's own argument
parsing.
git_opts
Any additional options to add to git command itself (not the
``worktree`` subcommand), in a single string. This is useful for
passing ``-c`` to run git with temporary changes to the git
configuration.
.. note::
This is only supported in git 1.7.2 and newer.
user
User under which to run the git command. By default, the command is run
by the user under which the minion is running.
password
Windows only. Required when specifying ``user``. This parameter will be
ignored on non-Windows platforms.
ignore_retcode : False
If ``True``, do not log an error to the minion log if the git command
returns a nonzero exit status.
output_encoding
Use this option to specify which encoding to use to decode the output
from any git commands which are run. This should not be needed in most
cases.
.. note::
This should only be needed if the files in the repository were
created with filenames using an encoding other than UTF-8 to handle
Unicode characters.
.. _`git-tag(1)`: http://git-scm.com/docs/git-tag
CLI Example:
.. code-block:: bash
# Create an non-annotated tag
salt myminion git.tag /path/to/repo v1.2
# Create an annotated tag
salt myminion git.tag /path/to/repo v1.2 message='Version 1.2'
# Delete the tag
salt myminion git.tag /path/to/repo v1.2 opts='-d'
'''
cwd = _expand_path(cwd, user)
command = ['git'] + _format_git_opts(git_opts)
command.append('tag')
# Don't add options for annotated tags, since we'll automatically add them
# if a message was passed. This keeps us from blocking on input, as passing
# these options without a separate message option would launch an editor.
formatted_opts = [x for x in _format_opts(opts) if x not in ('-a', '--annotate')]
# Make sure that the message was not passed in the opts
if any(x == '-m' or '--message' in x for x in formatted_opts):
raise SaltInvocationError(
'Tag messages must be passed in the "message" argument'
)
command.extend(formatted_opts)
command.append(name)
if '-d' not in formatted_opts and '--delete' not in formatted_opts:
command.append(ref)
return _git_run(command,
cwd=cwd,
user=user,
password=password,
ignore_retcode=ignore_retcode,
redirect_stderr=True,
output_encoding=output_encoding)['stdout']
def version(versioninfo=False):
'''
.. versionadded:: 2015.8.0

View file

@ -530,7 +530,7 @@ def name(device, partition, name):
'Invalid characters passed to partition.name'
)
cmd = 'parted -m -s {0} name {1} {2}'.format(device, partition, name)
cmd = '''parted -m -s {0} name {1} "'{2}'"'''.format(device, partition, name)
out = __salt__['cmd.run'](cmd).splitlines()
return out

View file

@ -19,8 +19,8 @@ Module to provide redis functionality to Salt
from __future__ import absolute_import, unicode_literals, print_function
from salt.ext.six.moves import zip
from salt.ext import six
from salt.utils import clean_kwargs
from datetime import datetime
import salt.utils.args
# Import third party libs
try:
@ -396,7 +396,7 @@ def hmset(key, **fieldsvals):
database = fieldsvals.pop('db', None)
password = fieldsvals.pop('password', None)
server = _connect(host, port, database, password)
return server.hmset(key, clean_kwargs(**fieldsvals))
return server.hmset(key, salt.utils.args.clean_kwargs(**fieldsvals))
def hset(key, field, value, host=None, port=None, db=None, password=None):

View file

@ -88,8 +88,6 @@ SERVICE_ERROR_CONTROL = {0: 'Ignore',
'severe': 2,
'critical': 3}
RETRY_ATTEMPTS = 90
def __virtual__():
'''
@ -104,6 +102,51 @@ def __virtual__():
return __virtualname__
def _status_wait(service_name, end_time, service_states):
'''
Helper function that will wait for the status of the service to match the
provided status before an end time expires. Used for service stop and start
.. versionadded:: 2017.7.9, 2018.3.4
Args:
service_name (str):
The name of the service
end_time (float):
A future time. e.g. time.time() + 10
service_states (list):
Services statuses to wait for as returned by info()
Returns:
dict: A dictionary containing information about the service.
:codeauthor: Damon Atkins <https://github.com/damon-atkins>
'''
info_results = info(service_name)
while info_results['Status'] in service_states and time.time() < end_time:
# From Microsoft: Do not wait longer than the wait hint. A good interval
# is one-tenth of the wait hint but not less than 1 second and not more
# than 10 seconds.
# https://docs.microsoft.com/en-us/windows/desktop/services/starting-a-service
# https://docs.microsoft.com/en-us/windows/desktop/services/stopping-a-service
# Wait hint is in ms
wait_time = info_results['Status_WaitHint']
# Convert to seconds or 0
wait_time = wait_time / 1000 if wait_time else 0
if wait_time < 1:
wait_time = 1
elif wait_time > 10:
wait_time = 10
time.sleep(wait_time)
info_results = info(service_name)
return info_results
def get_enabled():
'''
Return a list of enabled services. Enabled is defined as a service that is
@ -378,7 +421,7 @@ def info(name):
return ret
def start(name):
def start(name, timeout=90):
'''
Start the specified service.
@ -389,8 +432,14 @@ def start(name):
Args:
name (str): The name of the service to start
timeout (int):
The time in seconds to wait for the service to start before
returning. Default is 90 seconds
.. versionadded:: 2017.7.9, 2018.3.4
Returns:
bool: True if successful, False otherwise
bool: ``True`` if successful, otherwise ``False``
CLI Example:
@ -411,24 +460,28 @@ def start(name):
raise CommandExecutionError(
'Failed To Start {0}: {1}'.format(name, exc[2]))
attempts = 0
while info(name)['Status'] in ['Start Pending', 'Stopped'] \
and attempts <= RETRY_ATTEMPTS:
time.sleep(1)
attempts += 1
srv_status = _status_wait(service_name=name,
end_time=time.time() + int(timeout),
service_states=['Start Pending', 'Stopped'])
return status(name)
return srv_status['Status'] == 'Running'
def stop(name):
def stop(name, timeout=90):
'''
Stop the specified service
Args:
name (str): The name of the service to stop
timeout (int):
The time in seconds to wait for the service to stop before
returning. Default is 90 seconds
.. versionadded:: 2017.7.9, 2018.3.4
Returns:
bool: True if successful, False otherwise
bool: ``True`` if successful, otherwise ``False``
CLI Example:
@ -436,15 +489,6 @@ def stop(name):
salt '*' service.stop <service name>
'''
# net stop issues a stop command and waits briefly (~30s), but will give
# up if the service takes too long to stop with a misleading
# "service could not be stopped" message and RC 0.
cmd = ['net', 'stop', '/y', name]
res = __salt__['cmd.run'](cmd, python_shell=False)
if 'service was stopped' in res:
return True
try:
win32serviceutil.StopService(name)
except pywintypes.error as exc:
@ -452,28 +496,37 @@ def stop(name):
raise CommandExecutionError(
'Failed To Stop {0}: {1}'.format(name, exc[2]))
attempts = 0
while info(name)['Status'] in ['Running', 'Stop Pending'] \
and attempts <= RETRY_ATTEMPTS:
time.sleep(1)
attempts += 1
srv_status = _status_wait(service_name=name,
end_time=time.time() + int(timeout),
service_states=['Running', 'Stop Pending'])
return not status(name)
return srv_status['Status'] == 'Stopped'
def restart(name):
def restart(name, timeout=90):
'''
Restart the named service. This issues a stop command followed by a start.
Args:
name: The name of the service to restart.
.. note::
If the name passed is ``salt-minion`` a scheduled task is created and
executed to restart the salt-minion service.
.. note::
If the name passed is ``salt-minion`` a scheduled task is
created and executed to restart the salt-minion service.
timeout (int):
The time in seconds to wait for the service to stop and start before
returning. Default is 90 seconds
.. note::
The timeout is cumulative meaning it is applied to the stop and
then to the start command. A timeout of 90 could take up to 180
seconds if the service is long in stopping and starting
.. versionadded:: 2017.7.9, 2018.3.4
Returns:
bool: ``True`` if successful, ``False`` otherwise
bool: ``True`` if successful, otherwise ``False``
CLI Example:
@ -485,7 +538,8 @@ def restart(name):
create_win_salt_restart_task()
return execute_salt_restart_task()
return stop(name) and start(name)
return stop(name=name, timeout=timeout) and \
start(name=name, timeout=timeout)
def create_win_salt_restart_task():
@ -531,7 +585,7 @@ def execute_salt_restart_task():
return __salt__['task.run'](name='restart-salt-minion')
def status(name, sig=None):
def status(name, *args, **kwargs):
'''
Return the status for a service.
If the name contains globbing, a dict mapping service name to True/False
@ -542,7 +596,6 @@ def status(name, sig=None):
Args:
name (str): The name of the service to check
sig (str): Not supported on Windows
Returns:
bool: True if running, False otherwise
@ -610,20 +663,26 @@ def modify(name,
.. versionadded:: 2016.11.0
Args:
name (str): The name of the service. Can be found using the
name (str):
The name of the service. Can be found using the
``service.get_service_name`` function
bin_path (str): The path to the service executable. Backslashes must be
escaped, eg: C:\\path\\to\\binary.exe
bin_path (str):
The path to the service executable. Backslashes must be escaped, eg:
``C:\\path\\to\\binary.exe``
exe_args (str): Any arguments required by the service executable
exe_args (str):
Any arguments required by the service executable
display_name (str): The name to display in the service manager
display_name (str):
The name to display in the service manager
description (str): The description to display for the service
description (str):
The description to display for the service
service_type (str): Specifies the service type. Default is ``own``.
Valid options are as follows:
service_type (str):
Specifies the service type. Default is ``own``. Valid options are as
follows:
- kernel: Driver service
- filesystem: File system driver service
@ -632,8 +691,8 @@ def modify(name,
- own (default): Service runs in its own process
- share: Service shares a process with one or more other services
start_type (str): Specifies the service start type. Valid options are as
follows:
start_type (str):
Specifies the service start type. Valid options are as follows:
- boot: Device driver that is loaded by the boot loader
- system: Device driver that is started during kernel initialization
@ -641,13 +700,14 @@ def modify(name,
- manual: Service must be started manually
- disabled: Service cannot be started
start_delayed (bool): Set the service to Auto(Delayed Start). Only valid
if the start_type is set to ``Auto``. If service_type is not passed,
but the service is already set to ``Auto``, then the flag will be
set.
start_delayed (bool):
Set the service to Auto(Delayed Start). Only valid if the start_type
is set to ``Auto``. If service_type is not passed, but the service
is already set to ``Auto``, then the flag will be set.
error_control (str): The severity of the error, and action taken, if
this service fails to start. Valid options are as follows:
error_control (str):
The severity of the error, and action taken, if this service fails
to start. Valid options are as follows:
- normal: Error is logged and a message box is displayed
- severe: Error is logged and computer attempts a restart with the
@ -657,29 +717,33 @@ def modify(name,
- ignore: Error is logged and startup continues, no notification is
given to the user
load_order_group: The name of the load order group to which this service
belongs
load_order_group (str):
The name of the load order group to which this service belongs
dependencies (list): A list of services or load ordering groups that
must start before this service
dependencies (list):
A list of services or load ordering groups that must start before
this service
account_name (str): The name of the account under which the service
should run. For ``own`` type services this should be in the
``domain\\username`` format. The following are examples of valid
built-in service accounts:
account_name (str):
The name of the account under which the service should run. For
``own`` type services this should be in the ``domain\\username``
format. The following are examples of valid built-in service
accounts:
- NT Authority\\LocalService
- NT Authority\\NetworkService
- NT Authority\\LocalSystem
- .\LocalSystem
account_password (str): The password for the account name specified in
``account_name``. For the above built-in accounts, this can be None.
Otherwise a password must be specified.
account_password (str):
The password for the account name specified in ``account_name``. For
the above built-in accounts, this can be None. Otherwise a password
must be specified.
run_interactive (bool): If this setting is True, the service will be
allowed to interact with the user. Not recommended for services that
run with elevated privileges.
run_interactive (bool):
If this setting is True, the service will be allowed to interact
with the user. Not recommended for services that run with elevated
privileges.
Returns:
dict: a dictionary of changes made
@ -936,20 +1000,26 @@ def create(name,
Args:
name (str): Specifies the service name. This is not the display_name
name (str):
Specifies the service name. This is not the display_name
bin_path (str): Specifies the path to the service binary file.
Backslashes must be escaped, eg: C:\\path\\to\\binary.exe
bin_path (str):
Specifies the path to the service binary file. Backslashes must be
escaped, eg: ``C:\\path\\to\\binary.exe``
exe_args (str): Any additional arguments required by the service binary.
exe_args (str):
Any additional arguments required by the service binary.
display_name (str): the name to be displayed in the service manager. If
not passed, the ``name`` will be used
display_name (str):
The name to be displayed in the service manager. If not passed, the
``name`` will be used
description (str): A description of the service
description (str):
A description of the service
service_type (str): Specifies the service type. Default is ``own``.
Valid options are as follows:
service_type (str):
Specifies the service type. Default is ``own``. Valid options are as
follows:
- kernel: Driver service
- filesystem: File system driver service
@ -958,8 +1028,8 @@ def create(name,
- own (default): Service runs in its own process
- share: Service shares a process with one or more other services
start_type (str): Specifies the service start type. Valid options are as
follows:
start_type (str):
Specifies the service start type. Valid options are as follows:
- boot: Device driver that is loaded by the boot loader
- system: Device driver that is started during kernel initialization
@ -967,13 +1037,15 @@ def create(name,
- manual (default): Service must be started manually
- disabled: Service cannot be started
start_delayed (bool): Set the service to Auto(Delayed Start). Only valid
if the start_type is set to ``Auto``. If service_type is not passed,
but the service is already set to ``Auto``, then the flag will be
set. Default is ``False``
start_delayed (bool):
Set the service to Auto(Delayed Start). Only valid if the start_type
is set to ``Auto``. If service_type is not passed, but the service
is already set to ``Auto``, then the flag will be set. Default is
``False``
error_control (str): The severity of the error, and action taken, if
this service fails to start. Valid options are as follows:
error_control (str):
The severity of the error, and action taken, if this service fails
to start. Valid options are as follows:
- normal (normal): Error is logged and a message box is displayed
- severe: Error is logged and computer attempts a restart with the
@ -983,29 +1055,33 @@ def create(name,
- ignore: Error is logged and startup continues, no notification is
given to the user
load_order_group: The name of the load order group to which this service
belongs
load_order_group (str):
The name of the load order group to which this service belongs
dependencies (list): A list of services or load ordering groups that
must start before this service
dependencies (list):
A list of services or load ordering groups that must start before
this service
account_name (str): The name of the account under which the service
should run. For ``own`` type services this should be in the
``domain\\username`` format. The following are examples of valid
built-in service accounts:
account_name (str):
The name of the account under which the service should run. For
``own`` type services this should be in the ``domain\\username``
format. The following are examples of valid built-in service
accounts:
- NT Authority\\LocalService
- NT Authority\\NetworkService
- NT Authority\\LocalSystem
- .\\LocalSystem
account_password (str): The password for the account name specified in
``account_name``. For the above built-in accounts, this can be None.
Otherwise a password must be specified.
account_password (str):
The password for the account name specified in ``account_name``. For
the above built-in accounts, this can be None. Otherwise a password
must be specified.
run_interactive (bool): If this setting is True, the service will be
allowed to interact with the user. Not recommended for services that
run with elevated privileges.
run_interactive (bool):
If this setting is True, the service will be allowed to interact
with the user. Not recommended for services that run with elevated
privileges.
Returns:
dict: A dictionary containing information about the new service
@ -1096,15 +1172,23 @@ def create(name,
return info(name)
def delete(name):
def delete(name, timeout=90):
'''
Delete the named service
Args:
name (str): The name of the service to delete
timeout (int):
The time in seconds to wait for the service to be deleted before
returning. This is necessary because a service must be stopped
before it can be deleted. Default is 90 seconds
.. versionadded:: 2017.7.9, 2018.3.4
Returns:
bool: True if successful, False otherwise
bool: ``True`` if successful, otherwise ``False``
CLI Example:
@ -1120,16 +1204,20 @@ def delete(name):
handle_scm, name, win32service.SERVICE_ALL_ACCESS)
except pywintypes.error as exc:
raise CommandExecutionError(
'Failed To Open {0}: {1}'.format(name, exc[2]))
'Failed to open {0}. {1}'.format(name, exc.strerror))
win32service.DeleteService(handle_svc)
try:
win32service.DeleteService(handle_svc)
except pywintypes.error as exc:
raise CommandExecutionError(
'Failed to delete {0}. {1}'.format(name, exc.strerror))
finally:
log.debug('Cleaning up')
win32service.CloseServiceHandle(handle_scm)
win32service.CloseServiceHandle(handle_svc)
win32service.CloseServiceHandle(handle_scm)
win32service.CloseServiceHandle(handle_svc)
attempts = 0
while name in get_all() and attempts <= RETRY_ATTEMPTS:
end_time = time.time() + int(timeout)
while name in get_all() and time.time() < end_time:
time.sleep(1)
attempts += 1
return name not in get_all()

View file

@ -111,12 +111,14 @@ class NestDisplay(object):
)
# Number includes all python numbers types
# (float, int, long, complex, ...)
# use repr() to get the full precision also for older python versions
# as until about python32 it was limited to 12 digits only by default
elif isinstance(ret, Number):
out.append(
self.ustring(
indent,
self.LIGHT_YELLOW,
ret,
repr(ret),
prefix=prefix
)
)

View file

@ -2656,8 +2656,15 @@ def managed(name,
ret['changes'] = {}
log.debug(traceback.format_exc())
salt.utils.files.remove(tmp_filename)
if not keep_source and sfn:
salt.utils.files.remove(sfn)
if not keep_source:
if not sfn \
and source \
and _urlparse(source).scheme == 'salt':
# The file would not have been cached until manage_file was
# run, so check again here for a cached copy.
sfn = __salt__['cp.is_cached'](source, __env__)
if sfn:
salt.utils.files.remove(sfn)
return _error(ret, 'Unable to check_cmd file: {0}'.format(exc))
# file being updated to verify using check_cmd
@ -2729,8 +2736,15 @@ def managed(name,
finally:
if tmp_filename:
salt.utils.files.remove(tmp_filename)
if not keep_source and sfn:
salt.utils.files.remove(sfn)
if not keep_source:
if not sfn \
and source \
and _urlparse(source).scheme == 'salt':
# The file would not have been cached until manage_file was
# run, so check again here for a cached copy.
sfn = __salt__['cp.is_cached'](source, __env__)
if sfn:
salt.utils.files.remove(sfn)
_RECURSE_TYPES = ['user', 'group', 'mode', 'ignore_files', 'ignore_dirs']

View file

@ -158,7 +158,12 @@ def _uptodate(ret, target, comments=None, local_changes=False):
# Shouldn't be making any changes if the repo was up to date, but
# report on them so we are alerted to potential problems with our
# logic.
ret['comment'] += '\n\nChanges made: ' + comments
ret['comment'] += (
'\n\nChanges {0}made: {1}'.format(
'that would be ' if __opts__['test'] else '',
_format_comments(comments)
)
)
return ret
@ -171,8 +176,7 @@ def _neutral_test(ret, comment):
def _fail(ret, msg, comments=None):
ret['result'] = False
if comments:
msg += '\n\nChanges already made: '
msg += _format_comments(comments)
msg += '\n\nChanges already made: ' + _format_comments(comments)
ret['comment'] = msg
return ret
@ -184,8 +188,12 @@ def _already_cloned(ret, target, branch=None, comments=None):
' and is checked out to branch \'{0}\''.format(branch) if branch else ''
)
if comments:
ret['comment'] += '\n\nChanges already made: '
ret['comment'] += _format_comments(comments)
ret['comment'] += (
'\n\nChanges {0}made: {1}'.format(
'that would be ' if __opts__['test'] else '',
_format_comments(comments)
)
)
return ret
@ -268,6 +276,7 @@ def latest(name,
mirror=False,
remote='origin',
fetch_tags=True,
sync_tags=True,
depth=None,
identity=None,
https_user=None,
@ -460,6 +469,12 @@ def latest(name,
If ``True``, then when a fetch is performed all tags will be fetched,
even those which are not reachable by any branch on the remote.
sync_tags : True
If ``True``, then Salt will delete tags which exist in the local clone
but are not found on the remote repository.
.. versionadded:: 2018.3.4
depth
Defines depth in history when git a clone is needed in order to ensure
latest. E.g. ``depth: 1`` is useful when deploying from a repository
@ -851,11 +866,14 @@ def latest(name,
user=user,
password=password,
output_encoding=output_encoding)
all_local_tags = __salt__['git.list_tags'](
target,
user=user,
password=password,
output_encoding=output_encoding)
all_local_tags = set(
__salt__['git.list_tags'](
target,
user=user,
password=password,
output_encoding=output_encoding
)
)
local_rev, local_branch = _get_local_rev_and_branch(
target,
user,
@ -1370,11 +1388,43 @@ def latest(name,
ignore_retcode=True,
output_encoding=output_encoding) if '^{}' not in x
])
if set(all_local_tags) != remote_tags:
if all_local_tags != remote_tags:
has_remote_rev = False
ret['changes']['new_tags'] = list(remote_tags.symmetric_difference(
all_local_tags
))
new_tags = remote_tags - all_local_tags
deleted_tags = all_local_tags - remote_tags
if new_tags:
ret['changes']['new_tags'] = new_tags
if sync_tags and deleted_tags:
# Delete the local copy of the tags to keep up with the
# remote repository.
for tag_name in deleted_tags:
try:
if not __opts__['test']:
__salt__['git.tag'](
target,
tag_name,
opts='-d',
user=user,
password=password,
output_encoding=output_encoding)
except CommandExecutionError as exc:
ret.setdefault('warnings', []).append(
'Failed to remove local tag \'{0}\':\n\n'
'{1}\n\n'.format(tag_name, exc)
)
else:
ret['changes'].setdefault(
'deleted_tags', []).append(tag_name)
if ret['changes'].get('deleted_tags'):
comments.append(
'The following tags {0} removed from the local '
'checkout: {1}'.format(
'would be' if __opts__['test']
else 'were',
', '.join(ret['changes']['deleted_tags'])
)
)
if not has_remote_rev:
try:

View file

@ -199,6 +199,21 @@ def mounted(name,
update_mount_cache = False
if not name:
ret['result'] = False
ret['comment'] = 'Must provide name to mount.mounted'
return ret
if not device:
ret['result'] = False
ret['comment'] = 'Must provide device to mount.mounted'
return ret
if not fstype:
ret['result'] = False
ret['comment'] = 'Must provide fstype to mount.mounted'
return ret
if device_name_regex is None:
device_name_regex = []
@ -588,11 +603,11 @@ def mounted(name,
if __opts__['test']:
if __grains__['os'] in ['MacOS', 'Darwin']:
out = __salt__['mount.set_automaster'](name,
device,
fstype,
opts,
config,
test=True)
device,
fstype,
opts,
config,
test=True)
elif __grains__['os'] in ['AIX']:
out = __salt__['mount.set_filesystems'](name,
device,
@ -646,10 +661,10 @@ def mounted(name,
else:
if __grains__['os'] in ['MacOS', 'Darwin']:
out = __salt__['mount.set_automaster'](name,
device,
fstype,
opts,
config)
device,
fstype,
opts,
config)
elif __grains__['os'] in ['AIX']:
out = __salt__['mount.set_filesystems'](name,
device,
@ -830,6 +845,11 @@ def unmounted(name,
update_mount_cache = False
if not name:
ret['result'] = False
ret['comment'] = 'Must provide name to mount.unmounted'
return ret
# Get the active data
active = __salt__['mount.active'](extended=True)
if name not in active:

View file

@ -437,16 +437,15 @@ def running(name,
ret['comment'] = 'Service {0} is set to start'.format(name)
return ret
if salt.utils.platform.is_windows():
if enable is True:
ret.update(_enable(name, False, result=False, **kwargs))
# Conditionally add systemd-specific args to call to service.start
start_kwargs, warnings = \
_get_systemd_only(__salt__['service.start'], locals())
if warnings:
ret.setdefault('warnings', []).extend(warnings)
if salt.utils.platform.is_windows() and kwargs.get('timeout', False):
start_kwargs.update({'timeout': kwargs.get('timeout')})
try:
func_ret = __salt__['service.start'](name, **start_kwargs)
except CommandExecutionError as exc:
@ -590,6 +589,9 @@ def dead(name,
if warnings:
ret.setdefault('warnings', []).extend(warnings)
if salt.utils.platform.is_windows() and kwargs.get('timeout', False):
stop_kwargs.update({'timeout': kwargs.get('timeout')})
func_ret = __salt__['service.stop'](name, **stop_kwargs)
if not func_ret:
ret['result'] = False

View file

@ -5,16 +5,13 @@ Utilities to enable exception reraising across the master commands
'''
from __future__ import absolute_import, unicode_literals, print_function
# Import python libs
try:
import exceptions
except ImportError:
pass
# Import salt libs
import salt.exceptions
import salt.utils.event
# Import 3rd-party libs
from salt.ext.six.moves import builtins as exceptions
def raise_error(name=None, args=None, message=''):
'''

View file

@ -706,7 +706,7 @@ class CkMinions(object):
'S': 'ipcidr',
'E': 'pcre',
'N': 'node',
None: 'glob'}
None: 'compound'}
target_info = parse_target(auth_entry)
if not target_info:

View file

@ -71,7 +71,7 @@ def daemonize(redirect_out=True):
if pid > 0:
# exit first parent
salt.utils.crypt.reinit_crypto()
sys.exit(salt.defaults.exitcodes.EX_OK)
os._exit(salt.defaults.exitcodes.EX_OK)
except OSError as exc:
log.error('fork #1 failed: %s (%s)', exc.errno, exc)
sys.exit(salt.defaults.exitcodes.EX_GENERIC)

View file

@ -40,6 +40,8 @@ class TimedProc(object):
if self.timeout and not isinstance(self.timeout, (int, float)):
raise salt.exceptions.TimedProcTimeoutError('Error: timeout {0} must be a number'.format(self.timeout))
if kwargs.get('shell', False):
args = salt.utils.stringutils.to_bytes(args)
try:
self.process = subprocess.Popen(args, **kwargs)

View file

@ -742,6 +742,7 @@ class TestDaemon(object):
master_opts['config_dir'] = RUNTIME_VARS.TMP_CONF_DIR
master_opts['root_dir'] = os.path.join(TMP, 'rootdir')
master_opts['pki_dir'] = os.path.join(TMP, 'rootdir', 'pki', 'master')
master_opts['syndic_master'] = 'localhost'
# This is the syndic for master
# Let's start with a copy of the syndic master configuration
@ -1126,7 +1127,10 @@ class TestDaemon(object):
for dirname in (TMP, RUNTIME_VARS.TMP_STATE_TREE,
RUNTIME_VARS.TMP_PILLAR_TREE, RUNTIME_VARS.TMP_PRODENV_STATE_TREE):
if os.path.isdir(dirname):
shutil.rmtree(six.text_type(dirname), onerror=remove_readonly)
try:
shutil.rmtree(six.text_type(dirname), onerror=remove_readonly)
except Exception:
log.exception('Failed to remove directory: %s', dirname)
def wait_for_jid(self, targets, jid, timeout=120):
time.sleep(1) # Allow some time for minions to accept jobs

View file

@ -12,6 +12,7 @@ import yaml
from salt.config import cloud_providers_config
import salt.utils.cloud
import salt.utils.files
import salt.utils.yaml
# Import Salt Testing Libs
from tests.support.case import ShellCase
@ -121,7 +122,7 @@ class EC2Test(ShellCase):
conf = yaml.safe_load(fp)
conf[name].update(data)
with salt.utils.files.fopen(conf_path, 'w') as fp:
yaml.dump(conf, fp)
salt.utils.yaml.safe_dump(conf, fp)
def copy_file(self, name):
'''

View file

@ -7,4 +7,4 @@ azure-test:
ssh_username: ''
ssh_password: ''
media_link: ''
script_args: '-P -Z'
script_args: '-P'

View file

@ -2,4 +2,4 @@ digitalocean-test:
provider: digitalocean-config
image: 14.04.5 x64
size: 2GB
script_args: '-P -Z'
script_args: '-P'

View file

@ -3,7 +3,7 @@ ec2-test:
image: ami-98aa1cf0
size: m1.large
sh_username: ec2-user
script_args: '-P -Z'
script_args: '-P'
ec2-win2012r2-test:
provider: ec2-config
size: m1.large

View file

@ -2,4 +2,4 @@ gogrid-test:
provider: gogrid-config
size: 512MB
image: Ubuntu 14.04 LTS Server (64-bit) w/ None
script_args: '-P -Z'
script_args: '-P'

View file

@ -3,4 +3,4 @@ joyent-test:
size: k4-highcpu-kvm-250M
image: ubuntu-16.04
location: us-east-1
script_args: '-P -Z'
script_args: '-P'

View file

@ -2,4 +2,4 @@ linode-test:
provider: linode-config
size: Linode 2GB
image: Ubuntu 14.04 LTS
script_args: '-P -Z'
script_args: '-P'

View file

@ -2,4 +2,4 @@ rackspace-test:
provider: openstack-config
size: 2 GB Performance
image: Ubuntu 14.04 LTS (Trusty Tahr) (PVHVM)
script_args: '-P -Z'
script_args: '-P'

View file

@ -31,12 +31,12 @@ class ShadowModuleTest(ModuleCase):
super(self.__class__, self).__init__(arg)
self._test_user = self.__random_string()
self._no_user = self.__random_string()
self._password = self.run_function('shadow.gen_password', ['Password1234'])
def setUp(self):
'''
Get current settings
'''
self._password = self.run_function('shadow.gen_password', ['Password1234'])
if 'ERROR' in self._password:
self.fail('Failed to generate password: {0}'.format(self._password))
super(ShadowModuleTest, self).setUp()

View file

@ -1953,7 +1953,10 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin):
_expected = 'This is Æ test!'
if salt.utils.platform.is_windows():
# Windows cmd.exe will mangle the output using cmd's codepage.
_expected = "'This is test!'"
if six.PY2:
_expected = "'This is A+ test!'"
else:
_expected = "'This is test!'"
self.assertEqual(_expected, ret[key]['changes']['stdout'])
def tearDown(self):

View file

@ -363,7 +363,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
with salt.utils.files.fopen(grain_path, 'r') as fp_:
file_contents = fp_.readlines()
if salt.utils.platform.is_windows():
if IS_WINDOWS:
match = '^minion\r\n'
else:
match = '^minion\n'
@ -592,8 +592,8 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
name = os.path.join(TMP, 'local_source_with_source_hash')
local_path = os.path.join(BASE_FILES, 'grail', 'scene33')
actual_hash = '567fd840bf1548edc35c48eb66cdd78bfdfcccff'
if salt.utils.platform.is_windows():
# CRLF vs LF causes a differnt hash on windows
if IS_WINDOWS:
# CRLF vs LF causes a different hash on windows
actual_hash = 'f658a0ec121d9c17088795afcc6ff3c43cb9842a'
# Reverse the actual hash
bad_hash = actual_hash[::-1]
@ -677,7 +677,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
'-{0}_|-managed'.format(name)
local_path = os.path.join(BASE_FILES, 'hello_world.txt')
actual_hash = 'c98c24b677eff44860afea6f493bbaec5bb1c4cbb209c6fc2bbb47f66ff2ad31'
if salt.utils.platform.is_windows():
if IS_WINDOWS:
# CRLF vs LF causes a differnt hash on windows
actual_hash = '92b772380a3f8e27a93e57e6deeca6c01da07f5aadce78bb2fbb20de10a66925'
uppercase_hash = actual_hash.upper()
@ -743,6 +743,29 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
diff_lines = ret['changes']['diff'].split(os.linesep)
assert '+räksmörgås' in diff_lines, diff_lines
@with_tempfile()
def test_managed_keep_source_false_salt(self, name):
'''
This test ensures that we properly clean the cached file if keep_source
is set to False, for source files using a salt:// URL
'''
source = 'salt://grail/scene33'
saltenv = 'base'
# Run the state
ret = self.run_state(
'file.managed',
name=name,
source=source,
saltenv=saltenv,
keep_source=False)
ret = ret[next(iter(ret))]
assert ret['result'] is True
# Now make sure that the file is not cached
result = self.run_function('cp.is_cached', [source, saltenv])
assert result == '', 'File is still cached at {0}'.format(result)
def test_directory(self):
'''
file.directory
@ -776,7 +799,6 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
ret = self.run_state(
'file.directory', test=True, name=sym_dir,
follow_symlinks=True, mode=700)
self.assertSaltTrueReturn(ret)
finally:
if os.path.isdir(tmp_dir):
@ -861,14 +883,10 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
self.assertFalse(os.path.exists(straydir))
self.assertTrue(os.path.isdir(name))
@skipIf(salt.utils.platform.is_windows(), 'Skip on windows')
@with_tempdir()
def test_directory_clean_exclude(self, base_dir):
'''
file.directory with clean=True and exclude_pat set
Skipped on windows because clean and exclude_pat not supported by
salt.sates.file._check_directory_win
'''
name = os.path.join(base_dir, 'directory_clean_dir')
if not os.path.isdir(name):
@ -904,11 +922,14 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
self.assertFalse(os.path.exists(strayfile2))
self.assertTrue(os.path.exists(keepfile))
@skipIf(salt.utils.is_windows(), 'Skip on windows')
@skipIf(IS_WINDOWS, 'Skip on windows')
@with_tempdir()
def test_test_directory_clean_exclude(self, base_dir):
'''
file.directory with test=True, clean=True and exclude_pat set
Skipped on windows because clean and exclude_pat not supported by
salt.sates.file._check_directory_win
'''
name = os.path.join(base_dir, 'directory_clean_dir')
os.mkdir(name)
@ -1231,7 +1252,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
self.assertTrue(os.path.isfile(os.path.join(name, '32', 'scene')))
self.assertTrue(os.path.isfile(os.path.join(name, 'scene34')))
@skipIf(salt.utils.platform.is_windows(), 'Skip on windows')
@skipIf(IS_WINDOWS, 'Skip on windows')
@with_tempdir()
def test_recurse_issue_34945(self, base_dir):
'''
@ -1273,7 +1294,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
name=name,
source='salt://соль')
self.assertSaltTrueReturn(ret)
if six.PY2 and salt.utils.platform.is_windows():
if six.PY2 and IS_WINDOWS:
# Providing unicode to os.listdir so that we avoid having listdir
# try to decode the filenames using the systemencoding on windows
# python 2.
@ -2098,7 +2119,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
'#-- end salt managed zoneend --',
'']
self.assertEqual(expected, contents)
self.assertEqual([salt.utils.stringutils.to_str(line) for line in expected], contents)
@with_tempdir()
def test_issue_11003_immutable_lazy_proxy_sum(self, base_dir):
@ -2190,6 +2211,10 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
{korean_3}
- replace: True
- show_diff: True
'''.format(**locals()))
if not salt.utils.platform.is_windows():
template += textwrap.dedent('''\
some-utf8-file-content-test:
cmd.run:
- name: 'cat "{test_file}"'
@ -2240,18 +2265,28 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
ret['some-utf8-file-create2']['changes'],
{'diff': diff}
)
# Confirm that the file has the expected contents as specified in
# the prior state.
self.assertEqual(
ret['some-utf8-file-content-test']['comment'],
'Command "cat "{0}"" run'.format(test_file_encoded)
)
self.assertEqual(
ret['some-utf8-file-content-test']['changes']['stdout'],
'\n'.join((korean_2, korean_1, korean_3))
)
if salt.utils.platform.is_windows():
import subprocess
import win32api
p = subprocess.Popen(salt.utils.to_str('type {}'.format(win32api.GetShortPathName(test_file))),
shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.poll()
out = p.stdout.read()
self.assertEqual(
out.decode('utf-8'),
os.linesep.join((korean_2, korean_1, korean_3)) + os.linesep
)
else:
self.assertEqual(
ret['some-utf8-file-content-test']['comment'],
'Command "cat "{0}"" run'.format(
test_file_encoded
)
)
self.assertEqual(
ret['some-utf8-file-content-test']['changes']['stdout'],
'\n'.join((korean_2, korean_1, korean_3))
)
finally:
try:
os.remove(template_path)
@ -2496,18 +2531,18 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin):
marker_start = '# start'
marker_end = '# end'
content = os.linesep.join([
content = six.text_type(os.linesep.join([
'Line 1 of block',
'Line 2 of block',
''
])
without_block = os.linesep.join([
]))
without_block = six.text_type(os.linesep.join([
'Hello world!',
'',
'# comment here',
''
])
with_non_matching_block = os.linesep.join([
]))
with_non_matching_block = six.text_type(os.linesep.join([
'Hello world!',
'',
'# start',
@ -2515,16 +2550,16 @@ class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin):
'# end',
'# comment here',
''
])
with_non_matching_block_and_marker_end_not_after_newline = os.linesep.join([
]))
with_non_matching_block_and_marker_end_not_after_newline = six.text_type(os.linesep.join([
'Hello world!',
'',
'# start',
'No match here# end',
'# comment here',
''
])
with_matching_block = os.linesep.join([
]))
with_matching_block = six.text_type(os.linesep.join([
'Hello world!',
'',
'# start',
@ -2533,8 +2568,8 @@ class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin):
'# end',
'# comment here',
''
])
with_matching_block_and_extra_newline = os.linesep.join([
]))
with_matching_block_and_extra_newline = six.text_type(os.linesep.join([
'Hello world!',
'',
'# start',
@ -2544,8 +2579,8 @@ class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin):
'# end',
'# comment here',
''
])
with_matching_block_and_marker_end_not_after_newline = os.linesep.join([
]))
with_matching_block_and_marker_end_not_after_newline = six.text_type(os.linesep.join([
'Hello world!',
'',
'# start',
@ -2553,7 +2588,7 @@ class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin):
'Line 2 of block# end',
'# comment here',
''
])
]))
content_explicit_posix_newlines = ('Line 1 of block\n'
'Line 2 of block\n')
content_explicit_windows_newlines = ('Line 1 of block\r\n'
@ -3711,8 +3746,8 @@ class RemoteFileTest(ModuleCase, SaltReturnAssertsMixin):
cls.webserver = Webserver()
cls.webserver.start()
cls.source = cls.webserver.url('grail/scene33')
if salt.utils.platform.is_windows():
# CRLF vs LF causes a differnt hash on windows
if IS_WINDOWS:
# CRLF vs LF causes a different hash on windows
cls.source_hash = '21438b3d5fd2c0028bcab92f7824dc69'
else:
cls.source_hash = 'd2feb3beb323c79fc7a0f44f1408b4a3'
@ -3732,6 +3767,11 @@ class RemoteFileTest(ModuleCase, SaltReturnAssertsMixin):
if exc.errno != errno.ENOENT:
raise exc
def run_state(self, *args, **kwargs):
ret = super(RemoteFileTest, self).run_state(*args, **kwargs)
log.debug('ret = %s', ret)
return ret
def test_file_managed_http_source_no_hash(self):
'''
Test a remote file with no hash
@ -3740,7 +3780,6 @@ class RemoteFileTest(ModuleCase, SaltReturnAssertsMixin):
name=self.name,
source=self.source,
skip_verify=False)
log.debug('ret = %s', ret)
# This should fail because no hash was provided
self.assertSaltFalseReturn(ret)
@ -3753,7 +3792,6 @@ class RemoteFileTest(ModuleCase, SaltReturnAssertsMixin):
source=self.source,
source_hash=self.source_hash,
skip_verify=False)
log.debug('ret = %s', ret)
self.assertSaltTrueReturn(ret)
def test_file_managed_http_source_skip_verify(self):
@ -3764,9 +3802,27 @@ class RemoteFileTest(ModuleCase, SaltReturnAssertsMixin):
name=self.name,
source=self.source,
skip_verify=True)
log.debug('ret = %s', ret)
self.assertSaltTrueReturn(ret)
def test_file_managed_keep_source_false_http(self):
'''
This test ensures that we properly clean the cached file if keep_source
is set to False, for source files using an http:// URL
'''
# Run the state
ret = self.run_state('file.managed',
name=self.name,
source=self.source,
source_hash=self.source_hash,
keep_source=False)
ret = ret[next(iter(ret))]
assert ret['result'] is True
# Now make sure that the file is not cached
result = self.run_function('cp.is_cached', [self.source])
assert result == '', 'File is still cached at {0}'.format(result)
WIN_TEST_FILE = 'c:/testfile'

View file

@ -8,18 +8,24 @@ from __future__ import absolute_import, print_function, unicode_literals
import functools
import inspect
import os
import shutil
import socket
import string
import tempfile
# Import Salt Testing libs
from tests.support.case import ModuleCase
from tests.support.helpers import with_tempdir
from tests.support.mixins import SaltReturnAssertsMixin
from tests.support.paths import TMP
# Import salt libs
import salt.utils.files
import salt.utils.path
from salt.utils.versions import LooseVersion as _LooseVersion
from salt.ext.six.moves.urllib.parse import urlparse # pylint: disable=no-name-in-module
TEST_REPO = 'https://github.com/saltstack/salt-test-repo.git'
def __check_git_version(caller, min_version, skip_msg):
@ -66,6 +72,9 @@ def ensure_min_git(caller):
def uses_git_opts(caller):
'''
Skip test if git_opts is not supported
IMPORTANT! This decorator should be at the bottom of any decorators added
to a given function.
'''
min_version = '1.7.2'
return __check_git_version(
@ -75,14 +84,63 @@ def uses_git_opts(caller):
)
class WithGitMirror(object):
def __init__(self, repo_url, **kwargs):
self.repo_url = repo_url
if 'dir' not in kwargs:
kwargs['dir'] = TMP
self.kwargs = kwargs
def __call__(self, func):
self.func = func
return functools.wraps(func)(
lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) # pylint: disable=W0108
)
def wrap(self, testcase, *args, **kwargs):
# Get temp dir paths
mirror_dir = tempfile.mkdtemp(**self.kwargs)
admin_dir = tempfile.mkdtemp(**self.kwargs)
clone_dir = tempfile.mkdtemp(**self.kwargs)
# Clean up the directories, we want git to actually create them
os.rmdir(mirror_dir)
os.rmdir(admin_dir)
os.rmdir(clone_dir)
# Create a URL to clone
mirror_url = 'file://' + mirror_dir
# Mirror the repo
testcase.run_function(
'git.clone', [mirror_dir], url=TEST_REPO, opts='--mirror')
# Make sure the directory for the mirror now exists
assert os.path.exists(mirror_dir)
# Clone to the admin dir
ret = testcase.run_state('git.latest', name=mirror_url, target=admin_dir)
ret = ret[next(iter(ret))]
assert os.path.exists(admin_dir)
try:
# Run the actual function with three arguments added:
# 1. URL for the test to use to clone
# 2. Cloned admin dir for making/pushing changes to the mirror
# 3. Yet-nonexistant clone_dir for the test function to use as a
# destination for cloning.
return self.func(testcase, mirror_url, admin_dir, clone_dir, *args, **kwargs)
finally:
shutil.rmtree(mirror_dir, ignore_errors=True)
shutil.rmtree(admin_dir, ignore_errors=True)
shutil.rmtree(clone_dir, ignore_errors=True)
with_git_mirror = WithGitMirror
@ensure_min_git
class GitTest(ModuleCase, SaltReturnAssertsMixin):
'''
Validate the git state
'''
def setUp(self):
domain = 'github.com'
self.test_repo = 'https://{0}/saltstack/salt-test-repo.git'.format(domain)
domain = urlparse(TEST_REPO).netloc
try:
if hasattr(socket, 'setdefaulttimeout'):
# 10 second dns timeout
@ -96,6 +154,9 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Reset the dns timeout after the test is over
socket.setdefaulttimeout(None)
def _head(self, cwd):
return self.run_function('git.rev_parse', [cwd, 'HEAD'])
@with_tempdir(create=False)
def test_latest(self, target):
'''
@ -103,7 +164,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
'''
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
target=target
)
self.assertSaltTrueReturn(ret)
@ -116,7 +177,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
'''
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
rev='develop',
target=target,
submodules=True
@ -146,7 +207,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
'''
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
rev='develop',
target=target,
submodules=True
@ -162,7 +223,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
'''
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
rev='develop',
target=target,
unless='test -e {0}'.format(target),
@ -178,7 +239,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
'''
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
rev=0.11,
target=target,
submodules=True,
@ -196,7 +257,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Clone repo
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
target=target
)
self.assertSaltTrueReturn(ret)
@ -212,7 +273,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Re-run state with force_reset=False
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
target=target,
force_reset=False
)
@ -226,7 +287,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Now run the state with force_reset=True
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
target=target,
force_reset=True
)
@ -235,37 +296,21 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Make sure that we no longer have uncommitted changes
self.assertFalse(self.run_function('git.diff', [target, 'HEAD']))
@with_git_mirror(TEST_REPO)
@uses_git_opts
@with_tempdir(create=False)
@with_tempdir(create=False)
@with_tempdir(create=False)
def test_latest_fast_forward(self, mirror_dir, admin_dir, clone_dir):
def test_latest_fast_forward(self, mirror_url, admin_dir, clone_dir):
'''
Test running git.latest state a second time after changes have been
made to the remote repo.
'''
def _head(cwd):
return self.run_function('git.rev_parse', [cwd, 'HEAD'])
mirror_url = 'file://' + mirror_dir
# Mirror the repo
self.run_function(
'git.clone', [mirror_dir], url=self.test_repo, opts='--mirror')
# Make sure the directory for the mirror now exists
self.assertTrue(os.path.exists(mirror_dir))
# Clone the mirror twice, once to the admin location and once to
# the clone_dir
ret = self.run_state('git.latest', name=mirror_url, target=admin_dir)
self.assertSaltTrueReturn(ret)
# Clone the repo
ret = self.run_state('git.latest', name=mirror_url, target=clone_dir)
self.assertSaltTrueReturn(ret)
ret = ret[next(iter(ret))]
assert ret['result']
# Make a change to the repo by editing the file in the admin copy
# of the repo and committing.
head_pre = _head(admin_dir)
head_pre = self._head(admin_dir)
with salt.utils.files.fopen(os.path.join(admin_dir, 'LICENSE'), 'a') as fp_:
fp_.write('Hello world!')
self.run_function(
@ -275,8 +320,8 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
)
# Make sure HEAD is pointing to a new SHA so we know we properly
# committed our change.
head_post = _head(admin_dir)
self.assertNotEqual(head_pre, head_post)
head_post = self._head(admin_dir)
assert head_pre != head_post
# Push the change to the mirror
# NOTE: the test will fail if the salt-test-repo's default branch
@ -285,10 +330,11 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Re-run the git.latest state on the clone_dir
ret = self.run_state('git.latest', name=mirror_url, target=clone_dir)
self.assertSaltTrueReturn(ret)
ret = ret[next(iter(ret))]
assert ret['result']
# Make sure that the clone_dir now has the correct SHA
self.assertEqual(head_post, _head(clone_dir))
assert head_post == self._head(clone_dir)
@with_tempdir(create=False)
def _changed_local_branch_helper(self, target, rev, hint):
@ -299,7 +345,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Clone repo
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
rev=rev,
target=target
)
@ -320,7 +366,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# comment field.
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
rev=rev,
target=target
)
@ -409,7 +455,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
'''
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
rev='HEAD',
target=target,
depth=1
@ -423,7 +469,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
ret = self.run_state(
'git.latest',
name=self.test_repo,
name=TEST_REPO,
rev='non-default-branch',
target=target,
depth=1
@ -431,6 +477,64 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
self.assertSaltTrueReturn(ret)
self.assertTrue(os.path.isdir(os.path.join(target, '.git')))
@with_git_mirror(TEST_REPO)
@uses_git_opts
def test_latest_sync_tags(self, mirror_url, admin_dir, clone_dir):
'''
Test that a removed tag is properly reported as such and removed in the
local clone, and that new tags are reported as new.
'''
tag1 = 'mytag1'
tag2 = 'mytag2'
# Add and push a tag
self.run_function('git.tag', [admin_dir, tag1])
self.run_function('git.push', [admin_dir, 'origin', tag1])
# Clone the repo
ret = self.run_state('git.latest', name=mirror_url, target=clone_dir)
ret = ret[next(iter(ret))]
assert ret['result']
# Now remove the tag
self.run_function('git.push', [admin_dir, 'origin', ':{0}'.format(tag1)])
# Add and push another tag
self.run_function('git.tag', [admin_dir, tag2])
self.run_function('git.push', [admin_dir, 'origin', tag2])
# Re-run the state with sync_tags=False. This should NOT delete the tag
# from the local clone, but should report that a tag has been added.
ret = self.run_state('git.latest',
name=mirror_url,
target=clone_dir,
sync_tags=False)
ret = ret[next(iter(ret))]
assert ret['result']
# Make ABSOLUTELY SURE both tags are present, since we shouldn't have
# removed tag1.
all_tags = self.run_function('git.list_tags', [clone_dir])
assert tag1 in all_tags
assert tag2 in all_tags
# Make sure the reported changes are correct
expected_changes = {'new_tags': [tag2]}
assert ret['changes'] == expected_changes, ret['changes']
# Re-run the state with sync_tags=True. This should remove the local
# tag, since it doesn't exist in the remote repository.
ret = self.run_state('git.latest',
name=mirror_url,
target=clone_dir,
sync_tags=True)
ret = ret[next(iter(ret))]
assert ret['result']
# Make ABSOLUTELY SURE the expected tags are present/gone
all_tags = self.run_function('git.list_tags', [clone_dir])
assert tag1 not in all_tags
assert tag2 in all_tags
# Make sure the reported changes are correct
expected_changes = {'deleted_tags': [tag1]}
assert ret['changes'] == expected_changes, ret['changes']
@with_tempdir(create=False)
def test_cloned(self, target):
'''
@ -439,35 +543,35 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Test mode
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
test=True)
ret = ret[next(iter(ret))]
assert ret['result'] is None
assert ret['changes'] == {
'new': '{0} => {1}'.format(self.test_repo, target)
'new': '{0} => {1}'.format(TEST_REPO, target)
}
assert ret['comment'] == '{0} would be cloned to {1}'.format(
self.test_repo,
TEST_REPO,
target
)
# Now actually run the state
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target)
ret = ret[next(iter(ret))]
assert ret['result'] is True
assert ret['changes'] == {
'new': '{0} => {1}'.format(self.test_repo, target)
'new': '{0} => {1}'.format(TEST_REPO, target)
}
assert ret['comment'] == '{0} cloned to {1}'.format(self.test_repo, target)
assert ret['comment'] == '{0} cloned to {1}'.format(TEST_REPO, target)
# Run the state again to test idempotence
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target)
ret = ret[next(iter(ret))]
assert ret['result'] is True
@ -477,7 +581,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Run the state again to test idempotence (test mode)
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
test=True)
ret = ret[next(iter(ret))]
@ -497,18 +601,18 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Test mode
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
branch=old_branch,
test=True)
ret = ret[next(iter(ret))]
assert ret['result'] is None
assert ret['changes'] == {
'new': '{0} => {1}'.format(self.test_repo, target)
'new': '{0} => {1}'.format(TEST_REPO, target)
}
assert ret['comment'] == (
'{0} would be cloned to {1} with branch \'{2}\''.format(
self.test_repo,
TEST_REPO,
target,
old_branch
)
@ -517,17 +621,17 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Now actually run the state
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
branch=old_branch)
ret = ret[next(iter(ret))]
assert ret['result'] is True
assert ret['changes'] == {
'new': '{0} => {1}'.format(self.test_repo, target)
'new': '{0} => {1}'.format(TEST_REPO, target)
}
assert ret['comment'] == (
'{0} cloned to {1} with branch \'{2}\''.format(
self.test_repo,
TEST_REPO,
target,
old_branch
)
@ -536,7 +640,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Run the state again to test idempotence
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
branch=old_branch)
ret = ret[next(iter(ret))]
@ -550,7 +654,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Run the state again to test idempotence (test mode)
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
test=True,
branch=old_branch)
@ -565,7 +669,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Change branch (test mode)
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
branch=new_branch,
test=True)
@ -581,7 +685,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Now really change the branch
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
branch=new_branch)
ret = ret[next(iter(ret))]
@ -598,7 +702,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# locally, as that would fail.
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
branch=old_branch)
ret = ret[next(iter(ret))]
@ -613,7 +717,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Test switching to a nonexistant branch. This should fail.
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
branch=bad_branch)
ret = ret[next(iter(ret))]
@ -633,7 +737,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Test mode
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
branch=branch,
test=True)
@ -642,7 +746,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
assert ret['changes']
assert ret['comment'] == (
'{0} would be cloned to {1} with branch \'{2}\''.format(
self.test_repo,
TEST_REPO,
target,
branch
)
@ -651,7 +755,7 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin):
# Now actually run the state
ret = self.run_state(
'git.cloned',
name=self.test_repo,
name=TEST_REPO,
target=target,
branch=branch)
ret = ret[next(iter(ret))]

View file

@ -29,6 +29,7 @@ from tests.support.mock import (
import salt.config
import salt.loader
import salt.state
import salt.utils.args
import salt.utils.files
import salt.utils.json
import salt.utils.hashutils
@ -533,7 +534,7 @@ class StateTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={"test": ""})
with patch.object(salt.utils.state, 'get_sls_opts', mock):
mock = MagicMock(return_value=True)
with patch.object(salt.utils, 'test_mode', mock):
with patch.object(salt.utils.args, 'test_mode', mock):
self.assertRaises(SaltInvocationError,
state.single,
"pkg.installed",
@ -637,7 +638,7 @@ class StateTestCase(TestCase, LoaderModuleMockMixin):
)
with patch.object(salt.utils.state, 'get_sls_opts', mock):
mock = MagicMock(return_value=True)
with patch.object(salt.utils, 'test_mode', mock):
with patch.object(salt.utils.args, 'test_mode', mock):
MockState.State.flag = True
MockState.HighState.flag = True
self.assertEqual(state.sls_id("apache", "http"), 2)
@ -686,7 +687,7 @@ class StateTestCase(TestCase, LoaderModuleMockMixin):
)
with patch.object(salt.utils.state, 'get_sls_opts', mock):
mock = MagicMock(return_value=True)
with patch.object(salt.utils, 'test_mode', mock):
with patch.object(salt.utils.args, 'test_mode', mock):
self.assertRaises(SaltInvocationError,
state.show_sls,
"foo",
@ -717,7 +718,7 @@ class StateTestCase(TestCase, LoaderModuleMockMixin):
mock = MagicMock(return_value={'test': True})
with patch.object(salt.utils.state, 'get_sls_opts', mock):
mock = MagicMock(return_value=True)
with patch.object(salt.utils, 'test_mode', mock):
with patch.object(salt.utils.args, 'test_mode', mock):
self.assertRaises(SaltInvocationError,
state.top,
"reverse_top.sls",
@ -878,7 +879,7 @@ class StateTestCase(TestCase, LoaderModuleMockMixin):
"saltenv": None})
with patch.object(salt.utils.state, 'get_sls_opts', mock):
mock = MagicMock(return_value=True)
with patch.object(salt.utils,
with patch.object(salt.utils.args,
'test_mode',
mock):
self.assertRaises(

View file

@ -48,7 +48,7 @@ class CkMinionsTestCase(TestCase):
TestCase for salt.utils.minions.CkMinions class
'''
def setUp(self):
self.ckminions = salt.utils.minions.CkMinions({})
self.ckminions = salt.utils.minions.CkMinions({'minion_data_cache': True})
def test_spec_check(self):
# Test spec-only rule

View file

@ -9,6 +9,7 @@ import textwrap
import salt.loader
import salt.utils.data
import salt.utils.files
import salt.utils.reactor as reactor
import salt.utils.yaml
@ -441,7 +442,7 @@ class TestReactor(TestCase, AdaptedConfigurationTestCaseMixin):
os.path, 'isfile',
MagicMock(return_value=True)):
with patch.object(
salt.utils, 'is_empty',
salt.utils.files, 'is_empty',
MagicMock(return_value=False)):
with patch.object(
codecs, 'open',