Merge branch '2017.7' into fix_48169

This commit is contained in:
Nicole Thomas 2018-07-20 10:45:59 -04:00 committed by GitHub
commit ead19725b6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
78 changed files with 3632 additions and 520 deletions

View file

@ -1,8 +1,13 @@
pipeline {
agent { label 'docs' }
options {
timestamps()
ansiColor('xterm')
}
environment {
PYENV_ROOT = "/usr/local/pyenv"
PATH = "$PYENV_ROOT/bin:$PATH"
PY_COLORS = 1
}
stages {
stage('github-pending') {
@ -22,11 +27,13 @@ pipeline {
stage('build') {
steps {
sh 'eval "$(pyenv init -)"; make -C doc clean html'
archiveArtifacts artifacts: 'doc/_build/html/'
}
}
}
post {
always {
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'The docs job has passed',

View file

@ -1,5 +1,9 @@
pipeline {
agent { label 'kitchen-slave' }
options {
timestamps()
ansiColor('xterm')
}
environment {
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml"
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml"
@ -7,6 +11,7 @@ pipeline {
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py2"
TEST_PLATFORM = "centos-7"
PY_COLORS = 1
}
stages {
stage('github-pending') {
@ -31,21 +36,25 @@ pipeline {
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
post {
always {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins/jenkins-testing.pem'
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
}
}
}
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",

View file

@ -1,5 +1,9 @@
pipeline {
agent { label 'kitchen-slave' }
options {
timestamps()
ansiColor('xterm')
}
environment {
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml"
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml"
@ -7,6 +11,7 @@ pipeline {
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py3"
TEST_PLATFORM = "centos-7"
PY_COLORS = 1
}
stages {
stage('github-pending') {
@ -31,21 +36,25 @@ pipeline {
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
post {
always {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins/jenkins-testing.pem'
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
}
}
}
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",

View file

@ -1,5 +1,9 @@
pipeline {
agent { label 'kitchen-slave' }
options {
timestamps()
ansiColor('xterm')
}
environment {
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml"
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml"
@ -7,6 +11,7 @@ pipeline {
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py2"
TEST_PLATFORM = "ubuntu-1604"
PY_COLORS = 1
}
stages {
stage('github-pending') {
@ -31,21 +36,25 @@ pipeline {
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
post {
always {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins/jenkins-testing.pem'
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
}
}
}
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",

View file

@ -1,5 +1,9 @@
pipeline {
agent { label 'kitchen-slave' }
options {
timestamps()
ansiColor('xterm')
}
environment {
SALT_KITCHEN_PLATFORMS = "/var/jenkins/workspace/platforms.yml"
SALT_KITCHEN_DRIVER = "/var/jenkins/workspace/driver.yml"
@ -7,6 +11,7 @@ pipeline {
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py3"
TEST_PLATFORM = "ubuntu-1604"
PY_COLORS = 1
}
stages {
stage('github-pending') {
@ -31,21 +36,25 @@ pipeline {
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
post {
always {
script { withCredentials([[$class: 'AmazonWebServicesCredentialsBinding', accessKeyVariable: 'AWS_ACCESS_KEY_ID', credentialsId: 'AWS_ACCESS_KEY_ID', secretKeyVariable: 'AWS_SECRET_ACCESS_KEY']]) {
sshagent(credentials: ['jenkins-testing-ssh-key']) {
sh 'ssh-add ~/.ssh/jenkins/jenkins-testing.pem'
sh 'ssh-add ~/.ssh/jenkins-testing.pem'
sh 'bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM'
}
}}
archiveArtifacts artifacts: 'artifacts/xml-unittests-output/*.xml'
}
}
}
}
post {
always {
junit 'artifacts/xml-unittests-output/*.xml'
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has passed",

View file

@ -1,8 +1,13 @@
pipeline {
agent { label 'pr-lint-slave' }
options {
timestamps()
ansiColor('xterm')
}
environment {
PYENV_ROOT = "/usr/local/pyenv"
PATH = "$PYENV_ROOT/bin:$PATH"
PY_COLORS = 1
}
stages {
stage('github-pending') {
@ -16,8 +21,7 @@ pipeline {
stage('setup') {
steps {
sh 'eval "$(pyenv init -)"; pyenv install 2.7.14 || echo "We already have this python."; pyenv local 2.7.14; pyenv shell 2.7.14'
sh 'eval "$(pyenv init -)"; pip install pylint SaltPyLint'
sh 'eval "$(pyenv init -)"; which pylint; pylint --version'
sh 'eval "$(pyenv init -)"; pip install tox'
}
}
stage('linting') {
@ -25,13 +29,13 @@ pipeline {
parallel {
stage('salt linting') {
steps {
sh 'eval "$(pyenv init -)"; pylint --rcfile=.testing.pylintrc --disable=W1307,str-format-in-logging setup.py salt/ | tee pylint-report.xml'
sh 'eval "$(pyenv init -)"; tox -e pylint-salt | tee pylint-report.xml'
archiveArtifacts artifacts: 'pylint-report.xml'
}
}
stage('test linting') {
steps {
sh 'eval "$(pyenv init -)"; pylint --rcfile=.testing.pylintrc --disable=W0232,E1002,W1307,str-format-in-logging tests/ | tee pylint-report-tests.xml'
sh 'eval "$(pyenv init -)"; tox -e pylint-tests | tee pylint-report-tests.xml'
archiveArtifacts artifacts: 'pylint-report-tests.xml'
}
}
@ -39,6 +43,17 @@ pipeline {
}
}
post {
always {
step([$class: 'WarningsPublisher',
parserConfigurations: [[
parserName: 'PyLint',
pattern: 'pylint-report*.xml'
]],
failedTotalAll: '1',
usePreviousBuildAsReference: true
])
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'The lint job has passed',

1
.github/CODEOWNERS vendored
View file

@ -48,6 +48,7 @@ salt/spm/* @saltstack/team-spm
# Team SSH
salt/cli/ssh.py @saltstack/team-ssh
salt/client/ssh/* @saltstack/team-ssh
salt/roster/* @saltstack/team-ssh
salt/runners/ssh.py @saltstack/team-ssh
salt/**/thin.py @saltstack/team-ssh

View file

@ -1,6 +1,6 @@
---
<% vagrant = system('gem list -i kitchen-vagrant 2>/dev/null >/dev/null') %>
<% version = '2017.7.4' %>
<% version = '2017.7.6' %>
<% platformsfile = ENV['SALT_KITCHEN_PLATFORMS'] || '.kitchen/platforms.yml' %>
<% driverfile = ENV['SALT_KITCHEN_DRIVER'] || '.kitchen/driver.yml' %>
<% verifierfile = ENV['SALT_KITCHEN_VERIFIER'] || '.kitchen/verifier.yml' %>
@ -31,7 +31,7 @@ provisioner:
salt_version: latest
salt_bootstrap_url: https://bootstrap.saltstack.com
salt_bootstrap_options: -X -p rsync stable <%= version %>
log_level: debug
log_level: info
sudo: true
require_chef: false
retry_on_exit_code:

View file

@ -251,7 +251,7 @@ project = 'Salt'
version = salt.version.__version__
latest_release = '2018.3.2' # latest release
previous_release = '2017.7.6' # latest release from previous branch
previous_release = '2017.7.7' # latest release from previous branch
previous_release_dir = '2017.7' # path on web server for previous branch
next_release = '' # next release
next_release_dir = '' # path on web server for next release branch

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-API" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-API" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-api \- salt-api Command
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-CALL" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-CALL" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-call \- salt-call Documentation
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-CLOUD" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-CLOUD" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-cloud \- Salt Cloud Command
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-CP" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-CP" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-cp \- salt-cp Documentation
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-KEY" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-KEY" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-key \- salt-key Documentation
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-MASTER" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-MASTER" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-master \- salt-master Documentation
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-MINION" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-MINION" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-minion \- salt-minion Documentation
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-PROXY" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-PROXY" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-proxy \- salt-proxy Documentation
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-RUN" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-RUN" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-run \- salt-run Documentation
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-SSH" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-SSH" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-ssh \- salt-ssh Documentation
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-SYNDIC" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-SYNDIC" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-syndic \- salt-syndic Documentation
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT-UNITY" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT-UNITY" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt-unity \- salt-unity Command
.

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SALT" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SALT" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
salt \- salt
.

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
.\" Man page generated from reStructuredText.
.
.TH "SPM" "1" "May 07, 2018" "2017.7.6" "Salt"
.TH "SPM" "1" "Jun 14, 2018" "2017.7.7" "Salt"
.SH NAME
spm \- Salt Package Manager Command
.

View file

@ -1,9 +1,8 @@
========================================
In Progress: Salt 2017.7.7 Release Notes
========================================
===========================
Salt 2017.7.7 Release Notes
===========================
Version 2017.7.7 is an **unreleased** bugfix release for :ref:`2017.7.0 <release-2017-7-0>`.
This release is still in progress and has not been released yet.
Version 2017.7.7 is a bugfix release for :ref:`2017.7.0 <release-2017-7-0>`.
The ``2017.7.7`` release contains only a small number of fixes, which are detailed
below.

View file

@ -77,6 +77,7 @@ Function Get-Settings {
"SSLeay" = "ssleay32.dll"
"OpenSSLLic" = "OpenSSL_License.txt"
"msvcr" = "msvcr120.dll"
"Libsodium" = "libsodium.dll"
}
$ini.Add("64bitDLLs", $64bitDLLs)
@ -86,6 +87,7 @@ Function Get-Settings {
"SSLeay" = "ssleay32.dll"
"OpenSSLLic" = "OpenSSL_License.txt"
"msvcr" = "msvcr120.dll"
"Libsodium" = "libsodium.dll"
}
$ini.Add("32bitDLLs", $32bitDLLs)

View file

@ -9,34 +9,36 @@ salt for Windows with their corresponding licenses:
| certifi | ISC |
| cffi | MIT |
| CherryPy | BSD |
| cryptography | BSD |
| enum34 | BSD |
| futures | BSD |
| gitdb | BSD |
| GitPython | BSD |
| idna | BSD-like |
| ioflo | Apache 2.0 |
| ioloop | MIT |
| ipaddress | PSF |
| Jinja2 | BSD |
| libnacl | --- |
| libnacl | Apache |
| lxml | BSD |
| Mako | MIT |
| MarkupSafe | BSD |
| msgpack-python | --- |
| msgpack-python | Apache 2.0 |
| pip | MIT |
| psutil | BSD |
| pyasn1 | BSD |
| pycparser | BSD |
| pycurl | LGPL + MIT |
| PyMySQL | MIT |
| pypiwin32 | PSF |
| PyOpenSSL | Apache 2.0 |
| python-certifi-win32 | BSD |
| python-dateutil | Simplified BSD |
| python-gnupg | BSD |
| pywin32 | PSF |
| PyYAML | MIT |
| pyzmq | LGPL + BSD |
| requests | Apache 2.0 |
| setuptools | MIT |
| singledispatch | MIT |
| six | MIT |
| smmap | BSD |
| timelib | ZLIB/PHP |
| tornado | Apache 2.0 |

View file

@ -12,6 +12,7 @@ idna==2.5
ioloop==0.1a0
ipaddress==1.0.18
Jinja2==2.9.6
libnacl==1.6.1 # required by the nacl module
lxml==3.7.3
Mako==1.0.6
MarkupSafe==1.0
@ -23,6 +24,7 @@ pycrypto==2.6.1
pycurl==7.43.0
PyMySQL==0.7.11
pyOpenSSL==17.0.0
#python-certifi-win32==1.2
python-dateutil==2.6.0
python-gnupg==0.4.0
pywin32==223

View file

@ -10,3 +10,4 @@ git+https://github.com/saltstack/pytest-salt.git@master#egg=pytest-salt
# satisfy other requirements, and httpretty 0.8.10 has bugs in setup.py that
# prevent it from being successfully installed (at least on Python 3.4).
httpretty; python_version >= '3.4'
pylint==1.6.5

View file

@ -1,4 +1,5 @@
-r base.txt
pycrypto>=2.6.1
pyzmq>=2.2.0
pyzmq>=2.2.0,<17.1.0; python_version == '3.4' # pyzmq 17.1.0 stopped building wheels for python3.4
pyzmq>=2.2.0; python_version != '3.4'

View file

@ -221,7 +221,7 @@ class LocalClient(object):
# Looks like the timeout is invalid, use config
return self.opts['timeout']
def gather_job_info(self, jid, tgt, tgt_type, **kwargs):
def gather_job_info(self, jid, tgt, tgt_type, listen=True, **kwargs):
'''
Return the information about a given job
'''
@ -233,6 +233,7 @@ class LocalClient(object):
arg=[jid],
tgt_type=tgt_type,
timeout=timeout,
listen=listen,
**kwargs
)

View file

@ -164,7 +164,7 @@ def salt_refs(data, ret=None):
return ret
def prep_trans_tar(opts, file_client, chunks, file_refs, pillar=None, id_=None):
def prep_trans_tar(file_client, chunks, file_refs, pillar=None, id_=None):
'''
Generate the execution package from the saltenv file refs and a low state
data structure

View file

@ -32,6 +32,59 @@ __func_alias__ = {
log = logging.getLogger(__name__)
def _ssh_state(chunks, st_kwargs,
kwargs, test=False):
'''
Function to run a state with the given chunk via salt-ssh
'''
file_refs = salt.client.ssh.state.lowstate_file_refs(
chunks,
_merge_extra_filerefs(
kwargs.get('extra_filerefs', ''),
__opts__.get('extra_filerefs', '')
)
)
# Create the tar containing the state pkg and relevant files.
trans_tar = salt.client.ssh.state.prep_trans_tar(
__context__['fileclient'],
chunks,
file_refs,
__pillar__,
st_kwargs['id_'])
trans_tar_sum = salt.utils.get_hash(trans_tar, __opts__['hash_type'])
cmd = 'state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}'.format(
__opts__['thin_dir'],
test,
trans_tar_sum,
__opts__['hash_type'])
single = salt.client.ssh.Single(
__opts__,
cmd,
fsclient=__context__['fileclient'],
minion_opts=__salt__.minion_opts,
**st_kwargs)
single.shell.send(
trans_tar,
'{0}/salt_state.tgz'.format(__opts__['thin_dir']))
stdout, stderr, _ = single.cmd_block()
# Clean up our tar
try:
os.remove(trans_tar)
except (OSError, IOError):
pass
# Read in the JSON data and return the data structure
try:
return json.loads(stdout, object_hook=salt.utils.decode_dict)
except Exception as e:
log.error("JSON Render failed for: %s\n%s", stdout, stderr)
log.error(str(e))
# If for some reason the json load fails, return the stdout
return stdout
def _set_retcode(ret, highstate=None):
'''
Set the return code based on the data back from the state system
@ -156,7 +209,6 @@ def sls(mods, saltenv='base', test=None, exclude=None, **kwargs):
# Create the tar containing the state pkg and relevant files.
_cleanup_slsmod_low_data(chunks)
trans_tar = salt.client.ssh.state.prep_trans_tar(
__opts__,
__context__['fileclient'],
chunks,
file_refs,
@ -323,7 +375,6 @@ def low(data, **kwargs):
)
# Create the tar containing the state pkg and relevant files.
trans_tar = salt.client.ssh.state.prep_trans_tar(
__opts__,
__context__['fileclient'],
chunks,
file_refs,
@ -408,7 +459,6 @@ def high(data, **kwargs):
# Create the tar containing the state pkg and relevant files.
_cleanup_slsmod_low_data(chunks)
trans_tar = salt.client.ssh.state.prep_trans_tar(
__opts__,
__context__['fileclient'],
chunks,
file_refs,
@ -642,7 +692,6 @@ def highstate(test=None, **kwargs):
# Create the tar containing the state pkg and relevant files.
_cleanup_slsmod_low_data(chunks)
trans_tar = salt.client.ssh.state.prep_trans_tar(
__opts__,
__context__['fileclient'],
chunks,
file_refs,
@ -719,7 +768,6 @@ def top(topfn, test=None, **kwargs):
# Create the tar containing the state pkg and relevant files.
_cleanup_slsmod_low_data(chunks)
trans_tar = salt.client.ssh.state.prep_trans_tar(
__opts__,
__context__['fileclient'],
chunks,
file_refs,
@ -835,6 +883,7 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
salt '*' state.sls_id my_state my_module,a_common_module
'''
st_kwargs = __salt__.kwargs
conflict = _check_queue(queue, kwargs)
if conflict is not None:
return conflict
@ -847,13 +896,11 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
if opts['environment'] is None:
opts['environment'] = 'base'
try:
st_ = salt.state.HighState(opts,
proxy=__proxy__,
initial_pillar=_get_initial_pillar(opts))
except NameError:
st_ = salt.state.HighState(opts,
initial_pillar=_get_initial_pillar(opts))
st_ = salt.client.ssh.state.SSHHighState(
__opts__,
__pillar__,
__salt__,
__context__['fileclient'])
if not _check_pillar(kwargs, st_.opts['pillar']):
__context__['retcode'] = 5
@ -864,10 +911,7 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
if isinstance(mods, six.string_types):
split_mods = mods.split(',')
st_.push_active()
try:
high_, errors = st_.render_highstate({opts['environment']: split_mods})
finally:
st_.pop_active()
high_, errors = st_.render_highstate({opts['environment']: split_mods})
errors += st_.state.verify_high(high_)
# Apply requisites to high data
high_, req_in_errors = st_.state.requisite_in(high_)
@ -879,20 +923,22 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
__context__['retcode'] = 1
return errors
chunks = st_.state.compile_high_data(high_)
ret = {}
for chunk in chunks:
if chunk.get('__id__', '') == id_:
ret.update(st_.state.call_chunk(chunk, {}, chunks))
chunk = [x for x in chunks if x.get('__id__', '') == id_]
_set_retcode(ret, highstate=highstate)
# Work around Windows multiprocessing bug, set __opts__['test'] back to
# value from before this function was run.
__opts__['test'] = orig_test
if not ret:
if not chunk:
raise SaltInvocationError(
'No matches for ID \'{0}\' found in SLS \'{1}\' within saltenv '
'\'{2}\''.format(id_, mods, opts['environment'])
)
ret = _ssh_state(chunk,
st_kwargs,
kwargs,
test=test)
_set_retcode(ret, highstate=highstate)
# Work around Windows multiprocessing bug, set __opts__['test'] back to
# value from before this function was run.
__opts__['test'] = orig_test
return ret
@ -1078,7 +1124,6 @@ def single(fun, name, test=None, **kwargs):
# Create the tar containing the state pkg and relevant files.
trans_tar = salt.client.ssh.state.prep_trans_tar(
__opts__,
__context__['fileclient'],
chunks,
file_refs,

View file

@ -520,9 +520,17 @@ class AsyncAuth(object):
error = SaltClientError('Detect mode is on')
break
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 {0} seconds before retry.'.format(acceptance_wait_time))
yield tornado.gen.sleep(acceptance_wait_time)

View file

@ -64,10 +64,10 @@ if USE_IMPORTLIB:
SUFFIXES = []
for suffix in importlib.machinery.EXTENSION_SUFFIXES:
SUFFIXES.append((suffix, 'rb', MODULE_KIND_EXTENSION))
for suffix in importlib.machinery.BYTECODE_SUFFIXES:
SUFFIXES.append((suffix, 'rb', MODULE_KIND_COMPILED))
for suffix in importlib.machinery.SOURCE_SUFFIXES:
SUFFIXES.append((suffix, 'rb', MODULE_KIND_SOURCE))
for suffix in importlib.machinery.BYTECODE_SUFFIXES:
SUFFIXES.append((suffix, 'rb', MODULE_KIND_COMPILED))
MODULE_KIND_MAP = {
MODULE_KIND_SOURCE: importlib.machinery.SourceFileLoader,
MODULE_KIND_COMPILED: importlib.machinery.SourcelessFileLoader,

View file

@ -282,12 +282,14 @@ def raw_cron(user):
# Preserve line endings
lines = sdecode(__salt__['cmd.run_stdout'](cmd,
runas=user,
ignore_retcode=True,
rstrip=False,
python_shell=False)).splitlines(True)
else:
cmd = 'crontab -u {0} -l'.format(user)
# Preserve line endings
lines = sdecode(__salt__['cmd.run_stdout'](cmd,
ignore_retcode=True,
rstrip=False,
python_shell=False)).splitlines(True)

View file

@ -5235,7 +5235,6 @@ def _prepare_trans_tar(name, mods=None, saltenv='base', pillar=None):
refs = salt.client.ssh.state.lowstate_file_refs(chunks)
_mk_fileclient()
trans_tar = salt.client.ssh.state.prep_trans_tar(
__opts__,
__context__['cp.fileclient'],
chunks, refs, pillar, name)
return trans_tar

View file

@ -84,6 +84,9 @@ def fire_master(data, tag, preload=None):
channel = salt.transport.Channel.factory(__opts__, master_uri=master)
try:
channel.send(load)
# channel.send was successful.
# Ensure ret is True.
ret = True
except Exception:
ret = False
return ret

View file

@ -4262,26 +4262,6 @@ def check_perms(name, ret, user, group, mode, follow_symlinks=False):
perms['lgroup'] = cur['group']
perms['lmode'] = salt.utils.normalize_mode(cur['mode'])
# Mode changes if needed
if mode is not None:
# File is a symlink, ignore the mode setting
# if follow_symlinks is False
if os.path.islink(name) and not follow_symlinks:
pass
else:
mode = salt.utils.normalize_mode(mode)
if mode != perms['lmode']:
if __opts__['test'] is True:
ret['changes']['mode'] = mode
else:
set_mode(name, mode)
if mode != salt.utils.normalize_mode(get_mode(name)):
ret['result'] = False
ret['comment'].append(
'Failed to change mode to {0}'.format(mode)
)
else:
ret['changes']['mode'] = mode
# user/group changes if needed, then check if it worked
if user:
if isinstance(user, int):
@ -4358,6 +4338,27 @@ def check_perms(name, ret, user, group, mode, follow_symlinks=False):
elif 'cgroup' in perms and user != '':
ret['changes']['group'] = group
# Mode changes if needed
if mode is not None:
# File is a symlink, ignore the mode setting
# if follow_symlinks is False
if os.path.islink(name) and not follow_symlinks:
pass
else:
mode = salt.utils.normalize_mode(mode)
if mode != perms['lmode']:
if __opts__['test'] is True:
ret['changes']['mode'] = mode
else:
set_mode(name, mode)
if mode != salt.utils.normalize_mode(get_mode(name)):
ret['result'] = False
ret['comment'].append(
'Failed to change mode to {0}'.format(mode)
)
else:
ret['changes']['mode'] = mode
if isinstance(orig_comment, six.string_types):
if orig_comment:
ret['comment'].insert(0, orig_comment)
@ -4484,6 +4485,11 @@ def check_managed_changes(
defaults,
skip_verify,
**kwargs)
# Ensure that user-provided hash string is lowercase
if source_sum and ('hsum' in source_sum):
source_sum['hsum'] = source_sum['hsum'].lower()
if comments:
__clean_tmp(sfn)
return False, comments
@ -4725,7 +4731,7 @@ def manage_file(name,
source
file reference on the master
source_hash
source_sum
sum hash for source
user

View file

@ -26,15 +26,16 @@ def __virtual__():
return (False, 'glusterfs server is not installed')
def _get_minor_version():
# Set default version to 6 for tests
version = 6
def _get_version():
# Set the default minor version to 6 for tests
version = [3, 6]
cmd = 'gluster --version'
result = __salt__['cmd.run'](cmd).splitlines()
for line in result:
if line.startswith('glusterfs'):
version = int(line.split()[1].split('.')[1])
return version
version = line.split()[-1].split('.')
version = [int(i) for i in version]
return tuple(version)
def _gluster_ok(xml_data):
@ -67,7 +68,7 @@ def _gluster_xml(cmd):
# We will pass the command string as stdin to allow for much longer
# command strings. This is especially useful for creating large volumes
# where the list of bricks exceeds 128 characters.
if _get_minor_version() < 6:
if _get_version() < (3, 6,):
result = __salt__['cmd.run'](
'script -q -c "gluster --xml --mode=script"', stdin="{0}\n\004".format(cmd)
)

View file

@ -160,7 +160,11 @@ def install(pkg=None,
env.update({'SUDO_UID': uid, 'SUDO_USER': ''})
cmd = ' '.join(cmd)
result = __salt__['cmd.run_all'](cmd, python_shell=True, cwd=dir, runas=runas, env=env)
result = __salt__['cmd.run_all'](cmd,
python_shell=True,
cwd=dir,
runas=runas,
env=env)
if result['retcode'] != 0:
raise CommandExecutionError(result['stderr'])
@ -168,33 +172,9 @@ def install(pkg=None,
# npm >1.2.21 is putting the output to stderr even though retcode is 0
npm_output = result['stdout'] or result['stderr']
try:
return json.loads(npm_output)
return salt.utils.find_json(npm_output)
except ValueError:
pass
json_npm_output = _extract_json(npm_output)
return json_npm_output or npm_output
def _extract_json(npm_output):
lines = npm_output.splitlines()
log.error(lines)
# Strip all lines until JSON output starts
while lines and not lines[0].startswith('{') and not lines[0].startswith('['):
lines = lines[1:]
while lines and not lines[-1].startswith('}') and not lines[-1].startswith(']'):
lines = lines[:-1]
# macOS with fsevents includes the following line in the return
# when a new module is installed which is invalid JSON:
# [fsevents] Success: "..."
while lines and (lines[0].startswith('[fsevents]') or lines[0].startswith('Pass ')):
lines = lines[1:]
try:
return json.loads(''.join(lines))
except ValueError:
pass
return None
return npm_output
def uninstall(pkg, dir=None, runas=None, env=None):

View file

@ -25,13 +25,13 @@ def __virtual__():
'''
if salt.utils.is_darwin() or salt.utils.is_windows():
return True
return (False, 'Module proxy: module only works on Windows or MacOS systems')
return False, 'Module proxy: module only works on Windows or MacOS systems'
def _get_proxy_osx(function, network_service):
def _get_proxy_osx(cmd_function, network_service):
ret = {}
out = __salt__['cmd.run']('networksetup -{0} {1}'.format(function, network_service))
out = __salt__['cmd.run']('networksetup -{0} {1}'.format(cmd_function, network_service))
match = re.match('Enabled: (.*)\nServer: (.*)\nPort: (.*)\n', out)
if match is not None:
g = match.groups()
@ -41,8 +41,8 @@ def _get_proxy_osx(function, network_service):
return ret
def _set_proxy_osx(function, server, port, user, password, network_service):
cmd = 'networksetup -{0} {1} {2} {3}'.format(function, network_service, server, port)
def _set_proxy_osx(cmd_function, server, port, user, password, network_service):
cmd = 'networksetup -{0} {1} {2} {3}'.format(cmd_function, network_service, server, port)
if user is not None and password is not None:
cmd = cmd + ' On {0} {1}'.format(user, password)
@ -58,12 +58,12 @@ def _get_proxy_windows(types=None):
if types is None:
types = ['http', 'https', 'ftp']
reg_val = __salt__['reg.read_value']('HKEY_CURRENT_USER',
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyServer')
servers = reg_val['vdata']
servers = __salt__['reg.read_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyServer')['vdata']
if "=" in servers:
if servers and "=" in servers:
split = servers.split(";")
for s in split:
if len(s) == 0:
@ -87,16 +87,19 @@ def _get_proxy_windows(types=None):
del ret[key]
# Return enabled info
reg_val = __salt__['reg.read_value']('HKEY_CURRENT_USER',
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyEnable')
enabled = reg_val.get('vdata', 0)
ret['enabled'] = True if enabled == 1 else False
ret['enabled'] = __salt__['reg.read_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyEnable')['vdata'] == 1
return ret
def _set_proxy_windows(server, port, types=None, bypass_hosts=None, import_winhttp=True):
def _set_proxy_windows(server,
port,
types=None,
bypass_hosts=None,
import_winhttp=True):
if types is None:
types = ['http', 'https', 'ftp']
@ -104,17 +107,27 @@ def _set_proxy_windows(server, port, types=None, bypass_hosts=None, import_winht
for t in types:
server_str += '{0}={1}:{2};'.format(t, server, port)
__salt__['reg.set_value']('HKEY_CURRENT_USER', r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyServer', server_str)
__salt__['reg.set_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyServer',
vdata=server_str)
__salt__['reg.set_value']('HKEY_CURRENT_USER', r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyEnable', 1, vtype='REG_DWORD')
__salt__['reg.set_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyEnable',
vdata=1,
vtype='REG_DWORD')
if bypass_hosts is not None:
bypass_hosts_str = '<local>;{0}'.format(';'.join(bypass_hosts))
__salt__['reg.set_value']('HKEY_CURRENT_USER', r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyOverride', bypass_hosts_str)
__salt__['reg.set_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyOverride',
vdata=bypass_hosts_str)
if import_winhttp:
cmd = 'netsh winhttp import proxy source=ie'
@ -138,15 +151,22 @@ def get_http_proxy(network_service="Ethernet"):
salt '*' proxy.get_http_proxy Ethernet
'''
if __grains__['os'] == 'Windows':
return _get_proxy_windows(['http'])
return _get_proxy_windows(types=['http'])
return _get_proxy_osx("getwebproxy", network_service)
return _get_proxy_osx(cmd_function="getwebproxy",
network_service=network_service)
def set_http_proxy(server, port, user=None, password=None, network_service="Ethernet", bypass_hosts=None):
def set_http_proxy(server,
port,
user=None,
password=None,
network_service="Ethernet",
bypass_hosts=None):
'''
Sets the http proxy settings. Note: On Windows this will override any other proxy settings you have,
the preferred method of updating proxies on windows is using set_proxy.
Sets the http proxy settings. Note: On Windows this will override any other
proxy settings you have, the preferred method of updating proxies on windows
is using set_proxy.
server
The proxy server to use
@ -165,8 +185,8 @@ def set_http_proxy(server, port, user=None, password=None, network_service="Ethe
macOS
bypass_hosts
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
set_proxy_bypass to edit the bypass hosts.
The hosts that are allowed to by pass the proxy. Only used on Windows
for other OS's use set_proxy_bypass to edit the bypass hosts.
CLI Example:
@ -175,9 +195,17 @@ def set_http_proxy(server, port, user=None, password=None, network_service="Ethe
salt '*' proxy.set_http_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet
'''
if __grains__['os'] == 'Windows':
return _set_proxy_windows(server, port, ['http'], bypass_hosts)
return _set_proxy_windows(server=server,
port=port,
types=['http'],
bypass_hosts=bypass_hosts)
return _set_proxy_osx("setwebproxy", server, port, user, password, network_service)
return _set_proxy_osx(cmd_function="setwebproxy",
server=server,
port=port,
user=user,
password=password,
network_service=network_service)
def get_https_proxy(network_service="Ethernet"):
@ -195,15 +223,22 @@ def get_https_proxy(network_service="Ethernet"):
salt '*' proxy.get_https_proxy Ethernet
'''
if __grains__['os'] == 'Windows':
return _get_proxy_windows(['https'])
return _get_proxy_windows(types=['https'])
return _get_proxy_osx("getsecurewebproxy", network_service)
return _get_proxy_osx(cmd_function="getsecurewebproxy",
network_service=network_service)
def set_https_proxy(server, port, user=None, password=None, network_service="Ethernet", bypass_hosts=None):
def set_https_proxy(server,
port,
user=None,
password=None,
network_service="Ethernet",
bypass_hosts=None):
'''
Sets the https proxy settings. Note: On Windows this will override any other proxy settings you have,
the preferred method of updating proxies on windows is using set_proxy.
Sets the https proxy settings. Note: On Windows this will override any other
proxy settings you have, the preferred method of updating proxies on windows
is using set_proxy.
server
The proxy server to use
@ -222,8 +257,8 @@ def set_https_proxy(server, port, user=None, password=None, network_service="Eth
macOS
bypass_hosts
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
set_proxy_bypass to edit the bypass hosts.
The hosts that are allowed to by pass the proxy. Only used on Windows
for other OS's use set_proxy_bypass to edit the bypass hosts.
CLI Example:
@ -232,9 +267,17 @@ def set_https_proxy(server, port, user=None, password=None, network_service="Eth
salt '*' proxy.set_https_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet
'''
if __grains__['os'] == 'Windows':
return _set_proxy_windows(server, port, ['https'], bypass_hosts)
return _set_proxy_windows(server=server,
port=port,
types=['https'],
bypass_hosts=bypass_hosts)
return _set_proxy_osx("setsecurewebproxy", server, port, user, password, network_service)
return _set_proxy_osx(cmd_function="setsecurewebproxy",
server=server,
port=port,
user=user,
password=password,
network_service=network_service)
def get_ftp_proxy(network_service="Ethernet"):
@ -252,12 +295,18 @@ def get_ftp_proxy(network_service="Ethernet"):
salt '*' proxy.get_ftp_proxy Ethernet
'''
if __grains__['os'] == 'Windows':
return _get_proxy_windows(['ftp'])
return _get_proxy_windows(types=['ftp'])
return _get_proxy_osx("getftpproxy", network_service)
return _get_proxy_osx(cmd_function="getftpproxy",
network_service=network_service)
def set_ftp_proxy(server, port, user=None, password=None, network_service="Ethernet", bypass_hosts=None):
def set_ftp_proxy(server,
port,
user=None,
password=None,
network_service="Ethernet",
bypass_hosts=None):
'''
Sets the ftp proxy settings
@ -278,8 +327,8 @@ def set_ftp_proxy(server, port, user=None, password=None, network_service="Ether
macOS
bypass_hosts
The hosts that are allowed to by pass the proxy. Only used on Windows for other OS's use
set_proxy_bypass to edit the bypass hosts.
The hosts that are allowed to by pass the proxy. Only used on Windows
for other OS's use set_proxy_bypass to edit the bypass hosts.
CLI Example:
@ -288,9 +337,17 @@ def set_ftp_proxy(server, port, user=None, password=None, network_service="Ether
salt '*' proxy.set_ftp_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet
'''
if __grains__['os'] == 'Windows':
return _set_proxy_windows(server, port, ['ftp'], bypass_hosts)
return _set_proxy_windows(server=server,
port=port,
types=['ftp'],
bypass_hosts=bypass_hosts)
return _set_proxy_osx("setftpproxy", server, port, user, password, network_service)
return _set_proxy_osx(cmd_function="setftpproxy",
server=server,
port=port,
user=user,
password=password,
network_service=network_service)
def get_proxy_bypass(network_service="Ethernet"):
@ -309,12 +366,16 @@ def get_proxy_bypass(network_service="Ethernet"):
'''
if __grains__['os'] == 'Windows':
reg_val = __salt__['reg.read_value']('HKEY_CURRENT_USER',
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
'ProxyOverride')
bypass_servers = reg_val['vdata'].replace("<local>", "").split(";")
reg_val = __salt__['reg.read_value'](
hive='HKEY_CURRENT_USER',
key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings',
vname='ProxyOverride')['vdata']
return bypass_servers
# `reg.read_value` returns None if the key doesn't exist
if reg_val is None:
return []
return reg_val.replace('<local>', '').split(';')
out = __salt__['cmd.run']('networksetup -getproxybypassdomains {0}'.format(network_service))
@ -357,7 +418,12 @@ def set_proxy_win(server, port, types=None, bypass_hosts=None):
The password to use if required by the server
types
The types of proxy connections should be setup with this server. Valid types are http and https.
The types of proxy connections should be setup with this server. Valid
types are:
- ``http``
- ``https``
- ``ftp``
bypass_hosts
The hosts that are allowed to by pass the proxy.
@ -369,7 +435,10 @@ def set_proxy_win(server, port, types=None, bypass_hosts=None):
salt '*' proxy.set_http_proxy example.com 1080 types="['http', 'https']"
'''
if __grains__['os'] == 'Windows':
return _set_proxy_windows(server, port, types, bypass_hosts)
return _set_proxy_windows(server=server,
port=port,
types=types,
bypass_hosts=bypass_hosts)
def get_proxy_win():

View file

@ -19,6 +19,7 @@ Module to provide redis functionality to Salt
from __future__ import absolute_import
from salt.ext.six.moves import zip
from salt.ext import six
from salt.utils import clean_kwargs
from datetime import datetime
# Import third party libs
@ -395,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, **fieldsvals)
return server.hmset(key, clean_kwargs(**fieldsvals))
def hset(key, field, value, host=None, port=None, db=None, password=None):

View file

@ -35,6 +35,7 @@ def __virtual__():
'Devuan',
'Arch',
'Arch ARM',
'Manjaro',
'ALT',
'SUSE Enterprise Server',
'SUSE',

View file

@ -296,7 +296,7 @@ def _register_functions():
modules_ = [module_ for module_ in modules.modules]
for mod_name in modules_:
mod_name = _to_snake_case(module_)
mod_name = _to_snake_case(mod_name)
mod_func = _copy_function(mod_name, str(mod_name))
mod_func.__doc__ = _build_doc(mod_name)
__all__.append(mod_name)

View file

@ -683,7 +683,7 @@ def modify(name,
win32service.SERVICE_QUERY_CONFIG)
except pywintypes.error as exc:
raise CommandExecutionError(
'Failed To Open {0}: {1}'.format(name, exc[2]))
'Failed To Open {0}: {1}'.format(name, exc))
config_info = win32service.QueryServiceConfig(handle_svc)

View file

@ -581,6 +581,8 @@ def latest_version(*names, **kwargs):
status = pkg_info.get('status', '').lower()
if status.find('not installed') > -1 or status.find('out-of-date') > -1:
ret[name] = pkg_info.get('version')
else:
ret[name] = ''
# Return a string if only one package name passed
if len(names) == 1 and len(ret):

View file

@ -210,7 +210,7 @@ import tornado
TORNADO_50 = tornado.version_info >= (5,)
if not TORNADO_50:
import zmq.eventloop.ioloop
import zmq.eventloop.ioloop # pylint: disable=import-error
# instantiate the zmq IOLoop (specialized poller)
zmq.eventloop.ioloop.install()

View file

@ -923,6 +923,11 @@ class Pillar(object):
decrypt_errors = self.decrypt_pillar(pillar)
if decrypt_errors:
pillar.setdefault('_errors', []).extend(decrypt_errors)
# Reset the file_roots for the renderers
for mod_name in sys.modules:
if mod_name.startswith('salt.loaded.int.render.'):
sys.modules[mod_name].__opts__['file_roots'] = self.actual_file_roots
return pillar
def decrypt_pillar(self, pillar):

View file

@ -150,7 +150,7 @@ def lookup_jid(jid,
try:
# Check if the return data has an 'out' key. We'll use that as the
# outputter in the absence of one being passed on the CLI.
outputter = data[next(iter(data))].get('out')
outputter = returns[next(iter(returns))].get('out')
except (StopIteration, AttributeError):
outputter = None

View file

@ -80,6 +80,8 @@ def _checksum_file_path(path):
drive.rstrip(':'),
path.lstrip('/\\'),
)
elif str(exc).startswith('Cannot mix UNC'):
relpath = salt.utils.path_join('unc', path)
else:
raise
ret = salt.utils.path_join(__opts__['cachedir'], 'archive_hash', relpath)

View file

@ -1396,19 +1396,25 @@ def symlink(
preflight_errors = []
if salt.utils.is_windows():
# Make sure the passed owner exists
if not salt.utils.win_functions.get_sid_from_name(win_owner):
try:
salt.utils.win_functions.get_sid_from_name(win_owner)
except CommandExecutionError as exc:
preflight_errors.append('User {0} does not exist'.format(win_owner))
# Make sure users passed in win_perms exist
if win_perms:
for name_check in win_perms:
if not salt.utils.win_functions.get_sid_from_name(name_check):
try:
salt.utils.win_functions.get_sid_from_name(name_check)
except CommandExecutionError as exc:
preflight_errors.append('User {0} does not exist'.format(name_check))
# Make sure users passed in win_deny_perms exist
if win_deny_perms:
for name_check in win_deny_perms:
if not salt.utils.win_functions.get_sid_from_name(name_check):
try:
salt.utils.win_functions.get_sid_from_name(name_check)
except CommandExecutionError as exc:
preflight_errors.append('User {0} does not exist'.format(name_check))
else:
uid = __salt__['file.user_to_uid'](user)
@ -5663,7 +5669,7 @@ def copy(
if not os.path.isdir(dname):
if makedirs:
try:
_makedirs(name=name)
_makedirs(name=name, user=user, group=group, dir_mode=mode)
except CommandExecutionError as exc:
return _error(ret, 'Drive {0} is not mapped'.format(exc.message))
else:

View file

@ -72,9 +72,16 @@ def _changes(name,
delusers = [salt.utils.win_functions.get_sam_name(user).lower() for user in delusers]
change = {}
ret = {}
if gid:
if lgrp['gid'] != gid:
change['gid'] = gid
try:
gid = int(gid)
if lgrp['gid'] != gid:
change['gid'] = gid
except (TypeError, ValueError):
ret['result'] = False
ret['comment'] = 'Invalid gid'
return ret
if members:
# -- if new member list if different than the current

View file

@ -275,9 +275,13 @@ def bootstrap(name, user=None, silent=True):
if __opts__['test']:
try:
call = __salt__['npm.install'](dir=name, runas=user, pkg=None, silent=silent, dry_run=True)
ret['result'] = None
ret['changes'] = {'old': [], 'new': call}
ret['comment'] = '{0} is set to be bootstrapped'.format(name)
if call:
ret['result'] = None
ret['changes'] = {'old': [], 'new': call}
ret['comment'] = '{0} is set to be bootstrapped'.format(name)
else:
ret['result'] = True
ret['comment'] = '{0} is already bootstrapped'.format(name)
except (CommandNotFoundError, CommandExecutionError) as err:
ret['result'] = False
ret['comment'] = 'Error Bootstrapping \'{0}\': {1}'.format(name, err)

View file

@ -70,10 +70,9 @@ def _init_libcrypto():
libcrypto.RSA_public_decrypt.argtypes = (c_int, c_char_p, c_char_p, c_void_p, c_int)
try:
if libcrypto.OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG |
OPENSSL_INIT_ADD_ALL_CIPHERS |
OPENSSL_INIT_ADD_ALL_DIGESTS, None) != 1:
raise OSError("Failed to initialize OpenSSL library (OPENSSL_init_crypto failed)")
libcrypto.OPENSSL_init_crypto(OPENSSL_INIT_NO_LOAD_CONFIG |
OPENSSL_INIT_ADD_ALL_CIPHERS |
OPENSSL_INIT_ADD_ALL_DIGESTS, None)
except AttributeError:
# Support for OpenSSL < 1.1 (OPENSSL_API_COMPAT < 0x10100000L)
libcrypto.OPENSSL_no_config()

View file

@ -156,20 +156,23 @@ def query(key, keyid, method='GET', params=None, headers=None,
headers=headers,
data=data,
verify=verify_ssl,
stream=True)
stream=True,
timeout=300)
elif method == 'GET' and local_file and not return_bin:
result = requests.request(method,
requesturl,
headers=headers,
data=data,
verify=verify_ssl,
stream=True)
stream=True,
timeout=300)
else:
result = requests.request(method,
requesturl,
headers=headers,
data=data,
verify=verify_ssl)
verify=verify_ssl,
timeout=300)
finally:
if data is not None:
data.close()

View file

@ -14,8 +14,8 @@
# Import python libs
from __future__ import absolute_import
# pylint: disable=blacklisted-module
from distutils.version import StrictVersion as _StrictVersion
from distutils.version import LooseVersion as _LooseVersion
from distutils.version import StrictVersion as _StrictVersion # pylint: disable=no-name-in-module
from distutils.version import LooseVersion as _LooseVersion # pylint: disable=no-name-in-module
# pylint: enable=blacklisted-module
# Import 3rd-party libs

View file

@ -140,7 +140,7 @@ def accept_dict(match, include_rejected=False, include_denied=False):
.. code-block:: python
>>> wheel.cmd('accept_dict',
>>> wheel.cmd('key.accept_dict',
{
'minions_pre': [
'jerry',

View file

@ -366,14 +366,14 @@ class DownloadWindowsDlls(Command):
import pip
# pip has moved many things to `_internal` starting with pip 10
if LooseVersion(pip.__version__) < LooseVersion('10.0'):
from pip.utils.logging import indent_log
from pip.utils.logging import indent_log # pylint: disable=no-name-in-module
else:
from pip._internal.utils.logging import indent_log # pylint: disable=no-name-in-module
platform_bits, _ = platform.architecture()
url = 'https://repo.saltstack.com/windows/dependencies/{bits}/{fname}.dll'
dest = os.path.join(os.path.dirname(sys.executable), '{fname}.dll')
with indent_log():
for fname in ('libeay32', 'ssleay32', 'msvcr120'):
for fname in ('libeay32', 'libsodium', 'ssleay32', 'msvcr120'):
# See if the library is already on the system
if find_library(fname):
continue

View file

@ -0,0 +1 @@
Hello, World!

View file

@ -3,3 +3,7 @@ ssh-file-test:
file.managed:
- name: /tmp/{{ jinja }}
- contents: 'test'
second_id:
cmd.run:
- name: echo test

View file

@ -197,11 +197,12 @@ class MacServiceModuleTest(ModuleCase):
self.assertFalse(
self.run_function('service.disabled', [SERVICE_NAME]))
self.assertTrue(self.run_function('service.stop', [SERVICE_NAME]))
self.assertTrue(self.run_function('service.disable', [SERVICE_NAME]))
self.assertTrue(
self.run_function('service.disabled', [SERVICE_NAME]))
self.assertTrue(self.run_function('service.enable', [SERVICE_NAME]))
self.assertTrue(self.run_function('service.disabled', ['spongebob']))
self.assertFalse(self.run_function('service.disabled', ['spongebob']))
def test_get_all(self):
'''

View file

@ -11,7 +11,7 @@ import string
# Import Salt Testing libs
from tests.support.case import ModuleCase
from tests.support.unit import skipIf
from tests.support.helpers import destructiveTest, skip_if_not_root
from tests.support.helpers import destructiveTest, skip_if_not_root, flaky
# Import salt libs
import salt.utils
@ -33,6 +33,7 @@ SET_SUBNET_NAME = __random_string()
@skip_if_not_root
@flaky
@skipIf(not salt.utils.is_darwin(), 'Test only available on macOS')
@skipIf(not salt.utils.which('systemsetup'), '\'systemsetup\' binary not found in $PATH')
class MacSystemModuleTest(ModuleCase):

View file

@ -17,13 +17,14 @@ import datetime
# Import Salt Testing libs
from tests.support.case import ModuleCase
from tests.support.unit import skipIf
from tests.support.helpers import destructiveTest, skip_if_not_root
from tests.support.helpers import destructiveTest, skip_if_not_root, flaky
# Import salt libs
import salt.utils
@skip_if_not_root
@flaky
@skipIf(not salt.utils.is_darwin(), 'Test only available on macOS')
@skipIf(not salt.utils.which('systemsetup'), '\'systemsetup\' binary not found in $PATH')
class MacTimezoneModuleTest(ModuleCase):

View file

@ -118,21 +118,21 @@ class ServiceModuleTest(ModuleCase):
systemd = salt.utils.systemd.booted()
# check service was not enabled
if systemd or salt.utils.is_windows():
self.assertIn('ERROR', enable)
else:
try:
self.assertFalse(enable)
except AssertionError:
self.assertIn('ERROR', enable)
# check service was not disabled
if tuple(self.run_function('grains.item', ['osrelease_info'])['osrelease_info']) == (14, 0o4) and not systemd:
# currently upstart does not have a mechanism to report if disabling a service fails if does not exist
self.assertTrue(self.run_function('service.disable', [srv_name]))
else:
if salt.utils.is_windows():
try:
disable = self.run_function('service.disable', [srv_name])
self.assertFalse(disable)
except AssertionError:
self.assertTrue('error' in disable.lower())
else:
self.assertFalse(self.run_function('service.disable', [srv_name]))
if salt.utils.is_darwin():
self.assertFalse(self.run_function('service.disabled', [srv_name]))

View file

@ -382,3 +382,88 @@ class OrchEventTest(ShellCase):
self.assertTrue(received)
del listener
signal.alarm(0)
def test_orchestration_with_pillar_dot_items(self):
'''
Test to confirm when using a state file that includes other state file, if
one of those state files includes pillar related functions that will not
be pulling from the pillar cache that all the state files are available and
the file_roots has been preserved. See issues #48277 and #46986.
'''
self.write_conf({
'fileserver_backend': ['roots'],
'file_roots': {
'base': [self.base_env],
},
})
orch_sls = os.path.join(self.base_env, 'main.sls')
with salt.utils.fopen(orch_sls, 'w') as fp_:
fp_.write(textwrap.dedent('''
include:
- one
- two
- three
'''))
orch_sls = os.path.join(self.base_env, 'one.sls')
with salt.utils.fopen(orch_sls, 'w') as fp_:
fp_.write(textwrap.dedent('''
{%- set foo = salt['saltutil.runner']('pillar.show_pillar') %}
placeholder_one:
test.succeed_without_changes
'''))
orch_sls = os.path.join(self.base_env, 'two.sls')
with salt.utils.fopen(orch_sls, 'w') as fp_:
fp_.write(textwrap.dedent('''
placeholder_two:
test.succeed_without_changes
'''))
orch_sls = os.path.join(self.base_env, 'three.sls')
with salt.utils.fopen(orch_sls, 'w') as fp_:
fp_.write(textwrap.dedent('''
placeholder_three:
test.succeed_without_changes
'''))
orch_sls = os.path.join(self.base_env, 'main.sls')
listener = salt.utils.event.get_event(
'master',
sock_dir=self.master_opts['sock_dir'],
transport=self.master_opts['transport'],
opts=self.master_opts)
start_time = time.time()
jid = self.run_run_plus(
'state.orchestrate',
'main',
__reload_config=True).get('jid')
if jid is None:
raise salt.exceptions.SaltInvocationError('jid missing from run_run_plus output')
signal.signal(signal.SIGALRM, self.alarm_handler)
signal.alarm(self.timeout)
received = False
try:
while True:
event = listener.get_event(full=True)
if event is None:
continue
if event.get('tag', '') == 'salt/run/{0}/ret'.format(jid):
received = True
# Don't wrap this in a try/except. We want to know if the
# data structure is different from what we expect!
ret = event['data']['return']['data']['master']
for state in ret:
data = ret[state]
# Each state should be successful
self.assertEqual(data['comment'], 'Success!')
break
finally:
self.assertTrue(received)
del listener
signal.alarm(0)

View file

@ -24,9 +24,12 @@ class SSHStateTest(SSHCase):
'''
testing the state system with salt-ssh
'''
def _check_dict_ret(self, ret, val, exp_ret):
def _check_dict_ret(self, ret, val, exp_ret, equal=True):
for key, value in ret.items():
self.assertEqual(value[val], exp_ret)
if equal:
self.assertEqual(value[val], exp_ret)
else:
self.assertNotEqual(value[val], exp_ret)
def _check_request(self, empty=False):
check = self.run_function('state.check_request', wipe=False)
@ -50,12 +53,31 @@ class SSHStateTest(SSHCase):
'''
test state.sls_id with salt-ssh
'''
# check state.sls_id with test=True
ret = self.run_function('state.sls_id', ['ssh-file-test', SSH_SLS,
'test=True'])
self._check_dict_ret(ret=ret, val='comment',
exp_ret='The file /tmp/test is set to be changed')
# check state.sls_id without test=True
ret = self.run_function('state.sls_id', ['ssh-file-test', SSH_SLS])
self._check_dict_ret(ret=ret, val='__sls__', exp_ret=SSH_SLS)
# make sure the other id in the state was not run
self._check_dict_ret(ret=ret, val='__id__',
exp_ret='second_id', equal=False)
check_file = self.run_function('file.file_exists', ['/tmp/test'])
self.assertTrue(check_file)
def test_state_sls_wrong_id(self):
'''
test state.sls_id when id does not exist
'''
# check state.sls_id with test=True
ret = self.run_function('state.sls_id', ['doesnotexist', SSH_SLS])
assert 'No matches for ID' in ret
def test_state_show_sls(self):
'''
test state.show_sls with salt-ssh

View file

@ -25,6 +25,7 @@ from tests.support.case import ModuleCase
from tests.support.unit import skipIf
from tests.support.paths import FILES, TMP, TMP_STATE_TREE
from tests.support.helpers import (
destructiveTest,
skip_if_not_root,
with_system_user_and_group,
with_tempfile,
@ -137,6 +138,10 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
'''
remove files created in previous tests
'''
user = 'salt'
if user in str(self.run_function('user.list_users', [user])):
self.run_function('user.delete', [user])
for path in (FILEPILLAR, FILEPILLARDEF, FILEPILLARGIT):
try:
os.remove(path)
@ -656,6 +661,57 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
self.assertIn(
'does not exist', ret['comment'])
def test_managed_source_hash_indifferent_case(self):
'''
Test passing a source_hash as an uppercase hash.
This is a regression test for Issue #38914 and Issue #48230 (test=true use).
'''
name = os.path.join(TMP, 'source_hash_indifferent_case')
state_name = 'file_|-/tmp/salt-tests-tmpdir/source_hash_indifferent_case_|' \
'-/tmp/salt-tests-tmpdir/source_hash_indifferent_case_|-managed'
local_path = os.path.join(FILES, 'file', 'base', 'hello_world.txt')
actual_hash = 'c98c24b677eff44860afea6f493bbaec5bb1c4cbb209c6fc2bbb47f66ff2ad31'
uppercase_hash = actual_hash.upper()
try:
# Lay down tmp file to test against
self.run_state(
'file.managed',
name=name,
source=local_path,
source_hash=actual_hash
)
# Test uppercase source_hash: should return True with no changes
ret = self.run_state(
'file.managed',
name=name,
source=local_path,
source_hash=uppercase_hash
)
assert ret[state_name]['result'] is True
assert ret[state_name]['pchanges'] == {}
assert ret[state_name]['changes'] == {}
# Test uppercase source_hash using test=true
# Should return True with no changes
ret = self.run_state(
'file.managed',
name=name,
source=local_path,
source_hash=uppercase_hash,
test=True
)
assert ret[state_name]['result'] is True
assert ret[state_name]['pchanges'] == {}
assert ret[state_name]['changes'] == {}
finally:
# Clean Up File
if os.path.exists(name):
os.remove(name)
def test_directory(self):
'''
file.directory
@ -2478,6 +2534,27 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
os.remove(source)
os.remove(dest)
@destructiveTest
@with_tempfile
def test_file_copy_make_dirs(self, source):
'''
ensure make_dirs creates correct user perms
'''
shutil.copyfile(os.path.join(FILES, 'hosts'), source)
dest = os.path.join(TMP, 'dir1', 'dir2', 'copied_file.txt')
user = 'salt'
mode = '0644'
self.run_function('user.add', [user])
ret = self.run_state('file.copy', name=dest, source=source, user=user,
makedirs=True, mode=mode)
file_checks = [dest, os.path.join(TMP, 'dir1'), os.path.join(TMP, 'dir1', 'dir2')]
for check in file_checks:
user_check = self.run_function('file.get_user', [check])
mode_check = self.run_function('file.get_mode', [check])
assert user_check == user
assert salt.utils.normalize_mode(mode_check) == mode
def test_contents_pillar_with_pillar_list(self):
'''
This tests for any regressions for this issue:
@ -2488,6 +2565,37 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin):
ret = self.run_function('state.sls', mods=state_file)
self.assertSaltTrueReturn(ret)
@skip_if_not_root
@skipIf(not HAS_PWD, "pwd not available. Skipping test")
@skipIf(not HAS_GRP, "grp not available. Skipping test")
@with_system_user_and_group('user12209', 'group12209',
on_existing='delete', delete=True)
def test_issue_48336_file_managed_mode_setuid(self, user, group):
'''
Ensure that mode is correct with changing of ownership and group
symlinks)
'''
tempfile = os.path.join(TMP, 'temp_file_issue_48336')
# Run the state
ret = self.run_state(
'file.managed', name=tempfile,
user=user, group=group, mode='4750',
)
self.assertSaltTrueReturn(ret)
# Check that the owner and group are correct, and
# the mode is what we expect
temp_file_stats = os.stat(tempfile)
# Normalize the mode
temp_file_mode = six.text_type(oct(stat.S_IMODE(temp_file_stats.st_mode)))
temp_file_mode = salt.utils.normalize_mode(temp_file_mode)
self.assertEqual(temp_file_mode, '4750')
self.assertEqual(pwd.getpwuid(temp_file_stats.st_uid).pw_name, user)
self.assertEqual(grp.getgrgid(temp_file_stats.st_gid).gr_name, group)
class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin):
marker_start = '# start'

View file

@ -33,7 +33,7 @@ class NpmStateTest(ModuleCase, SaltReturnAssertsMixin):
Basic test to determine if NPM module was successfully installed and
removed.
'''
ret = self.run_state('npm.installed', name='pm2', registry="http://registry.npmjs.org/")
ret = self.run_state('npm.installed', name='pm2@2.10.4', registry="http://registry.npmjs.org/")
self.assertSaltTrueReturn(ret)
ret = self.run_state('npm.removed', name='pm2')
self.assertSaltTrueReturn(ret)
@ -69,7 +69,7 @@ class NpmStateTest(ModuleCase, SaltReturnAssertsMixin):
Basic test to determine if NPM module successfully installs multiple
packages.
'''
ret = self.run_state('npm.installed', name=None, pkgs=['pm2', 'grunt'], registry="http://registry.npmjs.org/")
ret = self.run_state('npm.installed', name=None, pkgs=['pm2@2.10.4', 'grunt@1.0.2'], registry="http://registry.npmjs.org/")
self.assertSaltTrueReturn(ret)
@skipIf(salt.utils.which('npm') and LooseVersion(cmd.run('npm -v')) >= LooseVersion(MAX_NPM_VERSION),

View file

@ -22,6 +22,11 @@ from tests.support.mixins import SaltReturnAssertsMixin
# Import salt libs
import salt.utils
try:
import grp
except ImportError:
grp = None
if salt.utils.is_darwin():
USER = 'macuser'
GROUP = 'macuser'
@ -32,13 +37,11 @@ elif salt.utils.is_windows():
GROUP = 'winuser'
GID = randint(400, 500)
NOGROUPGID = randint(400, 500)
grp = None
else:
USER = 'nobody'
GROUP = 'nobody'
GID = 'nobody'
NOGROUPGID = 'nogroup'
import grp
@destructiveTest

View file

@ -14,6 +14,7 @@ import logging
import os
import shutil
import signal
import socket
import subprocess
import sys
import tempfile
@ -590,8 +591,24 @@ class TestSaltProgram(six.with_metaclass(TestSaltProgramMeta, TestProgram)):
'log_dir',
'script_dir',
])
pub_port = 4505
ret_port = 4506
for port in [pub_port, ret_port]:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
connect = sock.bind(('localhost', port))
except:
# these ports are already in use, use different ones
pub_port = 4606
ret_port = 4607
break
sock.close()
config_base = {
'root_dir': '{test_dir}',
'publish_port': pub_port,
'ret_port': ret_port,
}
configs = {}
config_dir = os.path.join('etc', 'salt')

View file

@ -789,6 +789,7 @@ class CronTestCase(TestCase, LoaderModuleMockMixin):
cron.raw_cron(STUB_USER)
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l",
runas=STUB_USER,
ignore_retcode=True,
rstrip=False,
python_shell=False)
@ -803,6 +804,7 @@ class CronTestCase(TestCase, LoaderModuleMockMixin):
MagicMock(return_value=False)):
cron.raw_cron(STUB_USER)
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -u root -l",
ignore_retcode=True,
rstrip=False,
python_shell=False)
@ -818,6 +820,7 @@ class CronTestCase(TestCase, LoaderModuleMockMixin):
cron.raw_cron(STUB_USER)
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l",
runas=STUB_USER,
ignore_retcode=True,
rstrip=False,
python_shell=False)
@ -833,6 +836,7 @@ class CronTestCase(TestCase, LoaderModuleMockMixin):
cron.raw_cron(STUB_USER)
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l",
runas=STUB_USER,
ignore_retcode=True,
rstrip=False,
python_shell=False)
@ -848,6 +852,7 @@ class CronTestCase(TestCase, LoaderModuleMockMixin):
cron.raw_cron(STUB_USER)
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l",
runas=STUB_USER,
ignore_retcode=True,
rstrip=False,
python_shell=False)
@ -863,6 +868,7 @@ class CronTestCase(TestCase, LoaderModuleMockMixin):
cron.raw_cron(STUB_USER)
cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l",
runas=STUB_USER,
ignore_retcode=True,
rstrip=False,
python_shell=False)

View file

@ -6,6 +6,7 @@
# Import Python Libs
from __future__ import absolute_import
import json
import textwrap
# Import Salt Testing Libs
from tests.support.mixins import LoaderModuleMockMixin
@ -34,43 +35,87 @@ class NpmTestCase(TestCase, LoaderModuleMockMixin):
self.addCleanup(patcher.stop)
return {npm: {}}
# 'install' function tests: 1
# 'install' function tests: 4
def test_install(self):
'''
Test if it install an NPM package.
Test if it installs an NPM package.
'''
mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'})
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
self.assertRaises(CommandExecutionError, npm.install,
'coffee-script')
mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error',
'stdout': '{"salt": ["SALT"]}'})
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
mock_err = MagicMock(return_value='SALT')
with patch.object(json, 'loads', mock_err):
self.assertEqual(npm.install('coffee-script'), 'SALT')
# This is at least somewhat closer to the actual output format.
mock_json_out = textwrap.dedent('''\
[
{
"salt": "SALT"
}
]''')
mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error',
'stdout': '{"salt": ["SALT"]}'})
# Successful run, expected output format
mock = MagicMock(return_value={'retcode': 0, 'stderr': '',
'stdout': mock_json_out})
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
self.assertEqual(npm.install('coffee-script'),
[{u'salt': u'SALT'}])
mock_json_out_extra = textwrap.dedent('''\
Compilation output here
[bcrypt] Success: "/tmp/node_modules/bcrypt/foo" is installed via remote"
[grpc] Success: "/usr/lib/node_modules/@foo/bar" is installed via remote"
[
{
"from" : "express@",
"name" : "express",
"dependencies" : {
"escape-html" : {
"from" : "escape-html@~1.0.3",
"dependencies" : {},
"version" : "1.0.3"
}
},
"version" : "4.16.3"
}
]''')
extra_expected = [{u'dependencies':
{u'escape-html': {
u'dependencies': {},
u'from': u'escape-html@~1.0.3',
u'version': u'1.0.3'}
},
u'from': u'express@',
u'name': u'express',
u'version': u'4.16.3'}]
# Successful run, expected output format with additional leading text
mock = MagicMock(return_value={'retcode': 0, 'stderr': '',
'stdout': mock_json_out_extra})
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
self.assertEqual(npm.install('coffee-script'), extra_expected)
# Successful run, unexpected output format
mock = MagicMock(return_value={'retcode': 0, 'stderr': '',
'stdout': 'SALT'})
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
mock_err = MagicMock(side_effect=ValueError())
# When JSON isn't successfully parsed, return should equal input
with patch.object(json, 'loads', mock_err):
self.assertEqual(npm.install('coffee-script'),
'{"salt": ["SALT"]}')
self.assertEqual(npm.install('coffee-script'), 'SALT')
# 'uninstall' function tests: 1
def test_uninstall(self):
'''
Test if it uninstall an NPM package.
Test if it uninstalls an NPM package.
'''
mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'})
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
self.assertFalse(npm.uninstall('coffee-script'))
mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error'})
mock = MagicMock(return_value={'retcode': 0, 'stderr': ''})
with patch.dict(npm.__salt__, {'cmd.run_all': mock}):
self.assertTrue(npm.uninstall('coffee-script'))

View file

@ -126,65 +126,56 @@ class ProxyTestCase(TestCase, LoaderModuleMockMixin):
def test_get_http_proxy_windows(self):
'''
Test to make sure that we correctly get the current proxy info
on Windows
Test to make sure that we correctly get the current proxy info on
Windows
'''
result = {'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'}
mock = MagicMock(return_value=result)
expected = {'server': '192.168.0.1',
'port': '3128'}
with patch.dict(proxy.__grains__, {'os': 'Windows'}):
result = {
'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'
}
mock = MagicMock(return_value=result)
expected = {
'server': '192.168.0.1',
'port': '3128'
}
with patch.dict(proxy.__salt__, {'reg.read_value': mock}):
out = proxy.get_http_proxy()
mock.assert_called_once_with('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyServer')
mock.assert_called_once_with(
hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyServer')
self.assertEqual(expected, out)
def test_get_https_proxy_windows(self):
'''
Test to make sure that we correctly get the current proxy info
on Windows
Test to make sure that we correctly get the current proxy info on
Windows
'''
result = {'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'}
mock = MagicMock(return_value=result)
expected = {'server': '192.168.0.2',
'port': '3128'}
with patch.dict(proxy.__grains__, {'os': 'Windows'}):
result = {
'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'
}
mock = MagicMock(return_value=result)
expected = {
'server': '192.168.0.2',
'port': '3128'
}
with patch.dict(proxy.__salt__, {'reg.read_value': mock}):
out = proxy.get_https_proxy()
mock.assert_called_once_with('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyServer')
mock.assert_called_once_with(
hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyServer')
self.assertEqual(expected, out)
def test_get_ftp_proxy_windows(self):
'''
Test to make sure that we correctly get the current proxy info
on Windows
Test to make sure that we correctly get the current proxy info on
Windows
'''
result = {'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'}
mock = MagicMock(return_value=result)
expected = {'server': '192.168.0.3',
'port': '3128'}
with patch.dict(proxy.__grains__, {'os': 'Windows'}):
result = {
'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'
}
mock = MagicMock(return_value=result)
expected = {
'server': '192.168.0.3',
'port': '3128'
}
with patch.dict(proxy.__salt__, {'reg.read_value': mock}):
out = proxy.get_ftp_proxy()
mock.assert_called_once_with('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyServer')
mock.assert_called_once_with(
hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyServer')
self.assertEqual(expected, out)
def test_get_all_proxies_macos_fails(self):
@ -196,201 +187,179 @@ class ProxyTestCase(TestCase, LoaderModuleMockMixin):
def test_get_all_proxies_windows(self):
'''
Test to make sure that we correctly get the current proxy info
on Windows
Test to make sure that we correctly get the current proxy info on
Windows
'''
results = [{'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'},
{'vdata': 1}]
mock = MagicMock(side_effect=results)
expected = {'enabled': True,
'http': {'server': '192.168.0.1',
'port': '3128'},
'https': {'server': '192.168.0.2',
'port': '3128'},
'ftp': {'server': '192.168.0.3',
'port': '3128'}}
calls = [
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyServer'),
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyEnable')]
with patch.dict(proxy.__grains__, {'os': 'Windows'}):
results = [
{
'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'
},
{
'vdata': 1
}
]
mock = MagicMock(side_effect=results)
expected = {
'enabled': True,
'http': {
'server': '192.168.0.1',
'port': '3128'
},
'https': {
'server': '192.168.0.2',
'port': '3128'
},
'ftp': {
'server': '192.168.0.3',
'port': '3128'
}
}
with patch.dict(proxy.__salt__, {'reg.read_value': mock}):
out = proxy.get_proxy_win()
calls = [
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyServer'),
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyEnable'),
]
mock.assert_has_calls(calls)
self.assertEqual(expected, out)
def test_set_http_proxy_windows(self):
'''
Test to make sure that we correctly set the proxy info
on Windows
Test to make sure that we correctly set the proxy info on Windows
'''
calls = [
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyServer',
vdata='http=192.168.0.1:3128;'),
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyEnable',
vdata=1,
vtype='REG_DWORD'),
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyOverride',
vdata='<local>;.moo.com;.salt.com')]
mock_reg = MagicMock()
mock_cmd = MagicMock()
with patch.dict(proxy.__grains__, {'os': 'Windows'}):
mock_reg = MagicMock()
mock_cmd = MagicMock()
with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg, 'cmd.run': mock_cmd}):
out = proxy.set_http_proxy('192.168.0.1', 3128, bypass_hosts=['.moo.com', '.salt.com'])
calls = [
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyServer',
'http=192.168.0.1:3128;'),
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyEnable',
1,
vtype='REG_DWORD'),
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyOverride',
'<local>;.moo.com;.salt.com')
]
with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg,
'cmd.run': mock_cmd}):
out = proxy.set_http_proxy(server='192.168.0.1',
port=3128,
bypass_hosts=['.moo.com', '.salt.com'])
mock_reg.assert_has_calls(calls)
mock_cmd.assert_called_once_with('netsh winhttp import proxy source=ie')
self.assertTrue(out)
def test_set_https_proxy_windows(self):
'''
Test to make sure that we correctly set the proxy info
on Windows
Test to make sure that we correctly set the proxy info on Windows
'''
calls = [
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyServer',
vdata='https=192.168.0.1:3128;'),
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyEnable',
vdata=1,
vtype='REG_DWORD'),
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyOverride',
vdata='<local>;.moo.com;.salt.com')]
mock_reg = MagicMock()
mock_cmd = MagicMock()
with patch.dict(proxy.__grains__, {'os': 'Windows'}):
mock_reg = MagicMock()
mock_cmd = MagicMock()
with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg, 'cmd.run': mock_cmd}):
out = proxy.set_https_proxy('192.168.0.1', 3128, bypass_hosts=['.moo.com', '.salt.com'])
calls = [
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyServer',
'https=192.168.0.1:3128;'),
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyEnable',
1,
vtype='REG_DWORD'),
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyOverride',
'<local>;.moo.com;.salt.com')
]
with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg,
'cmd.run': mock_cmd}):
out = proxy.set_https_proxy(server='192.168.0.1',
port=3128,
bypass_hosts=['.moo.com', '.salt.com'])
mock_reg.assert_has_calls(calls)
mock_cmd.assert_called_once_with('netsh winhttp import proxy source=ie')
self.assertTrue(out)
def test_set_ftp_proxy_windows(self):
'''
Test to make sure that we correctly set the proxy info
on Windows
Test to make sure that we correctly set the proxy info on Windows
'''
calls = [
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyServer',
vdata='ftp=192.168.0.1:3128;'),
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyEnable',
vdata=1,
vtype='REG_DWORD'),
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyOverride',
vdata='<local>;.moo.com;.salt.com')]
mock_reg = MagicMock()
mock_cmd = MagicMock()
with patch.dict(proxy.__grains__, {'os': 'Windows'}):
mock_reg = MagicMock()
mock_cmd = MagicMock()
with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg, 'cmd.run': mock_cmd}):
out = proxy.set_ftp_proxy('192.168.0.1', 3128, bypass_hosts=['.moo.com', '.salt.com'])
calls = [
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyServer',
'ftp=192.168.0.1:3128;'),
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyEnable',
1,
vtype='REG_DWORD'),
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyOverride',
'<local>;.moo.com;.salt.com')
]
with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg,
'cmd.run': mock_cmd}):
out = proxy.set_ftp_proxy(server='192.168.0.1',
port=3128,
bypass_hosts=['.moo.com', '.salt.com'])
mock_reg.assert_has_calls(calls)
mock_cmd.assert_called_once_with('netsh winhttp import proxy source=ie')
self.assertTrue(out)
def test_set_proxy_windows(self):
'''
Test to make sure that we correctly set the proxy info
on Windows
Test to make sure that we correctly set the proxy info on Windows
'''
calls = [
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyServer',
vdata='http=192.168.0.1:3128;https=192.168.0.1:3128;ftp=192.168.0.1:3128;'),
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyEnable',
vdata=1,
vtype='REG_DWORD'),
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyOverride',
vdata='<local>;.moo.com;.salt.com')]
mock_reg = MagicMock()
mock_cmd = MagicMock()
with patch.dict(proxy.__grains__, {'os': 'Windows'}):
mock_reg = MagicMock()
mock_cmd = MagicMock()
with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg, 'cmd.run': mock_cmd}):
out = proxy.set_proxy_win('192.168.0.1', 3128, bypass_hosts=['.moo.com', '.salt.com'])
calls = [
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyServer',
'http=192.168.0.1:3128;https=192.168.0.1:3128;ftp=192.168.0.1:3128;'),
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyEnable',
1,
vtype='REG_DWORD'),
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyOverride',
'<local>;.moo.com;.salt.com')
]
with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg,
'cmd.run': mock_cmd}):
out = proxy.set_proxy_win(server='192.168.0.1',
port=3128,
bypass_hosts=['.moo.com', '.salt.com'])
mock_reg.assert_has_calls(calls)
mock_cmd.assert_called_once_with('netsh winhttp import proxy source=ie')
self.assertTrue(out)
def test_set_proxy_windows_no_ftp(self):
'''
Test to make sure that we correctly set the proxy info
on Windows
Test to make sure that we correctly set the proxy info on Windows
'''
calls = [
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyServer',
vdata='http=192.168.0.1:3128;https=192.168.0.1:3128;'),
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyEnable',
vdata=1,
vtype='REG_DWORD'),
call(hive='HKEY_CURRENT_USER',
key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
vname='ProxyOverride',
vdata='<local>;.moo.com;.salt.com')]
mock_reg = MagicMock()
mock_cmd = MagicMock()
with patch.dict(proxy.__grains__, {'os': 'Windows'}):
mock_reg = MagicMock()
mock_cmd = MagicMock()
with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg, 'cmd.run': mock_cmd}):
out = proxy.set_proxy_win('192.168.0.1', 3128, types=['http', 'https'],
with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg,
'cmd.run': mock_cmd}):
out = proxy.set_proxy_win(server='192.168.0.1',
port=3128,
types=['http', 'https'],
bypass_hosts=['.moo.com', '.salt.com'])
calls = [
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyServer',
'http=192.168.0.1:3128;https=192.168.0.1:3128;'),
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyEnable',
1,
vtype='REG_DWORD'),
call('HKEY_CURRENT_USER',
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings',
'ProxyOverride',
'<local>;.moo.com;.salt.com')
]
mock_reg.assert_has_calls(calls)
mock_cmd.assert_called_once_with('netsh winhttp import proxy source=ie')
self.assertTrue(out)

View file

@ -361,6 +361,7 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin):
ZyppCallMock(return_value=get_test_data('zypper-available.txt'))), \
patch('salt.modules.zypper.refresh_db', MagicMock(return_value=True)):
self.assertEqual(zypper.latest_version('vim'), '7.4.326-2.62')
self.assertDictEqual(zypper.latest_version('vim', 'fakepkg'), {'vim': '7.4.326-2.62', 'fakepkg': ''})
def test_upgrade_success(self):
'''

View file

@ -39,6 +39,7 @@ import salt.serializers.yaml as yamlserializer
import salt.serializers.json as jsonserializer
import salt.serializers.python as pythonserializer
from salt.exceptions import CommandExecutionError
import salt.utils.win_functions
@skipIf(NO_MOCK, NO_MOCK_REASON)
@ -60,6 +61,15 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
}
}
def tearDown(self):
remove_dir = '/tmp/etc'
if salt.utils.is_windows():
remove_dir = 'c:\\tmp\\etc'
try:
salt.utils.rm_rf(remove_dir)
except OSError:
pass
def test_serialize(self):
def returner(contents, *args, **kwargs):
returner.returned = contents
@ -145,10 +155,15 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
else:
group = 'saltstack'
ret = {'name': name,
'result': False,
'comment': '',
'changes': {}}
def return_val(kwargs):
val = {
'name': name,
'result': False,
'comment': '',
'changes': {},
}
val.update(kwargs)
return val
mock_t = MagicMock(return_value=True)
mock_f = MagicMock(return_value=False)
@ -162,7 +177,7 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t}):
comt = ('Must provide name to file.symlink')
ret.update({'comment': comt, 'name': ''})
ret = return_val({'comment': comt, 'name': ''})
self.assertDictEqual(filestate.symlink('', target), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
@ -170,8 +185,12 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
'file.group_to_gid': mock_empty,
'user.info': mock_empty,
'user.current': mock_user}):
comt = ('User {0} does not exist. Group {1} does not exist.'.format(user, group))
ret.update({'comment': comt, 'name': name})
if salt.utils.is_windows():
comt = ('User {0} does not exist'.format(user))
ret = return_val({'comment': comt, 'name': name})
else:
comt = ('User {0} does not exist. Group {1} does not exist.'.format(user, group))
ret = return_val({'comment': comt, 'name': name})
self.assertDictEqual(filestate.symlink(name, target, user=user,
group=group), ret)
@ -183,11 +202,22 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
'user.current': mock_user}):
with patch.dict(filestate.__opts__, {'test': True}):
with patch.object(os.path, 'exists', mock_f):
comt = ('Symlink {0} to {1}'
' is set for creation').format(name, target)
ret.update({'comment': comt,
'result': None,
'pchanges': {'new': name}})
if salt.utils.is_windows():
comt = ('User {0} does not exist'.format(user))
ret = return_val(
{
'comment': comt,
'result': False,
'name': name,
'changes': {}
}
)
else:
comt = ('Symlink {0} to {1}'
' is set for creation').format(name, target)
ret = return_val({'comment': comt,
'result': None,
'pchanges': {'new': name}})
self.assertDictEqual(filestate.symlink(name, target,
user=user,
group=group), ret)
@ -201,10 +231,21 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__opts__, {'test': False}):
with patch.object(os.path, 'isdir', mock_f):
with patch.object(os.path, 'exists', mock_f):
comt = ('Directory {0} for symlink is not present').format(test_dir)
ret.update({'comment': comt,
if salt.utils.is_windows():
comt = 'User {0} does not exist'.format(user)
ret = return_val(
{
'comment': comt,
'result': False,
'pchanges': {'new': name}})
'name': name,
'changes': {},
}
)
else:
comt = ('Directory {0} for symlink is not present').format(test_dir)
ret = return_val({'comment': comt,
'result': False,
'pchanges': {'new': name}})
self.assertDictEqual(filestate.symlink(name, target,
user=user,
group=group), ret)
@ -219,15 +260,19 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__opts__, {'test': False}):
with patch.object(os.path, 'isdir', mock_t):
with patch.object(salt.states.file, '_check_symlink_ownership', mock_t):
comt = ('Symlink {0} is present and owned by '
'{1}:{2}'.format(name, user, group))
ret.update({'comment': comt,
'result': True,
'pchanges': {}})
self.assertDictEqual(filestate.symlink(name, target,
user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
if salt.utils.is_windows():
comt = ('Symlink {0} is present and owned by '
'{1}'.format(name, user))
else:
comt = ('Symlink {0} is present and owned by '
'{1}:{2}'.format(name, user, group))
ret = return_val({'comment': comt,
'result': True,
'pchanges': {}})
self.assertDictEqual(filestate.symlink(name, target,
user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
'file.group_to_gid': mock_gid,
@ -239,15 +284,16 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.object(os.path, 'isdir', mock_t):
with patch.object(os.path, 'exists', mock_f):
with patch.object(os.path, 'lexists', mock_t):
comt = ('File exists where the backup target SALT'
' should go')
ret.update({'comment': comt,
'result': False,
'pchanges': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group, backupname='SALT'),
ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = ('File exists where the backup target SALT'
' should go')
ret = return_val({'comment': comt,
'result': False,
'pchanges': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group, backupname='SALT'),
ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -260,14 +306,15 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.object(os.path, 'isdir', mock_t):
with patch.object(os.path, 'exists', mock_f):
with patch.object(os.path, 'isfile', mock_t):
comt = ('File exists where the symlink {0} should be'
.format(name))
ret.update({'comment': comt,
'pchanges': {'new': name},
'result': False})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = ('File exists where the symlink {0} should be'
.format(name))
ret = return_val({'comment': comt,
'pchanges': {'new': name},
'result': False})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -281,11 +328,12 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])):
with patch.object(os.path, 'isfile', mock_t):
with patch.object(os.path, 'exists', mock_f):
comt = ('File exists where the symlink {0} should be'.format(name))
ret.update({'comment': comt, 'result': False})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = ('File exists where the symlink {0} should be'.format(name))
ret = return_val({'comment': comt, 'result': False, 'pchanges': {'new': '/tmp/testfile.txt'}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -299,11 +347,12 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])):
with patch.object(os.path, 'isdir', mock_t):
with patch.object(os.path, 'exists', mock_f):
comt = ('Directory exists where the symlink {0} should be'.format(name))
ret.update({'comment': comt, 'result': False})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = ('Directory exists where the symlink {0} should be'.format(name))
ret = return_val({'comment': comt, 'result': False, 'pchanges': {'new': '/tmp/testfile.txt'}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -316,12 +365,13 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__opts__, {'test': False}):
with patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])):
with patch.object(os.path, 'isfile', mock_f):
comt = ('Unable to create new symlink {0} -> '
'{1}: '.format(name, target))
ret.update({'comment': comt, 'result': False})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = ('Unable to create new symlink {0} -> '
'{1}: '.format(name, target))
ret = return_val({'comment': comt, 'result': False, 'pchanges': {'new': '/tmp/testfile.txt'}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -336,13 +386,15 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__opts__, {'test': False}):
with patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])):
with patch.object(os.path, 'isfile', mock_f):
comt = 'Created new symlink {0} -> {1}'.format(name, target)
ret.update({'comment': comt,
'result': True,
'changes': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.states.file._check_symlink_ownership', return_value=True):
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
comt = 'Created new symlink {0} -> {1}'.format(name, target)
ret = return_val({'comment': comt,
'result': True, 'pchanges': {'new': '/tmp/testfile.txt'},
'changes': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t,
'file.user_to_uid': mock_uid,
@ -357,15 +409,19 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
with patch.dict(filestate.__opts__, {'test': False}):
with patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])):
with patch.object(os.path, 'isfile', mock_f):
comt = ('Created new symlink {0} -> {1}, '
'but was unable to set ownership to '
'{2}:{3}'.format(name, target, user, group))
ret.update({'comment': comt,
'result': False,
'changes': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
with patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'):
with patch('salt.states.file._set_symlink_ownership', return_value=False):
with patch('salt.states.file._check_symlink_ownership', return_value=False):
comt = ('Created new symlink {0} -> {1}, '
'but was unable to set ownership to '
'{2}:{3}'.format(name, target, user, group))
ret = return_val({'comment': comt,
'result': False,
'pchanges': {'new': '/tmp/testfile.txt'},
'changes': {'new': name}})
self.assertDictEqual(filestate.symlink
(name, target, user=user,
group=group), ret)
# 'absent' function tests: 1
def test_absent(self):
@ -1127,7 +1183,10 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
'''
Test to ensure that some text appears at the beginning of a file.
'''
name = '/etc/motd'
name = '/tmp/etc/motd'
if salt.utils.is_windows():
name = 'c:\\tmp\\etc\\motd'
assert not os.path.exists(os.path.split(name)[0])
source = ['salt://motd/hr-messages.tmpl']
sources = ['salt://motd/devops-messages.tmpl']
text = ['Trust no one unless you have eaten much salt with him.']
@ -1156,12 +1215,15 @@ class TestFileState(TestCase, LoaderModuleMockMixin):
'cp.get_template': mock_f,
'file.search': mock_f,
'file.prepend': mock_t}):
with patch.object(os.path, 'isdir', mock_t):
comt = ('The following files will be changed:\n/etc:'
' directory - new\n')
ret.update({'comment': comt, 'name': name, 'pchanges': {'/etc': {'directory': 'new'}}})
self.assertDictEqual(filestate.prepend(name, makedirs=True),
ret)
comt = ('The following files will be changed:\n/tmp/etc:'
' directory - new\n')
pchanges = {'/tmp/etc': {'directory': 'new'}}
if salt.utils.is_windows():
comt = 'The directory "c:\\tmp\\etc" will be changed'
pchanges = {'c:\\tmp\\etc': {'directory': 'new'}}
ret.update({'comment': comt, 'name': name, 'pchanges': pchanges})
self.assertDictEqual(filestate.prepend(name, makedirs=True),
ret)
with patch.object(os.path, 'isabs', mock_f):
comt = ('Specified file {0} is not an absolute path'

View file

@ -125,6 +125,7 @@ class ServiceTestCase(TestCase, LoaderModuleMockMixin):
{'changes': 'saltstack',
'comment': 'The service salt is already dead', 'name': 'salt',
'result': True}]
info_mock = MagicMock(return_value={'StartType': ''})
mock = MagicMock(return_value="salt")
with patch.object(service, '_enabled_used_error', mock):
@ -140,31 +141,36 @@ class ServiceTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(service.__opts__, {'test': True}):
with patch.dict(service.__salt__, {'service.enabled': fmock,
'service.stop': tmock,
'service.status': fmock}):
'service.status': fmock,
'service.info': info_mock}):
with patch.object(service, '_enable', mock):
self.assertDictEqual(service.dead("salt", True), ret[5])
with patch.dict(service.__salt__, {'service.enabled': tmock,
'service.status': tmock}):
'service.status': tmock,
'service.info': info_mock}):
self.assertDictEqual(service.dead("salt"), ret[2])
with patch.dict(service.__opts__, {'test': False}):
with patch.dict(service.__salt__, {'service.enabled': fmock,
'service.stop': tmock,
'service.status': fmock}):
'service.status': fmock,
'service.info': info_mock}):
with patch.object(service, '_enable', mock):
self.assertDictEqual(service.dead("salt", True), ret[1])
with patch.dict(service.__salt__, {'service.enabled': MagicMock(side_effect=[True, True, False]),
'service.status': MagicMock(side_effect=[True, False, False]),
'service.stop': MagicMock(return_value="stack")}):
'service.stop': MagicMock(return_value="stack"),
'service.info': info_mock}):
with patch.object(service, '_enable', MagicMock(return_value={'changes': 'saltstack'})):
self.assertDictEqual(service.dead("salt", True), ret[3])
# test an initd which a wrong status (True even if dead)
with patch.dict(service.__salt__, {'service.enabled': MagicMock(side_effect=[False, False, False]),
'service.status': MagicMock(side_effect=[True, True, True]),
'service.stop': MagicMock(return_value="stack")}):
'service.stop': MagicMock(return_value="stack"),
'service.info': info_mock}):
with patch.object(service, '_disable', MagicMock(return_value={})):
self.assertDictEqual(service.dead("salt", False), ret[4])

23
tox.ini
View file

@ -1,10 +1,29 @@
[tox]
envlist = py27,py3
envlist = py27,py34,py35,py36,pylint-salt,pylint-tests
skip_missing_interpreters = True
skipsdist = True
[testenv]
deps = -r{toxinidir}/requirements/tests.txt
deps = -Ur{toxinidir}/requirements/tests.txt
commands = pytest --rootdir {toxinidir} {posargs}
passenv = LANG HOME
sitepackages = True
[testenv:pylint-salt]
basepython = python2.7
deps = -r{toxinidir}/requirements/dev.txt
commands =
pylint --version
pylint --rcfile=.testing.pylintrc --disable=I,W1307,C0411,C0413,W8410,str-format-in-logging {posargs:setup.py salt/}
sitepackages = False
[testenv:pylint-tests]
basepython = python2.7
deps = -r{toxinidir}/requirements/dev.txt
commands =
pylint --version
pylint --rcfile=.testing.pylintrc --disable=I,W0232,E1002,W1307,C0411,C0413,W8410,str-format-in-logging {posargs:tests/}
sitepackages = False
[pytest]
addopts = --log-file /tmp/salt-runtests.log --no-print-logs --ssh-tests -ra -sv