Merge branch '2017.7' into Issue47689-pip-state-performance

This commit is contained in:
Daniel Wallace 2018-07-20 07:50:04 -05:00 committed by GitHub
commit d6a49ae41c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
101 changed files with 4450 additions and 641 deletions

50
.ci/docs Normal file
View file

@ -0,0 +1,50 @@
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') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'Testing docs...',
status: 'PENDING',
context: "jenkins/pr/docs"
}
}
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 sphinx -e .'
}
}
stage('build') {
steps {
sh 'eval "$(pyenv init -)"; make -C doc clean html'
}
}
}
post {
always {
cleanWs()
}
success {
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'The docs job has passed',
status: 'SUCCESS',
context: "jenkins/pr/docs"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'The docs job has failed',
status: 'FAILURE',
context: "jenkins/pr/docs"
}
}
}

71
.ci/kitchen-centos7-py2 Normal file
View file

@ -0,0 +1,71 @@
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"
PATH = "/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin"
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py2"
TEST_PLATFORM = "centos-7"
PY_COLORS = 1
}
stages {
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
}
}
stage('run kitchen') {
steps {
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-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
}
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-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",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}

71
.ci/kitchen-centos7-py3 Normal file
View file

@ -0,0 +1,71 @@
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"
PATH = "/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin"
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py3"
TEST_PLATFORM = "centos-7"
PY_COLORS = 1
}
stages {
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
}
}
stage('run kitchen') {
steps {
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-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
}
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-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",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}

View file

@ -0,0 +1,71 @@
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"
PATH = "/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin"
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py2"
TEST_PLATFORM = "ubuntu-1604"
PY_COLORS = 1
}
stages {
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
}
}
stage('run kitchen') {
steps {
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-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
}
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-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",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}

View file

@ -0,0 +1,71 @@
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"
PATH = "/usr/local/rbenv/shims/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/bin"
RBENV_VERSION = "2.4.2"
TEST_SUITE = "py3"
TEST_PLATFORM = "ubuntu-1604"
PY_COLORS = 1
}
stages {
stage('github-pending') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "running ${TEST_SUITE}-${TEST_PLATFORM}...",
status: 'PENDING',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
stage('setup') {
steps {
sh 'bundle install --with ec2 windows --without opennebula docker'
}
}
stage('run kitchen') {
steps {
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-testing.pem'
sh 'bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM || bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM'
sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM'
}
}}
}
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-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",
status: 'SUCCESS',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
}
}
}

70
.ci/lint Normal file
View file

@ -0,0 +1,70 @@
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') {
steps {
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'Testing lint...',
status: 'PENDING',
context: "jenkins/pr/lint"
}
}
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 tox'
}
}
stage('linting') {
failFast false
parallel {
stage('salt linting') {
steps {
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 -)"; tox -e pylint-tests | tee pylint-report-tests.xml'
archiveArtifacts artifacts: 'pylint-report-tests.xml'
}
}
}
}
}
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',
status: 'SUCCESS',
context: "jenkins/pr/lint"
}
failure {
githubNotify credentialsId: 'test-jenkins-credentials',
description: 'The lint job has failed',
status: 'FAILURE',
context: "jenkins/pr/lint"
}
}
}

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

@ -250,8 +250,8 @@ on_saltstack = 'SALT_ON_SALTSTACK' in os.environ
project = 'Salt'
version = salt.version.__version__
latest_release = '2018.3.1' # latest release
previous_release = '2017.7.6' # latest release from previous branch
latest_release = '2018.3.2' # latest release
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

@ -148,22 +148,23 @@ Why aren't my custom modules/states/etc. available on my Minions?
-----------------------------------------------------------------
Custom modules are synced to Minions when
:mod:`saltutil.sync_modules <salt.modules.saltutil.sync_modules>`,
or :mod:`saltutil.sync_all <salt.modules.saltutil.sync_all>` is run.
Custom modules are also synced by :mod:`state.apply` when run without
any arguments.
:py:func:`saltutil.sync_modules <salt.modules.saltutil.sync_modules>`,
or :py:func:`saltutil.sync_all <salt.modules.saltutil.sync_all>` is run.
Similarly, custom states are synced to Minions when :py:func:`saltutil.sync_states
<salt.modules.saltutil.sync_states>`, or :py:func:`saltutil.sync_all
<salt.modules.saltutil.sync_all>` is run.
Similarly, custom states are synced to Minions
when :mod:`state.apply <salt.modules.state.apply_>`,
:mod:`saltutil.sync_states <salt.modules.saltutil.sync_states>`, or
:mod:`saltutil.sync_all <salt.modules.saltutil.sync_all>` is run.
They are both also synced when a :ref:`highstate <running-highstate>` is
triggered.
Custom states are also synced by :mod:`state.apply<salt.modules.state.apply_>`
when run without any arguments.
As of the Fluorine release, as well as 2017.7.7 and 2018.3.2 in their
respective release cycles, the ``sync`` argument to :py:func:`state.apply
<salt.modules.state.apply_>`/:py:func:`state.sls <salt.modules.state.sls>` can
be used to sync custom types when running individual SLS files.
Other custom types (renderers, outputters, etc.) have similar behavior, see the
documentation for the :mod:`saltutil <salt.modules.saltutil>` module for more
documentation for the :py:func:`saltutil <salt.modules.saltutil>` module for more
information.
:ref:`This reactor example <minion-start-reactor>` can be used to automatically

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

@ -1162,6 +1162,40 @@ The password used for HTTP proxy access.
proxy_password: obolus
Docker Configuration
====================
.. conf_minion:: docker.update_mine
``docker.update_mine``
----------------------
.. versionadded:: 2017.7.8,2018.3.3
.. versionchanged:: Fluorine
The default value is now ``False``
Default: ``True``
If enabled, when containers are added, removed, stopped, started, etc., the
:ref:`mine <salt-mine>` will be updated with the results of :py:func:`docker.ps
verbose=True all=True host=True <salt.modules.dockermod.ps>`. This mine data is
used by :py:func:`mine.get_docker <salt.modules.mine.get_docker>`. Set this
option to ``False`` to keep Salt from updating the mine with this information.
.. note::
This option can also be set in Grains or Pillar data, with Grains
overriding Pillar and the minion config file overriding Grains.
.. note::
Disabling this will of course keep :py:func:`mine.get_docker
<salt.modules.mine.get_docker>` from returning any information for a given
minion.
.. code-block:: yaml
docker.update_mine: False
Minion Module Management
========================

View file

@ -1,27 +1,67 @@
========================================
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 single fix for Issue `#48038`_, which
is a critical bug that occurs in a multi-syndic setup where the same job is run
multiple times on a minion.
The ``2017.7.7`` release contains only a small number of fixes, which are detailed
below.
This release fixes two critical issues.
The first is Issue `#48038`_, which is a critical bug that occurs in a multi-syndic
setup where the same job is run multiple times on a minion.
The second issue is `#48130`_. This bug appears in certain setups where the Master
reports a Minion time-out, even though the job is still running on the Minion.
Both of these issues have been fixed with this release.
Statistics
==========
- Total Merges: **1**
- Total Issue References: **1**
- Total PR References: **2**
- Total Merges: **5**
- Total Issue References: **2**
- Total PR References: **6**
- Contributors: **2** (`garethgreenaway`_, `rallytime`_)
- Contributors: **3** (`garethgreenaway`_, `gtmanfred`_, `rallytime`_)
Changelog for v2017.7.6..v2017.7.7
==================================
*Generated at: 2018-06-14 15:43:34 UTC*
*Generated at: 2018-06-17 19:26:52 UTC*
* **ISSUE** `#48130`_: (`rmarchei`_) Minion timeouts with 2018.3.1 (refs: `#48157`_)
* **PR** `#48157`_: (`gtmanfred`_) always listen when gathering job info
@ *2018-06-17 19:04:09 UTC*
* 8af4452134 Merge pull request `#48157`_ from gtmanfred/2017.7.7
* d8209e8a40 always listen when gathering job info
* **PR** `#48140`_: (`rallytime`_) Update man pages for 2017.7.7
@ *2018-06-14 21:22:43 UTC*
* b98c52ee51 Merge pull request `#48140`_ from rallytime/man-pages-2017.7.7
* 8893bf0d4c Update man pages for 2017.7.7
* **PR** `#48136`_: (`gtmanfred`_) [2017.7.7] bootstrap kitchen branch tests with 2017.7.6
@ *2018-06-14 21:20:16 UTC*
* baa0363336 Merge pull request `#48136`_ from gtmanfred/2017.7.7
* fce1c31146 bootstrap kitchen branch tests with 2017.7.6
* **PR** `#48134`_: (`rallytime`_) Add release notes file for 2017.7.7
@ *2018-06-14 16:31:34 UTC*
* b0ba08f4d9 Merge pull request `#48134`_ from rallytime/release-notes-2017.7.7
* 217005b8f1 Add missing `v` for tag reference
* d53569d1e3 Add release notes file for 2017.7.7
* **ISSUE** `#48038`_: (`austinpapp`_) jobs are not dedup'ing minion side (refs: `#48075`_)
@ -37,6 +77,13 @@ Changelog for v2017.7.6..v2017.7.7
.. _`#48038`: https://github.com/saltstack/salt/issues/48038
.. _`#48075`: https://github.com/saltstack/salt/pull/48075
.. _`#48098`: https://github.com/saltstack/salt/pull/48098
.. _`#48130`: https://github.com/saltstack/salt/issues/48130
.. _`#48134`: https://github.com/saltstack/salt/pull/48134
.. _`#48136`: https://github.com/saltstack/salt/pull/48136
.. _`#48140`: https://github.com/saltstack/salt/pull/48140
.. _`#48157`: https://github.com/saltstack/salt/pull/48157
.. _`austinpapp`: https://github.com/austinpapp
.. _`garethgreenaway`: https://github.com/garethgreenaway
.. _`gtmanfred`: https://github.com/gtmanfred
.. _`rallytime`: https://github.com/rallytime
.. _`rmarchei`: https://github.com/rmarchei

View file

@ -16,3 +16,16 @@ New win_snmp behavior
- :py:func:`win_snmp.set_community_names
<salt.modules.win_snmp.set_community_names>` now raises an error when SNMP
settings are being managed by GroupPolicy.
Option Added to Disable Docker Mine Updates
===========================================
When a docker container is added, removed, started, stopped, etc., the results
of a :py:func:`docker.ps verbose=True all=True host=True
<salt.modules.dockermod.ps>` are sent to the :ref:`mine <salt-mine>`, to be
used by :py:func:`mine.get_docker <salt.modules.mine.get_docker>`.
A new config option (:conf_minion:`docker.update_mine`) has been added. When
set to ``False``, Salt will not send this information to the mine. This is
useful in cases where sensitive information is stored in the container's
environment.

View file

@ -330,7 +330,13 @@ Nested pillar values can also be set via the command line:
.. code-block:: bash
salt '*' state.sls my_sls_file pillar='{"foo": {"bar": "baz"}}'
salt '*' state.sls my_sls_file pillar='{"foo": {"bar": "baz"}}'
Lists can be passed via command line pillar data as follows:
.. code-block:: bash
salt '*' state.sls my_sls_file pillar='{"some_list": ["foo", "bar", "baz"]}'
.. note::

View file

@ -135,13 +135,18 @@ where it is necessary to invoke the same function from a custom :ref:`outputter
<all-salt.output>`/returner, as well as an execution module.
Utility modules placed in ``salt://_utils/`` will be synced to the minions when
any of the following Salt functions are called:
a :ref:`highstate <running-highstate>` is run, as well as when any of the
following Salt functions are called:
* :mod:`state.apply <salt.modules.state.apply_>`
* :mod:`saltutil.sync_utils <salt.modules.saltutil.sync_utils>`
* :mod:`saltutil.sync_all <salt.modules.saltutil.sync_all>`
* :py:func:`saltutil.sync_utils <salt.modules.saltutil.sync_utils>`
* :py:func:`saltutil.sync_all <salt.modules.saltutil.sync_all>`
As of the Fluorine release, as well as 2017.7.7 and 2018.3.2 in their
respective release cycles, the ``sync`` argument to :py:func:`state.apply
<salt.modules.state.apply_>`/:py:func:`state.sls <salt.modules.state.sls>` can
be used to sync custom types when running individual SLS files.
To sync to the Master, use either of the following:
* :mod:`saltutil.sync_utils <salt.runners.saltutil.sync_utils>`
* :mod:`saltutil.sync_all <salt.runners.saltutil.sync_all>`
* :py:func:`saltutil.sync_utils <salt.runners.saltutil.sync_utils>`
* :py:func:`saltutil.sync_all <salt.runners.saltutil.sync_all>`

View file

@ -197,17 +197,17 @@ Write-Output " ----------------------------------------------------------------"
Write-Output " - $script_name :: Updating PIP and SetupTools . . ."
Write-Output " ----------------------------------------------------------------"
if ( ! [bool]$Env:SALT_PIP_LOCAL_CACHE) {
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip --no-cache-dir install -r $($script_path)\req_pip.txt" "python pip"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip --disable-pip-version-check --no-cache-dir install -r $($script_path)\req_pip.txt" "python pip"
} else {
$p = New-Item $Env:SALT_PIP_LOCAL_CACHE -ItemType Directory -Force # Ensure directory exists
if ( (Get-ChildItem $Env:SALT_PIP_LOCAL_CACHE | Measure-Object).Count -eq 0 ) {
# folder empty
Write-Output " pip download from req_pip.txt into empty local cache SALT_REQ_PIP $Env:SALT_PIP_LOCAL_CACHE"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip download --dest $Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip download"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip --disable-pip-version-check download --dest $Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip download"
}
Write-Output " reading from local pip cache $Env:SALT_PIP_LOCAL_CACHE"
Write-Output " If a (new) resource is missing, please delete all files in this cache, go online and repeat"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip install --no-index --find-links=$Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip install"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip --disable-pip-version-check install --no-index --find-links=$Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip install"
}
#==============================================================================
@ -218,16 +218,16 @@ Write-Output " ----------------------------------------------------------------"
Write-Output " - $script_name :: Installing pypi resources using pip . . ."
Write-Output " ----------------------------------------------------------------"
if ( ! [bool]$Env:SALT_REQ_LOCAL_CACHE) {
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip --no-cache-dir install -r $($script_path)\req.txt" "pip install"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip --disable-pip-version-check --no-cache-dir install -r $($script_path)\req.txt" "pip install"
} else {
if ( (Get-ChildItem $Env:SALT_REQ_LOCAL_CACHE | Measure-Object).Count -eq 0 ) {
# folder empty
Write-Output " pip download from req.txt into empty local cache SALT_REQ $Env:SALT_REQ_LOCAL_CACHE"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip download --dest $Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip download"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip --disable-pip-version-check download --dest $Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip download"
}
Write-Output " reading from local pip cache $Env:SALT_REQ_LOCAL_CACHE"
Write-Output " If a (new) resource is missing, please delete all files in this cache, go online and repeat"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip install --no-index --find-links=$Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip install"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python2Dir'])\python.exe -m pip --disable-pip-version-check install --no-index --find-links=$Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip install"
}
#==============================================================================

View file

@ -197,17 +197,17 @@ Write-Output " ----------------------------------------------------------------"
Write-Output " - $script_name :: Updating PIP and SetupTools . . ."
Write-Output " ----------------------------------------------------------------"
if ( ! [bool]$Env:SALT_PIP_LOCAL_CACHE) {
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip --no-cache-dir install -r $($script_path)\req_pip.txt" "python pip"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip --disable-pip-version-check --no-cache-dir install -r $($script_path)\req_pip.txt" "python pip"
} else {
$p = New-Item $Env:SALT_PIP_LOCAL_CACHE -ItemType Directory -Force # Ensure directory exists
if ( (Get-ChildItem $Env:SALT_PIP_LOCAL_CACHE | Measure-Object).Count -eq 0 ) {
# folder empty
Write-Output " pip download from req_pip.txt into empty local cache SALT_REQ_PIP $Env:SALT_PIP_LOCAL_CACHE"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip download --dest $Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip download"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip --disable-pip-version-check download --dest $Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip download"
}
Write-Output " reading from local pip cache $Env:SALT_PIP_LOCAL_CACHE"
Write-Output " If a (new) resource is missing, please delete all files in this cache, go online and repeat"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip install --no-index --find-links=$Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip install"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip --disable-pip-version-check install --no-index --find-links=$Env:SALT_PIP_LOCAL_CACHE -r $($script_path)\req_pip.txt" "pip install"
}
#==============================================================================
@ -218,16 +218,16 @@ Write-Output " ----------------------------------------------------------------"
Write-Output " - $script_name :: Installing pypi resources using pip . . ."
Write-Output " ----------------------------------------------------------------"
if ( ! [bool]$Env:SALT_REQ_LOCAL_CACHE) {
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip --no-cache-dir install -r $($script_path)\req.txt" "pip install"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip --disable-pip-version-check --no-cache-dir install -r $($script_path)\req.txt" "pip install"
} else {
if ( (Get-ChildItem $Env:SALT_REQ_LOCAL_CACHE | Measure-Object).Count -eq 0 ) {
# folder empty
Write-Output " pip download from req.txt into empty local cache SALT_REQ $Env:SALT_REQ_LOCAL_CACHE"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip download --dest $Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip download"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip --disable-pip-version-check download --dest $Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip download"
}
Write-Output " reading from local pip cache $Env:SALT_REQ_LOCAL_CACHE"
Write-Output " If a (new) resource is missing, please delete all files in this cache, go online and repeat"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip install --no-index --find-links=$Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip install"
Start_Process_and_test_exitcode "cmd" "/c $($ini['Settings']['Python3Dir'])\python.exe -m pip --disable-pip-version-check install --no-index --find-links=$Env:SALT_REQ_LOCAL_CACHE -r $($script_path)\req.txt" "pip install"
}
#==============================================================================

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

@ -1106,7 +1106,10 @@ ARGS = {10}\n'''.format(self.minion_config,
shim_tmp_file.write(salt.utils.to_bytes(cmd_str))
# Copy shim to target system, under $HOME/.<randomized name>
target_shim_file = '.{0}.{1}'.format(binascii.hexlify(os.urandom(6)), extension)
target_shim_file = '.{0}.{1}'.format(
binascii.hexlify(os.urandom(6)).decode('ascii'),
extension
)
if self.winrm:
target_shim_file = saltwinshell.get_target_shim_file(self, target_shim_file)
self.shell.send(shim_tmp_file.name, target_shim_file, makedirs=True)

View file

@ -32,6 +32,60 @@ __func_alias__ = {
log = logging.getLogger(__name__)
def _ssh_state(chunks, __opts__, __context__, __pillar__, __salt__, st_kwargs,
kwargs, test=False):
'''
funciton 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(
__opts__,
__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
@ -835,6 +889,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 +902,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 +917,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 +929,26 @@ 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,
__opts__,
__context__,
__pillar__,
__salt__,
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

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

@ -1032,10 +1032,11 @@ class Minion(MinionBase):
# I made the following 3 line oddity to preserve traceback.
# Please read PR #23978 before changing, hopefully avoiding regressions.
# Good luck, we're all counting on you. Thanks.
future_exception = self._connect_master_future.exception()
if future_exception:
# This needs to be re-raised to preserve restart_on_error behavior.
raise six.reraise(*future_exception)
if self._connect_master_future.done():
future_exception = self._connect_master_future.exception()
if future_exception:
# This needs to be re-raised to preserve restart_on_error behavior.
raise six.reraise(*future_exception)
if timeout and self._sync_connect_master_success is False:
raise SaltDaemonNotRunning('Failed to connect to the salt-master')
@ -1527,7 +1528,9 @@ class Minion(MinionBase):
)
ret['out'] = 'nested'
except TypeError as exc:
msg = 'Passed invalid arguments to {0}: {1}\n{2}'.format(function_name, exc, func.__doc__, )
msg = 'Passed invalid arguments to {0}: {1}\n{2}'.format(
function_name, exc, func.__doc__ or ''
)
log.warning(msg, exc_info_on_loglevel=logging.DEBUG)
ret['return'] = msg
ret['out'] = 'nested'

View file

@ -304,7 +304,7 @@ def install(name=None,
# We don't support installing specific version for now
# so transform the dict in list ignoring version provided
pkgs = [
p.keys()[0] for p in pkgs
next(iter(p)) for p in pkgs
if isinstance(p, dict)
]
pkg_to_install.extend(pkgs)

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

@ -436,11 +436,20 @@ def _refresh_mine_cache(wrapped):
refresh salt mine on exit.
'''
returned = wrapped(*args, **salt.utils.clean_kwargs(**kwargs))
__salt__['mine.send']('docker.ps', verbose=True, all=True, host=True)
if _check_update_mine():
__salt__['mine.send']('docker.ps', verbose=True, all=True, host=True)
return returned
return wrapper
def _check_update_mine():
try:
ret = __context__['docker.update_mine']
except KeyError:
ret = __context__['docker.update_mine'] = __salt__['config.get']('docker.update_mine', default=True)
return ret
# Helper functions
def _change_state(name, action, expected, *args, **kwargs):
'''

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

@ -870,7 +870,7 @@ def _network_conf(conf_tuples=None, **kwargs):
# on old versions of lxc, still support the gateway auto mode
# if we didn't explicitly say no to
# (lxc.network.ipv4.gateway: auto)
if _LooseVersion(version()) <= '1.0.7' and \
if _LooseVersion(version()) <= _LooseVersion('1.0.7') and \
True not in ['lxc.network.ipv4.gateway' in a for a in ret] and \
True in ['lxc.network.ipv4' in a for a in ret]:
ret.append({'lxc.network.ipv4.gateway': 'auto'})
@ -2079,7 +2079,7 @@ def clone(name,
if backing in ('dir', 'overlayfs', 'btrfs'):
size = None
# LXC commands and options changed in 2.0 - CF issue #34086 for details
if version() >= _LooseVersion('2.0'):
if _LooseVersion(version()) >= _LooseVersion('2.0'):
# https://linuxcontainers.org/lxc/manpages//man1/lxc-copy.1.html
cmd = 'lxc-copy'
cmd += ' {0} -n {1} -N {2}'.format(snapshot, orig, name)
@ -2270,22 +2270,22 @@ def _change_state(cmd,
# as te command itself mess with double forks; we must not
# communicate with it, but just wait for the exit status
pkwargs = {'python_shell': False,
'redirect_stderr': True,
'with_communicate': with_communicate,
'use_vt': use_vt,
'stdin': stdin,
'stdout': stdout,
'stderr': stderr}
'stdout': stdout}
for i in [a for a in pkwargs]:
val = pkwargs[i]
if val is _marker:
pkwargs.pop(i, None)
error = __salt__['cmd.run_stderr'](cmd, **pkwargs)
_cmdout = __salt__['cmd.run_all'](cmd, **pkwargs)
if error:
if _cmdout['retcode'] != 0:
raise CommandExecutionError(
'Error changing state for container \'{0}\' using command '
'\'{1}\': {2}'.format(name, cmd, error)
'\'{1}\': {2}'.format(name, cmd, _cmdout['stdout'])
)
if expected is not None:
# some commands do not wait, so we will
@ -3507,7 +3507,9 @@ def bootstrap(name,
configdir = '/var/tmp/.c_{0}'.format(rstr)
cmd = 'install -m 0700 -d {0}'.format(configdir)
if run(name, cmd, python_shell=False):
if run_all(
name, cmd, path=path, python_shell=False
)['retcode'] != 0:
log.error('tmpdir {0} creation failed ({1}'
.format(configdir, cmd))
return False
@ -3518,6 +3520,7 @@ def bootstrap(name,
copy_to(name, bs_, script, path=path)
result = run_all(name,
'sh -c "chmod +x {0}"'.format(script),
path=path,
python_shell=True)
copy_to(name, cfg_files['config'],
@ -3544,6 +3547,7 @@ def bootstrap(name,
run_all(name,
'sh -c \'if [ -f "{0}" ];then rm -f "{0}";fi\''
''.format(script),
path=path,
ignore_retcode=True,
python_shell=True)
else:

View file

@ -374,10 +374,18 @@ def flush():
def get_docker(interfaces=None, cidrs=None, with_container_id=False):
'''
Get all mine data for 'docker.get_containers' and run an aggregation
routine. The "interfaces" parameter allows for specifying which network
interfaces to select ip addresses from. The "cidrs" parameter allows for
specifying a list of cidrs which the ip address must match.
.. versionchanged:: 2017.7.8,2018.3.3
When :conf_minion:`docker.update_mine` is set to ``False`` for a given
minion, no mine data will be populated for that minion, and thus none
will be returned for it.
.. versionchanged:: Fluorine
:conf_minion:`docker.update_mine` now defaults to ``False``
Get all mine data for :py:func:`docker.ps <salt.modules.dockermod.ps_>` and
run an aggregation routine. The ``interfaces`` parameter allows for
specifying the network interfaces from which to select IP addresses. The
``cidrs`` parameter allows for specifying a list of subnets which the IP
address must match.
with_container_id
Boolean, to expose container_id in the list of results

View file

@ -168,20 +168,7 @@ def _get_pip_bin(bin_env):
# If the python binary was passed, return it
if 'python' in os.path.basename(bin_env):
return [os.path.normpath(bin_env), '-m', 'pip']
# Try to find the python binary based on the location of pip in a
# virtual environment, should be relative
if 'pip' in os.path.basename(bin_env):
# Look in the same directory as the pip binary, and also its
# parent directories.
pip_dirname = os.path.dirname(bin_env)
pip_parent_dir = os.path.dirname(pip_dirname)
for bin_path in _search_paths(pip_dirname, pip_parent_dir):
if os.path.isfile(bin_path):
logger.debug('pip: Found python binary: %s', bin_path)
return [os.path.normpath(bin_path), '-m', 'pip']
# Couldn't find python, use the passed pip binary
# This has the limitation of being unable to update pip itself
# We have been passed a pip binary, use the pip binary.
return [os.path.normpath(bin_env)]
raise CommandExecutionError(
@ -457,6 +444,13 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914
``/usr/bin/pip-2.7`` or ``/usr/bin/pip-2.6``. If a directory path is
specified, it is assumed to be a virtualenv.
.. note::
For Windows, if the pip module is being used to upgrade the pip
package, bin_env should be the path to the virtualenv or to the
python binary that should be used. The pip command is unable to
upgrade itself in Windows.
use_wheel
Prefer wheel archives (requires pip>=1.4)

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

@ -223,6 +223,7 @@ def list_users(runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'list_users', '-q'],
reset_system_locale=False,
runas=runas,
python_shell=False)
@ -246,6 +247,7 @@ def list_vhosts(runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'list_vhosts', '-q'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -320,6 +322,7 @@ def add_user(name, password=None, runas=None):
res = __salt__['cmd.run_all'](
cmd,
reset_system_locale=False,
output_loglevel='quiet',
runas=runas,
python_shell=python_shell)
@ -352,6 +355,7 @@ def delete_user(name, runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'delete_user', name],
reset_system_locale=False,
python_shell=False,
runas=runas)
msg = 'Deleted'
@ -387,6 +391,7 @@ def change_password(name, password, runas=None):
cmd = [RABBITMQCTL, 'change_password', name, password]
res = __salt__['cmd.run_all'](
cmd,
reset_system_locale=False,
runas=runas,
output_loglevel='quiet',
python_shell=python_shell)
@ -409,6 +414,7 @@ def clear_password(name, runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'clear_password', name],
reset_system_locale=False,
runas=runas,
python_shell=False)
msg = 'Password Cleared'
@ -434,7 +440,7 @@ def check_password(name, password, runas=None):
runas = salt.utils.get_user()
try:
res = __salt__['cmd.run']([RABBITMQCTL, 'status'], runas=runas, python_shell=False)
res = __salt__['cmd.run']([RABBITMQCTL, 'status'], reset_system_locale=False, runas=runas, python_shell=False)
server_version = re.search(r'\{rabbit,"RabbitMQ","(.+)"\}', res)
if server_version is None:
@ -466,6 +472,7 @@ def check_password(name, password, runas=None):
res = __salt__['cmd.run_all'](
cmd,
reset_system_locale=False,
runas=runas,
output_loglevel='quiet',
python_shell=python_shell)
@ -481,6 +488,7 @@ def check_password(name, password, runas=None):
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'eval', cmd],
reset_system_locale=False,
runas=runas,
output_loglevel='quiet',
python_shell=False)
@ -509,6 +517,7 @@ def add_vhost(vhost, runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'add_vhost', vhost],
reset_system_locale=False,
runas=runas,
python_shell=False)
@ -530,6 +539,7 @@ def delete_vhost(vhost, runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'delete_vhost', vhost],
reset_system_locale=False,
runas=runas,
python_shell=False)
msg = 'Deleted'
@ -551,6 +561,7 @@ def set_permissions(vhost, user, conf='.*', write='.*', read='.*', runas=None):
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'set_permissions', '-p',
vhost, user, conf, write, read],
reset_system_locale=False,
runas=runas,
python_shell=False)
msg = 'Permissions Set'
@ -571,6 +582,7 @@ def list_permissions(vhost, runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'list_permissions', '-q', '-p', vhost],
reset_system_locale=False,
runas=runas,
python_shell=False)
@ -591,6 +603,7 @@ def list_user_permissions(name, runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'list_user_permissions', name, '-q'],
reset_system_locale=False,
runas=runas,
python_shell=False)
@ -614,6 +627,7 @@ def set_user_tags(name, tags, runas=None):
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'set_user_tags', name] + list(tags),
reset_system_locale=False,
runas=runas,
python_shell=False)
msg = "Tag(s) set"
@ -634,6 +648,7 @@ def status(runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'status'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -654,6 +669,7 @@ def cluster_status(runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'cluster_status'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -678,7 +694,7 @@ def join_cluster(host, user='rabbit', ram_node=None, runas=None):
if runas is None and not salt.utils.is_windows():
runas = salt.utils.get_user()
stop_app(runas)
res = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
res = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
start_app(runas)
return _format_response(res, 'Join')
@ -698,6 +714,7 @@ def stop_app(runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'stop_app'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -718,6 +735,7 @@ def start_app(runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'start_app'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -738,6 +756,7 @@ def reset(runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'reset'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -758,6 +777,7 @@ def force_reset(runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'force_reset'],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -778,7 +798,7 @@ def list_queues(runas=None, *args):
runas = salt.utils.get_user()
cmd = [RABBITMQCTL, 'list_queues', '-q']
cmd.extend(args)
res = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
res = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
_check_response(res)
return _output_to_dict(res['stdout'])
@ -800,7 +820,7 @@ def list_queues_vhost(vhost, runas=None, *args):
runas = salt.utils.get_user()
cmd = [RABBITMQCTL, 'list_queues', '-q', '-p', vhost]
cmd.extend(args)
res = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
res = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
_check_response(res)
return _output_to_dict(res['stdout'])
@ -823,6 +843,7 @@ def list_policies(vhost="/", runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'list_policies', '-q', '-p', vhost],
reset_system_locale=False,
runas=runas,
python_shell=False)
_check_response(res)
@ -873,8 +894,8 @@ def set_policy(vhost, name, pattern, definition, priority=None, runas=None):
if priority:
cmd.extend(['--priority', priority])
cmd.extend([name, pattern, definition])
res = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
log.debug('Set policy: {0}'.format(res['stdout']))
res = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
log.debug('Set policy: %s', res['stdout'])
return _format_response(res, 'Set')
@ -894,6 +915,7 @@ def delete_policy(vhost, name, runas=None):
runas = salt.utils.get_user()
res = __salt__['cmd.run_all'](
[RABBITMQCTL, 'clear_policy', '-p', vhost, name],
reset_system_locale=False,
runas=runas,
python_shell=False)
log.debug('Delete policy: {0}'.format(res['stdout']))
@ -931,7 +953,7 @@ def list_available_plugins(runas=None):
if runas is None and not salt.utils.is_windows():
runas = salt.utils.get_user()
cmd = [_get_rabbitmq_plugin(), 'list', '-m']
ret = __salt__['cmd.run_all'](cmd, python_shell=False, runas=runas)
ret = __salt__['cmd.run_all'](cmd, reset_system_locale=False, python_shell=False, runas=runas)
_check_response(ret)
return _output_to_list(ret['stdout'])
@ -949,7 +971,7 @@ def list_enabled_plugins(runas=None):
if runas is None and not salt.utils.is_windows():
runas = salt.utils.get_user()
cmd = [_get_rabbitmq_plugin(), 'list', '-m', '-e']
ret = __salt__['cmd.run_all'](cmd, python_shell=False, runas=runas)
ret = __salt__['cmd.run_all'](cmd, reset_system_locale=False, python_shell=False, runas=runas)
_check_response(ret)
return _output_to_list(ret['stdout'])
@ -982,7 +1004,7 @@ def enable_plugin(name, runas=None):
if runas is None and not salt.utils.is_windows():
runas = salt.utils.get_user()
cmd = [_get_rabbitmq_plugin(), 'enable', name]
ret = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
ret = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
return _format_response(ret, 'Enabled')
@ -999,5 +1021,5 @@ def disable_plugin(name, runas=None):
if runas is None and not salt.utils.is_windows():
runas = salt.utils.get_user()
cmd = [_get_rabbitmq_plugin(), 'disable', name]
ret = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False)
ret = __salt__['cmd.run_all'](cmd, reset_system_locale=False, runas=runas, python_shell=False)
return _format_response(ret, 'Disabled')

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

@ -458,8 +458,7 @@ def template_str(tem, queue=False, **kwargs):
return ret
def apply_(mods=None,
**kwargs):
def apply_(mods=None, **kwargs):
'''
.. versionadded:: 2015.5.0
@ -599,6 +598,22 @@ def apply_(mods=None,
.. code-block:: bash
salt '*' state.apply test localconfig=/path/to/minion.yml
sync_mods
If specified, the desired custom module types will be synced prior to
running the SLS files:
.. code-block:: bash
salt '*' state.apply test sync_mods=states,modules
salt '*' state.apply test sync_mods=all
.. note::
This option is ignored when no SLS files are specified, as a
:ref:`highstate <running-highstate>` automatically syncs all custom
module types.
.. versionadded:: 2017.7.8,2018.3.3,Fluorine
'''
if mods:
return sls(mods, **kwargs)
@ -928,7 +943,7 @@ def highstate(test=None, queue=False, **kwargs):
return ret
def sls(mods, test=None, exclude=None, queue=False, **kwargs):
def sls(mods, test=None, exclude=None, queue=False, sync_mods=None, **kwargs):
'''
Execute the states in one or more SLS files
@ -1019,6 +1034,17 @@ def sls(mods, test=None, exclude=None, queue=False, **kwargs):
.. versionadded:: 2015.8.4
sync_mods
If specified, the desired custom module types will be synced prior to
running the SLS files:
.. code-block:: bash
salt '*' state.sls test sync_mods=states,modules
salt '*' state.sls test sync_mods=all
.. versionadded:: 2017.7.8,2018.3.3,Fluorine
CLI Example:
.. code-block:: bash
@ -1087,6 +1113,28 @@ def sls(mods, test=None, exclude=None, queue=False, **kwargs):
'{0}.cache.p'.format(kwargs.get('cache_name', 'highstate'))
)
if sync_mods is True:
sync_mods = ['all']
if sync_mods is not None:
sync_mods = salt.utils.split_input(sync_mods)
else:
sync_mods = []
if 'all' in sync_mods and sync_mods != ['all']:
# Prevent unnecessary extra syncing
sync_mods = ['all']
for module_type in sync_mods:
try:
__salt__['saltutil.sync_{0}'.format(module_type)](
saltenv=opts['environment']
)
except KeyError:
log.warning(
'Invalid custom module type \'%s\', ignoring',
module_type
)
try:
st_ = salt.state.HighState(opts,
pillar_override,

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

@ -942,7 +942,6 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals
rev_item['not_after'] = rev_cert['Not After']
serial_number = rev_item['serial_number'].replace(':', '')
serial_number = str(int(serial_number, 16))
if 'not_after' in rev_item and not include_expired:
not_after = datetime.datetime.strptime(

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()
@ -923,9 +923,11 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
# Generate jid before triggering a job to subscribe all returns from minions
chunk['jid'] = salt.utils.jid.gen_jid()
# Subscribe returns from minions before firing a job
minions = set(self.ckminions.check_minions(chunk['tgt'], chunk.get('tgt_type', 'glob')))
future_minion_map = self.subscribe_minion_returns(chunk['jid'], minions)
# start listening for the event before we fire the job to avoid races
events = [
self.application.event_listener.get_event(self, tag='salt/job/'+chunk['jid']),
self.application.event_listener.get_event(self, tag='syndic/job/'+chunk['jid']),
]
f_call = self._format_call_run_job_async(chunk)
# fire a job off
@ -937,88 +939,92 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
# if the job didn't publish, lets not wait around for nothing
# TODO: set header??
if 'jid' not in pub_data:
for future in future_minion_map:
for future in events:
try:
future.set_result(None)
except Exception:
pass
raise tornado.gen.Return('No minions matched the target. No command was sent, no jid was assigned.')
# Map of minion_id -> returned for all minions we think we need to wait on
minions = {m: False for m in pub_data['minions']}
# minimum time required for return to complete. By default no waiting, if
# we are a syndic then we must wait syndic_wait at a minimum
min_wait_time = Future()
min_wait_time.set_result(True)
# wait syndic a while to avoid missing published events
if self.application.opts['order_masters']:
yield tornado.gen.sleep(self.application.opts['syndic_wait'])
min_wait_time = tornado.gen.sleep(self.application.opts['syndic_wait'])
# To ensure job_not_running and all_return are terminated by each other, communicate using a future
is_finished = Future()
is_finished = tornado.gen.sleep(self.application.opts['gather_job_timeout'])
job_not_running_future = self.job_not_running(pub_data['jid'],
# ping until the job is not running, while doing so, if we see new minions returning
# that they are running the job, add them to the list
tornado.ioloop.IOLoop.current().spawn_callback(self.job_not_running, pub_data['jid'],
chunk['tgt'],
f_call['kwargs']['tgt_type'],
minions,
is_finished)
minion_returns_future = self.sanitize_minion_returns(future_minion_map, pub_data['minions'], is_finished)
yield job_not_running_future
raise tornado.gen.Return((yield minion_returns_future))
def subscribe_minion_returns(self, jid, minions):
# Subscribe each minion event
future_minion_map = {}
for minion in minions:
tag = tagify([jid, 'ret', minion], 'job')
minion_future = self.application.event_listener.get_event(self,
tag=tag,
matcher=EventListener.exact_matcher)
future_minion_map[minion_future] = minion
return future_minion_map
@tornado.gen.coroutine
def sanitize_minion_returns(self, future_minion_map, minions, is_finished):
'''
Return a future which will complete once all returns are completed
(according to minions), or one of the passed in "finish_chunk_ret_future" completes
'''
if minions is None:
minions = []
# Remove redundant minions
redundant_minion_futures = [future for future in future_minion_map.keys() if future_minion_map[future] not in minions]
for redundant_minion_future in redundant_minion_futures:
try:
redundant_minion_future.set_result(None)
except Exception:
pass
del future_minion_map[redundant_minion_future]
def more_todo():
'''Check if there are any more minions we are waiting on returns from
'''
return any(x is False for x in six.itervalues(minions))
# here we want to follow the behavior of LocalClient.get_iter_returns
# namely we want to wait at least syndic_wait (assuming we are a syndic)
# and that there are no more jobs running on minions. We are allowed to exit
# early if gather_job_timeout has been exceeded
chunk_ret = {}
while True:
f = yield Any(list(future_minion_map.keys()) + [is_finished])
to_wait = events+[is_finished]
if not min_wait_time.done():
to_wait += [min_wait_time]
def cancel_inflight_futures():
for event in to_wait:
if not event.done():
event.set_result(None)
f = yield Any(to_wait)
try:
# When finished entire routine, cleanup other futures and return result
if f is is_finished:
for event in future_minion_map.keys():
if not event.done():
event.set_result(None)
cancel_inflight_futures()
raise tornado.gen.Return(chunk_ret)
elif f is min_wait_time:
if not more_todo():
cancel_inflight_futures()
raise tornado.gen.Return(chunk_ret)
continue
f_result = f.result()
chunk_ret[f_result['data']['id']] = f_result['data']['return']
# if this is a start, then we need to add it to the pile
if f_result['tag'].endswith('/new'):
for minion_id in f_result['data']['minions']:
if minion_id not in minions:
minions[minion_id] = False
else:
chunk_ret[f_result['data']['id']] = f_result['data']['return']
# clear finished event future
minions[f_result['data']['id']] = True
# if there are no more minions to wait for, then we are done
if not more_todo() and min_wait_time.done():
cancel_inflight_futures()
raise tornado.gen.Return(chunk_ret)
except TimeoutException:
pass
# clear finished event future
try:
minions.remove(future_minion_map[f])
del future_minion_map[f]
except ValueError:
pass
if not minions:
if not is_finished.done():
is_finished.set_result(True)
raise tornado.gen.Return(chunk_ret)
if f == events[0]:
events[0] = self.application.event_listener.get_event(self, tag='salt/job/'+chunk['jid'])
else:
events[1] = self.application.event_listener.get_event(self, tag='syndic/job/'+chunk['jid'])
@tornado.gen.coroutine
def job_not_running(self, jid, tgt, tgt_type, is_finished):
def job_not_running(self, jid, tgt, tgt_type, minions, is_finished):
'''
Return a future which will complete once jid (passed in) is no longer
running on tgt
@ -1044,8 +1050,6 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
event = f.result()
except TimeoutException:
if not minion_running:
if not is_finished.done():
is_finished.set_result(True)
raise tornado.gen.Return(True)
else:
ping_pub_data = yield self.saltclients['local'](tgt,
@ -1059,6 +1063,8 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223
# Minions can return, we want to see if the job is running...
if event['data'].get('return', {}) == {}:
continue
if event['data']['id'] not in minions:
minions[event['data']['id']] = False
minion_running = True
@tornado.gen.coroutine

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

@ -154,7 +154,7 @@ def action(func=None,
info = {}
client = _get_client()
try:
info = client.action(func, cloudmap, instances, provider, instance, **_filter_kwargs(kwargs))
info = client.action(func, cloudmap, instances, provider, instance, _filter_kwargs(kwargs))
except SaltCloudConfigError as err:
log.error(err)
return info

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

@ -69,7 +69,7 @@ def set_(key, value, service=None, profile=None): # pylint: disable=W0613
'''
key, profile = _parse_key(key, profile)
cache = salt.cache.Cache(__opts__)
cache.set(profile['bank'], key=key, value=value)
cache.store(profile['bank'], key, value)
return get(key, service, profile)

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

@ -255,7 +255,12 @@ def _disable(name, started, result=True, **kwargs):
return ret
# Service can be disabled
before_toggle_disable_status = __salt__['service.disabled'](name)
if salt.utils.is_windows():
# service.disabled in Windows returns True for services that are set to
# Manual start, so we need to check specifically for Disabled
before_toggle_disable_status = __salt__['service.info'](name)['StartType'] in ['Disabled']
else:
before_toggle_disable_status = __salt__['service.disabled'](name)
if before_toggle_disable_status:
# Service is disabled
if started is True:
@ -549,7 +554,12 @@ def dead(name,
# command, so it is just an indicator but can not be fully trusted
before_toggle_status = __salt__['service.status'](name, sig)
if 'service.enabled' in __salt__:
before_toggle_enable_status = __salt__['service.enabled'](name)
if salt.utils.is_windows():
# service.enabled in Windows returns True for services that are set
# to Auto start, but services set to Manual can also be disabled
before_toggle_enable_status = __salt__['service.info'](name)['StartType'] in ['Auto', 'Manual']
else:
before_toggle_enable_status = __salt__['service.enabled'](name)
else:
before_toggle_enable_status = True

View file

@ -752,9 +752,9 @@ class IPCMessageSubscriber(IPCClient):
# This will prevent this message from showing up:
# '[ERROR ] Future exception was never retrieved:
# StreamClosedError'
if self._read_sync_future is not None:
if self._read_sync_future is not None and self._read_sync_future.done():
self._read_sync_future.exception()
if self._read_stream_future is not None:
if self._read_stream_future is not None and self._read_stream_future.done():
self._read_stream_future.exception()
def __del__(self):

View file

@ -896,10 +896,9 @@ class SaltMessageClient(object):
# This happens because the logic is always waiting to read
# the next message and the associated read future is marked
# 'StreamClosedError' when the stream is closed.
self._read_until_future.exception()
if (not self._stream_return_future.done() and
self.io_loop != tornado.ioloop.IOLoop.current(
instance=False)):
if self._read_until_future.done():
self._read_until_future.exception()
elif self.io_loop != tornado.ioloop.IOLoop.current(instance=False):
self.io_loop.add_future(
self._stream_return_future,
lambda future: self.io_loop.stop()
@ -1134,7 +1133,7 @@ class Subscriber(object):
self._closing = True
if not self.stream.closed():
self.stream.close()
if self._read_until_future is not None:
if self._read_until_future is not None and self._read_until_future.done():
# This will prevent this message from showing up:
# '[ERROR ] Future exception was never retrieved:
# StreamClosedError'

View file

@ -2360,7 +2360,7 @@ def alias_function(fun, name, doc=None):
orig_name = fun.__name__
alias_msg = ('\nThis function is an alias of '
'``{0}``.\n'.format(orig_name))
alias_fun.__doc__ = alias_msg + fun.__doc__
alias_fun.__doc__ = alias_msg + (fun.__doc__ or '')
return alias_fun

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,6 +14,7 @@ import tarfile
import zipfile
import tempfile
import subprocess
import concurrent
# Import third party libs
import jinja2
@ -106,6 +107,8 @@ def get_tops(extra_mods='', so_mods=''):
os.path.dirname(msgpack.__file__),
]
if _six.PY2:
tops.append(os.path.dirname(concurrent.__file__))
tops.append(_six.__file__.replace('.pyc', '.py'))
tops.append(backports_abc.__file__.replace('.pyc', '.py'))

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

@ -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

@ -429,6 +429,16 @@ class PipModuleTest(ModuleCase):
pprint.pprint(ret)
raise
@skipIf(not os.path.isfile('pip3'), 'test where pip3 is installed')
@skipIf(salt.utils.is_windows(), 'test specific for linux usage of /bin/python')
def test_system_pip3(self):
self.run_function('pip.install', pkgs=['lazyimport==0.0.1'], bin_env='/bin/pip3')
ret1 = self.run_function('cmd.run', '/bin/pip3 freeze | grep lazyimport')
self.run_function('pip.uninstall', pkgs=['lazyimport'], bin_env='/bin/pip3')
ret2 = self.run_function('cmd.run', '/bin/pip3 freeze | grep lazyimport')
assert 'lazyimport==0.0.1' in ret1
assert ret2 == ''
def tearDown(self):
super(PipModuleTest, self).tearDown()
if os.path.isdir(self.venv_test_dir):

View file

@ -266,6 +266,10 @@ class TestSaltAPIHandler(_SaltnadoIntegrationTestCase):
'tgt': '*',
'fun': 'test.ping',
}
self.application.opts['order_masters'] = True
self.application.opts['syndic_wait'] = 5
response = self.fetch('/',
method='POST',
body=salt.utils.json.dumps(low),

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

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import tarfile
import tempfile
import subprocess
import sys
import os
from tests.support.unit import TestCase
import salt.utils
import salt.utils.thin
try:
import virtualenv
HAS_VENV = True
except ImportError:
HAS_VENV = False
class TestThinDir(TestCase):
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
def tearDown(self):
salt.utils.rm_rf(self.tmpdir)
def test_thin_dir(self):
'''
Test the thin dir to make sure salt-call can run
Run salt call via a python in a new virtual environment to ensure
salt-call has all dependencies needed.
'''
venv_dir = os.path.join(self.tmpdir, 'venv')
virtualenv.create_environment(venv_dir)
salt.utils.thin.gen_thin(self.tmpdir)
thin_dir = os.path.join(self.tmpdir, 'thin')
thin_archive = os.path.join(thin_dir, 'thin.tgz')
tar = tarfile.open(thin_archive)
tar.extractall(thin_dir)
tar.close()
bins = 'bin'
if sys.platform == 'win32':
bins = 'Scripts'
cmd = [
os.path.join(venv_dir, bins, 'python'),
os.path.join(thin_dir, 'salt-call'),
'--version',
]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = proc.communicate()
proc.wait()
assert proc.returncode == 0, (stdout, stderr, proc.returncode)

View file

@ -29,7 +29,7 @@ def iter_installers(content):
x = m.groups()[0]
if not x.startswith(PREFIX):
continue
if x.endswith('zip'):
if x.endswith(('zip', 'sha256')):
continue
if installer:
if x != installer + '.md5':

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

@ -136,6 +136,7 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin):
with patch.dict(docker_mod.__salt__,
{'mine.send': mine_send,
'container_resource.run': MagicMock(),
'config.get': MagicMock(return_value=True),
'cp.cache_file': MagicMock(return_value=False)}):
with patch.dict(docker_mod.__utils__,
{'docker.get_client_args': client_args_mock}):
@ -144,6 +145,44 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin):
mine_send.assert_called_with('docker.ps', verbose=True, all=True,
host=True)
def test_update_mine(self):
'''
Test the docker.update_mine config option
'''
def config_get_disabled(val, default):
return {'base_url': docker_mod.NOTSET,
'version': docker_mod.NOTSET,
'docker.url': docker_mod.NOTSET,
'docker.version': docker_mod.NOTSET,
'docker.machine': docker_mod.NOTSET,
'docker.update_mine': False}[val]
def config_get_enabled(val, default):
return {'base_url': docker_mod.NOTSET,
'version': docker_mod.NOTSET,
'docker.url': docker_mod.NOTSET,
'docker.version': docker_mod.NOTSET,
'docker.machine': docker_mod.NOTSET,
'docker.update_mine': True}[val]
mine_mock = Mock()
dunder_salt = {
'config.get': MagicMock(side_effect=config_get_disabled),
'mine.send': mine_mock,
}
with patch.dict(docker_mod.__salt__, dunder_salt), \
patch.dict(docker_mod.__context__, {'docker.client': Mock()}), \
patch.object(docker_mod, 'state', MagicMock(return_value='stopped')):
docker_mod.stop('foo', timeout=1)
mine_mock.assert_not_called()
with patch.dict(docker_mod.__salt__, dunder_salt), \
patch.dict(docker_mod.__context__, {'docker.client': Mock()}), \
patch.object(docker_mod, 'state', MagicMock(return_value='stopped')):
dunder_salt['config.get'].side_effect = config_get_enabled
docker_mod.stop('foo', timeout=1)
self.assert_called_once(mine_mock)
@skipIf(_docker_py_version() < (1, 5, 0),
'docker module must be installed to run this test or is too old. >=1.5.0')
def test_list_networks(self, *args):

View file

@ -117,12 +117,12 @@ class MountTestCase(TestCase, LoaderModuleMockMixin):
mock_open(read_data=file_data),
create=True) as m:
m.return_value.__iter__.return_value = file_data.splitlines()
self.assertEqual(mount.fstab(), {'/tmp': {'device': 'swap',
'device_fsck': '-',
'fstype': 'tmpfs',
'mount_at_boot': 'yes',
'opts': ['size=2048m'],
'pass_fsck': '-'}})
self.assertEqual(mount.vfstab(), {'/tmp': {'device': 'swap',
'device_fsck': '-',
'fstype': 'tmpfs',
'mount_at_boot': 'yes',
'opts': ['size=2048m'],
'pass_fsck': '-'}})
def test_rm_fstab(self):
'''

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

@ -11,6 +11,7 @@ import os
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.unit import TestCase, skipIf
from tests.support.mock import (
Mock,
MagicMock,
patch,
mock_open,
@ -357,6 +358,9 @@ class StateTestCase(TestCase, LoaderModuleMockMixin):
'environment': None,
'__cli': 'salt',
},
'__salt__': {
'config.option': MagicMock(return_value=''),
},
},
}
@ -752,28 +756,25 @@ class StateTestCase(TestCase, LoaderModuleMockMixin):
"whitelist=sls1.sls",
pillar="A")
mock = MagicMock(return_value=True)
with patch.dict(state.__salt__,
{'config.option': mock}):
mock = MagicMock(return_value="A")
mock = MagicMock(return_value="A")
with patch.object(state, '_filter_running',
mock):
mock = MagicMock(return_value=True)
with patch.object(state, '_filter_running',
mock):
mock = MagicMock(return_value=True)
with patch.object(state, '_filter_running',
with patch.object(salt.payload, 'Serial',
mock):
mock = MagicMock(return_value=True)
with patch.object(salt.payload, 'Serial',
mock):
with patch.object(os.path,
'join', mock):
with patch.object(
state,
'_set'
'_retcode',
mock):
self.assertTrue(state.
highstate
(arg))
with patch.object(os.path,
'join', mock):
with patch.object(
state,
'_set'
'_retcode',
mock):
self.assertTrue(state.
highstate
(arg))
def test_clear_request(self):
'''
@ -914,17 +915,11 @@ class StateTestCase(TestCase, LoaderModuleMockMixin):
MockState.HighState.flag = False
mock = MagicMock(return_value=True)
with patch.dict(state.__salt__,
{'config.option':
mock}):
mock = MagicMock(return_value=
True)
with patch.object(
state,
'_filter_'
'running',
mock):
self.sub_test_sls()
with patch.object(state,
'_filter_'
'running',
mock):
self.sub_test_sls()
def sub_test_sls(self):
'''
@ -947,6 +942,75 @@ class StateTestCase(TestCase, LoaderModuleMockMixin):
None,
True))
def test_sls_sync(self):
'''
Test test.sls with the sync argument
We're only mocking the sync functions we expect to sync. If any other
sync functions are run then they will raise a KeyError, which we want
as it will tell us that we are syncing things we shouldn't.
'''
mock_empty_list = MagicMock(return_value=[])
with patch.object(state, 'running', mock_empty_list), \
patch.object(state, '_disabled', mock_empty_list), \
patch.object(state, '_get_pillar_errors', mock_empty_list):
sync_mocks = {
'saltutil.sync_modules': Mock(),
'saltutil.sync_states': Mock(),
}
with patch.dict(state.__salt__, sync_mocks):
state.sls('foo', sync_mods='modules,states')
for key in sync_mocks:
call_count = sync_mocks[key].call_count
expected = 1
assert call_count == expected, \
'{0} called {1} time(s) (expected: {2})'.format(
key, call_count, expected
)
# Test syncing all
sync_mocks = {'saltutil.sync_all': Mock()}
with patch.dict(state.__salt__, sync_mocks):
state.sls('foo', sync_mods='all')
for key in sync_mocks:
call_count = sync_mocks[key].call_count
expected = 1
assert call_count == expected, \
'{0} called {1} time(s) (expected: {2})'.format(
key, call_count, expected
)
# sync_mods=True should be interpreted as sync_mods=all
sync_mocks = {'saltutil.sync_all': Mock()}
with patch.dict(state.__salt__, sync_mocks):
state.sls('foo', sync_mods=True)
for key in sync_mocks:
call_count = sync_mocks[key].call_count
expected = 1
assert call_count == expected, \
'{0} called {1} time(s) (expected: {2})'.format(
key, call_count, expected
)
# Test syncing all when "all" is passed along with module types.
# This tests that we *only* run a sync_all and avoid unnecessary
# extra syncing.
sync_mocks = {'saltutil.sync_all': Mock()}
with patch.dict(state.__salt__, sync_mocks):
state.sls('foo', sync_mods='modules,all')
for key in sync_mocks:
call_count = sync_mocks[key].call_count
expected = 1
assert call_count == expected, \
'{0} called {1} time(s) (expected: {2})'.format(
key, call_count, expected
)
def test_pkg(self):
'''
Test to execute a packaged state run

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])

Some files were not shown because too many files have changed in this diff Show more