diff --git a/.ci/docs b/.ci/docs index 2ff232deaa46..7bf5d0a5b99d 100644 --- a/.ci/docs +++ b/.ci/docs @@ -1,54 +1,6 @@ -@Library('salt@1.1') _ +@Library('salt@master-1.5') _ -// Define the maximum time, in hours, that a test run should run for -def global_timeout = 2 -def salt_target_branch = 'master' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - -def shell_header - -wrappedNode('docs', global_timeout, '#jenkins-prod-pr') { - - shell_header = '' - // Checkout the repo - stage('checkout-scm') { - cleanWs notFailBuild: true - checkout scm - } - - // Setup the kitchen required bundle - stage('Setup') { - sh shell_header + ''' - eval "$(pyenv init -)" - pyenv --version - pyenv install --skip-existing 3.6.8 - pyenv shell 3.6.8 - python --version - pip install -U nox-py2 - nox --version - ''' - } - - stage('Build') { - sh shell_header + ''' - eval "$(pyenv init -)" - pyenv shell 3.6.8 - nox -e docs - ''' - archiveArtifacts artifacts: 'doc/doc-archive.tar.gz' - } -} +runDocs( + env: env) // vim: ft=groovy diff --git a/.ci/kitchen-amazon1-py2 b/.ci/kitchen-amazon1-py2 index 143cf13db11f..1cc816c06ab2 100644 --- a/.ci/kitchen-amazon1-py2 +++ b/.ci/kitchen-amazon1-py2 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'amazon' -def distro_version = '1' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'amazon', + distro_version: '1', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-amazon2-py2 b/.ci/kitchen-amazon2-py2 index f8c027486983..f4472174b64e 100644 --- a/.ci/kitchen-amazon2-py2 +++ b/.ci/kitchen-amazon2-py2 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'amazon' -def distro_version = '2' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'amazon', + distro_version: '2', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-amazon2-py3 b/.ci/kitchen-amazon2-py3 index 159e1dda1f7d..27af6a0e9d78 100644 --- a/.ci/kitchen-amazon2-py3 +++ b/.ci/kitchen-amazon2-py3 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'amazon' -def distro_version = '2' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'amazon', + distro_version: '2', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-archlts-py2 b/.ci/kitchen-archlts-py2 index 7aa8f99518fd..39d6b4d98d3d 100644 --- a/.ci/kitchen-archlts-py2 +++ b/.ci/kitchen-archlts-py2 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'arch' -def distro_version = 'lts' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '-n integration.modules.test_pkg' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'arch', + distro_version: 'lts', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '-n integration.modules.test_pkg', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-archlts-py3 b/.ci/kitchen-archlts-py3 index 551dfc271430..5d1b351582fe 100644 --- a/.ci/kitchen-archlts-py3 +++ b/.ci/kitchen-archlts-py3 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'arch' -def distro_version = 'lts' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '-n integration.modules.test_pkg' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'arch', + distro_version: 'lts', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '-n integration.modules.test_pkg', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-centos6-py2 b/.ci/kitchen-centos6-py2 index f217666ebf95..73c912f52512 100644 --- a/.ci/kitchen-centos6-py2 +++ b/.ci/kitchen-centos6-py2 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'centos' -def distro_version = '6' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '6', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2 b/.ci/kitchen-centos7-py2 index f980e3ce5999..7cffd0c53e5d 100644 --- a/.ci/kitchen-centos7-py2 +++ b/.ci/kitchen-centos7-py2 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'centos' -def distro_version = '7' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '7', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-cloud b/.ci/kitchen-centos7-py2-cloud new file mode 100644 index 000000000000..5a820da41efb --- /dev/null +++ b/.ci/kitchen-centos7-py2-cloud @@ -0,0 +1,17 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 0, + distro_name: 'centos', + distro_version: '7', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + kitchen_platforms_file: '/var/jenkins/workspace/nox-cloud-platforms.yml', + nox_env_name: 'runtests-cloud', + nox_passthrough_opts: '', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-m2crypto b/.ci/kitchen-centos7-py2-m2crypto new file mode 100644 index 000000000000..014678f1c5a1 --- /dev/null +++ b/.ci/kitchen-centos7-py2-m2crypto @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '7', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq-m2crypto', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-proxy b/.ci/kitchen-centos7-py2-proxy new file mode 100644 index 000000000000..9a7ef2cb5c8e --- /dev/null +++ b/.ci/kitchen-centos7-py2-proxy @@ -0,0 +1,17 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '7', + env: env, + extra_codecov_flags: ["proxy"], + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--proxy', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-pycryptodomex b/.ci/kitchen-centos7-py2-pycryptodomex new file mode 100644 index 000000000000..9f1d59b6574c --- /dev/null +++ b/.ci/kitchen-centos7-py2-pycryptodomex @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '7', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq-pycryptodomex', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-tcp b/.ci/kitchen-centos7-py2-tcp index 3f9265520c68..618d5fbb0af6 100644 --- a/.ci/kitchen-centos7-py2-tcp +++ b/.ci/kitchen-centos7-py2-tcp @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'centos' -def distro_version = '7' -def python_version = 'py2' -def test_transport = 'TCP' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '7', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-tcp', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-tornado b/.ci/kitchen-centos7-py2-tornado new file mode 100644 index 000000000000..92835dfe99e2 --- /dev/null +++ b/.ci/kitchen-centos7-py2-tornado @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '7', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-tornado', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py3 b/.ci/kitchen-centos7-py3 index b004d61172a6..80c931c321db 100644 --- a/.ci/kitchen-centos7-py3 +++ b/.ci/kitchen-centos7-py3 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'centos' -def distro_version = '7' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '7', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-centos7-py3-cloud b/.ci/kitchen-centos7-py3-cloud new file mode 100644 index 000000000000..0a01e58fd079 --- /dev/null +++ b/.ci/kitchen-centos7-py3-cloud @@ -0,0 +1,17 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 0, + distro_name: 'centos', + distro_version: '7', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + kitchen_platforms_file: '/var/jenkins/workspace/nox-cloud-platforms.yml', + nox_env_name: 'runtests-cloud', + nox_passthrough_opts: '', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py3-m2crypto b/.ci/kitchen-centos7-py3-m2crypto new file mode 100644 index 000000000000..ac0727c8bc91 --- /dev/null +++ b/.ci/kitchen-centos7-py3-m2crypto @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '7', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq-m2crypto', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py3-proxy b/.ci/kitchen-centos7-py3-proxy new file mode 100644 index 000000000000..69d521d1df9e --- /dev/null +++ b/.ci/kitchen-centos7-py3-proxy @@ -0,0 +1,17 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '7', + env: env, + extra_codecov_flags: ["proxy"], + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--proxy', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py3-pycryptodomex b/.ci/kitchen-centos7-py3-pycryptodomex new file mode 100644 index 000000000000..a4068b2dc638 --- /dev/null +++ b/.ci/kitchen-centos7-py3-pycryptodomex @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '7', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq-pycryptodomex', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py3-tcp b/.ci/kitchen-centos7-py3-tcp index 3b7339c2aedd..acde7f88a07f 100644 --- a/.ci/kitchen-centos7-py3-tcp +++ b/.ci/kitchen-centos7-py3-tcp @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'centos' -def distro_version = '7' -def python_version = 'py3' -def test_transport = 'TCP' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '7', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-tcp', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-centos8-py3 b/.ci/kitchen-centos8-py3 new file mode 100644 index 000000000000..d4c8c7093d10 --- /dev/null +++ b/.ci/kitchen-centos8-py3 @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'centos', + distro_version: '8', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-debian10-py3 b/.ci/kitchen-debian10-py3 index 1a00e06f92dc..31c81a7fa7f1 100644 --- a/.ci/kitchen-debian10-py3 +++ b/.ci/kitchen-debian10-py3 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'debian' -def distro_version = '10' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'debian', + distro_version: '10', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-debian8-py2 b/.ci/kitchen-debian8-py2 index 89b7f0af6c14..c089fd2b4215 100644 --- a/.ci/kitchen-debian8-py2 +++ b/.ci/kitchen-debian8-py2 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'debian' -def distro_version = '8' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'debian', + distro_version: '8', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-debian9-py2 b/.ci/kitchen-debian9-py2 index 1b4c37acb1ce..535b4c2ba7be 100644 --- a/.ci/kitchen-debian9-py2 +++ b/.ci/kitchen-debian9-py2 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'debian' -def distro_version = '9' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'debian', + distro_version: '9', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-debian9-py3 b/.ci/kitchen-debian9-py3 index 023ede4aae7a..30f466805a65 100644 --- a/.ci/kitchen-debian9-py3 +++ b/.ci/kitchen-debian9-py3 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'debian' -def distro_version = '9' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'debian', + distro_version: '9', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-fedora29-py2 b/.ci/kitchen-fedora29-py2 deleted file mode 100644 index 19bb3bc5ebca..000000000000 --- a/.ci/kitchen-fedora29-py2 +++ /dev/null @@ -1,159 +0,0 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'fedora' -def distro_version = '29' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} - -// vim: ft=groovy diff --git a/.ci/kitchen-fedora29-py3 b/.ci/kitchen-fedora29-py3 deleted file mode 100644 index 99b881dc3358..000000000000 --- a/.ci/kitchen-fedora29-py3 +++ /dev/null @@ -1,159 +0,0 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'fedora' -def distro_version = '29' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} - -// vim: ft=groovy diff --git a/.ci/kitchen-fedora30-py2 b/.ci/kitchen-fedora30-py2 index 3f45aa3c802c..fad0fbdf98a9 100644 --- a/.ci/kitchen-fedora30-py2 +++ b/.ci/kitchen-fedora30-py2 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'fedora' -def distro_version = '30' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'fedora', + distro_version: '30', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-fedora30-py3 b/.ci/kitchen-fedora30-py3 index 194020b69113..ff62b85e45ad 100644 --- a/.ci/kitchen-fedora30-py3 +++ b/.ci/kitchen-fedora30-py3 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'fedora' -def distro_version = '30' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'fedora', + distro_version: '30', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-fedora31-py3 b/.ci/kitchen-fedora31-py3 new file mode 100644 index 000000000000..803fe6ef97ab --- /dev/null +++ b/.ci/kitchen-fedora31-py3 @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'fedora', + distro_version: '31', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-macosxhighsierra-py2 b/.ci/kitchen-macosxhighsierra-py2 new file mode 100644 index 000000000000..29a1cb0bb8bb --- /dev/null +++ b/.ci/kitchen-macosxhighsierra-py2 @@ -0,0 +1,20 @@ +@Library('salt@master-1.5') _ + +// Pre-nox pipeline +runTestSuite( + concurrent_builds: 1, + distro_name: 'macosx', + distro_version: 'highsierra', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave-mac', + kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', + kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '', + python_version: 'py2', + run_full: params.runFull, + testrun_timeout: 6, + use_spot_instances: false) + +// vim: ft=groovy diff --git a/.ci/kitchen-macosxhighsierra-py3 b/.ci/kitchen-macosxhighsierra-py3 new file mode 100644 index 000000000000..55fdec3fabf3 --- /dev/null +++ b/.ci/kitchen-macosxhighsierra-py3 @@ -0,0 +1,20 @@ +@Library('salt@master-1.5') _ + +// Pre-nox pipeline +runTestSuite( + concurrent_builds: 1, + distro_name: 'macosx', + distro_version: 'highsierra', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave-mac', + kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', + kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '', + python_version: 'py3', + run_full: params.runFull, + testrun_timeout: 6, + use_spot_instances: false) + +// vim: ft=groovy diff --git a/.ci/kitchen-macosxmojave-py2 b/.ci/kitchen-macosxmojave-py2 index 7ef399f805e4..370cc55dc1d3 100644 --- a/.ci/kitchen-macosxmojave-py2 +++ b/.ci/kitchen-macosxmojave-py2 @@ -1,146 +1,20 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'macosx' -def distro_version = 'mojave' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - -wrappedNode('kitchen-slave-mac', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - "PATH=/Users/parallels/.rbenv/shims:/Users/parallels/.rbenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/salt/bin:/usr/local/sbin", - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - stage('VM Cleanup') { - sh ''' - for i in `prlctl list -aij|jq -r '.[]|select((.Uptime|tonumber > 86400) and (.State == "running"))|.ID'` - do - prlctl stop $i --kill - done - # don't delete vm's that haven't started yet ((.State == "stopped") and (.Uptime == "0")) - for i in `prlctl list -aij|jq -r '.[]|select((.Uptime|tonumber > 0) and (.State != "running"))|.ID'` - do - prlctl delete $i - done - ''' - } - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with vagrant macos --without ec2 windows opennebula docker' - } - - stage('Create VM') { - sh ''' - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.vagrant.d/insecure_private_key; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +// Pre-nox pipeline +runTestSuite( + concurrent_builds: 1, + distro_name: 'macosx', + distro_version: 'mojave', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave-mac', + kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', + kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '', + python_version: 'py2', + run_full: params.runFull, + testrun_timeout: 6, + use_spot_instances: false) // vim: ft=groovy diff --git a/.ci/kitchen-macosxmojave-py3 b/.ci/kitchen-macosxmojave-py3 index a46d63ab19b6..e3ceca6090c3 100644 --- a/.ci/kitchen-macosxmojave-py3 +++ b/.ci/kitchen-macosxmojave-py3 @@ -1,146 +1,20 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'macosx' -def distro_version = 'mojave' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - -wrappedNode('kitchen-slave-mac', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - "PATH=/Users/parallels/.rbenv/shims:/Users/parallels/.rbenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/salt/bin:/usr/local/sbin", - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - stage('VM Cleanup') { - sh ''' - for i in `prlctl list -aij|jq -r '.[]|select((.Uptime|tonumber > 86400) and (.State == "running"))|.ID'` - do - prlctl stop $i --kill - done - # don't delete vm's that haven't started yet ((.State == "stopped") and (.Uptime == "0")) - for i in `prlctl list -aij|jq -r '.[]|select((.Uptime|tonumber > 0) and (.State != "running"))|.ID'` - do - prlctl delete $i - done - ''' - } - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with vagrant macos --without ec2 windows opennebula docker' - } - - stage('Create VM') { - sh ''' - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.vagrant.d/insecure_private_key; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +// Pre-nox pipeline +runTestSuite( + concurrent_builds: 1, + distro_name: 'macosx', + distro_version: 'mojave', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave-mac', + kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', + kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '', + python_version: 'py3', + run_full: params.runFull, + testrun_timeout: 6, + use_spot_instances: false) // vim: ft=groovy diff --git a/.ci/kitchen-macosxsierra-py2 b/.ci/kitchen-macosxsierra-py2 new file mode 100644 index 000000000000..45b13915aa6b --- /dev/null +++ b/.ci/kitchen-macosxsierra-py2 @@ -0,0 +1,20 @@ +@Library('salt@master-1.5') _ + +// Pre-nox pipeline +runTestSuite( + concurrent_builds: 1, + distro_name: 'macosx', + distro_version: 'sierra', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave-mac', + kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', + kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '', + python_version: 'py2', + run_full: params.runFull, + testrun_timeout: 6, + use_spot_instances: false) + +// vim: ft=groovy diff --git a/.ci/kitchen-macosxsierra-py3 b/.ci/kitchen-macosxsierra-py3 new file mode 100644 index 000000000000..84d6bc295c03 --- /dev/null +++ b/.ci/kitchen-macosxsierra-py3 @@ -0,0 +1,20 @@ +@Library('salt@master-1.5') _ + +// Pre-nox pipeline +runTestSuite( + concurrent_builds: 1, + distro_name: 'macosx', + distro_version: 'sierra', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave-mac', + kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', + kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '', + python_version: 'py3', + run_full: params.runFull, + testrun_timeout: 6, + use_spot_instances: false) + +// vim: ft=groovy diff --git a/.ci/kitchen-opensuse15-py2 b/.ci/kitchen-opensuse15-py2 index 88cef5f6b423..46a638c7a4d3 100644 --- a/.ci/kitchen-opensuse15-py2 +++ b/.ci/kitchen-opensuse15-py2 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'opensuse' -def distro_version = '15' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'opensuse', + distro_version: '15', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-opensuse15-py3 b/.ci/kitchen-opensuse15-py3 index 84ff66f05c15..2c993a8700f1 100644 --- a/.ci/kitchen-opensuse15-py3 +++ b/.ci/kitchen-opensuse15-py3 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'opensuse' -def distro_version = '15' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'opensuse', + distro_version: '15', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py2 b/.ci/kitchen-ubuntu1604-py2 index 9de36a87ed6b..99263d9365c9 100644 --- a/.ci/kitchen-ubuntu1604-py2 +++ b/.ci/kitchen-ubuntu1604-py2 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'ubuntu' -def distro_version = '1604' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1604', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py2-m2crypto b/.ci/kitchen-ubuntu1604-py2-m2crypto new file mode 100644 index 000000000000..8fc5c4bd3509 --- /dev/null +++ b/.ci/kitchen-ubuntu1604-py2-m2crypto @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1604', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq-m2crypto', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py2-proxy b/.ci/kitchen-ubuntu1604-py2-proxy new file mode 100644 index 000000000000..939d322e6584 --- /dev/null +++ b/.ci/kitchen-ubuntu1604-py2-proxy @@ -0,0 +1,17 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1604', + env: env, + extra_codecov_flags: ["proxy"], + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--proxy', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py2-pycryptodomex b/.ci/kitchen-ubuntu1604-py2-pycryptodomex new file mode 100644 index 000000000000..a56a5e0900d0 --- /dev/null +++ b/.ci/kitchen-ubuntu1604-py2-pycryptodomex @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1604', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq-pycryptodomex', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py2-tcp b/.ci/kitchen-ubuntu1604-py2-tcp index 2d27d04fb533..cbd257e96417 100644 --- a/.ci/kitchen-ubuntu1604-py2-tcp +++ b/.ci/kitchen-ubuntu1604-py2-tcp @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'ubuntu' -def distro_version = '1604' -def python_version = 'py2' -def test_transport = 'TCP' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1604', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-tcp', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py2-tornado b/.ci/kitchen-ubuntu1604-py2-tornado new file mode 100644 index 000000000000..1a67ef5b305d --- /dev/null +++ b/.ci/kitchen-ubuntu1604-py2-tornado @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1604', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-tornado', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py3 b/.ci/kitchen-ubuntu1604-py3 index 2aae4c26f599..935039216df4 100644 --- a/.ci/kitchen-ubuntu1604-py3 +++ b/.ci/kitchen-ubuntu1604-py3 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'ubuntu' -def distro_version = '1604' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1604', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py3-m2crypto b/.ci/kitchen-ubuntu1604-py3-m2crypto new file mode 100644 index 000000000000..b2a6c46d56e6 --- /dev/null +++ b/.ci/kitchen-ubuntu1604-py3-m2crypto @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1604', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq-m2crypto', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py3-proxy b/.ci/kitchen-ubuntu1604-py3-proxy new file mode 100644 index 000000000000..7f329a368ff8 --- /dev/null +++ b/.ci/kitchen-ubuntu1604-py3-proxy @@ -0,0 +1,17 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1604', + env: env, + extra_codecov_flags: ["proxy"], + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--proxy', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py3-pycryptodomex b/.ci/kitchen-ubuntu1604-py3-pycryptodomex new file mode 100644 index 000000000000..d02214fa69de --- /dev/null +++ b/.ci/kitchen-ubuntu1604-py3-pycryptodomex @@ -0,0 +1,16 @@ +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1604', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq-pycryptodomex', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) + +// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py3-tcp b/.ci/kitchen-ubuntu1604-py3-tcp index 47a150ac4f32..78a4d663fdbf 100644 --- a/.ci/kitchen-ubuntu1604-py3-tcp +++ b/.ci/kitchen-ubuntu1604-py3-tcp @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'ubuntu' -def distro_version = '1604' -def python_version = 'py3' -def test_transport = 'TCP' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1604', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-tcp', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1804-py2 b/.ci/kitchen-ubuntu1804-py2 index 270bebc1c330..143d907de3a9 100644 --- a/.ci/kitchen-ubuntu1804-py2 +++ b/.ci/kitchen-ubuntu1804-py2 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'ubuntu' -def distro_version = '1804' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1804', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py2', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1804-py3 b/.ci/kitchen-ubuntu1804-py3 index 4a6bf582b44c..cdd2b77a99cc 100644 --- a/.ci/kitchen-ubuntu1804-py3 +++ b/.ci/kitchen-ubuntu1804-py3 @@ -1,159 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 6 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'ubuntu' -def distro_version = '1804' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--ssh-tests' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - cp -f ~/workspace/spot.yml .kitchen.local.yml - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM); echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout * 60 - 15, unit: 'MINUTES') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'ubuntu', + distro_version: '1804', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--ssh-tests', + python_version: 'py3', + testrun_timeout: 6, + use_spot_instances: true) // vim: ft=groovy diff --git a/.ci/kitchen-windows2016-py2 b/.ci/kitchen-windows2016-py2 index 9d3c1a258451..243abba8ed8d 100644 --- a/.ci/kitchen-windows2016-py2 +++ b/.ci/kitchen-windows2016-py2 @@ -1,158 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 8 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'windows' -def distro_version = '2016' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--unit' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout, unit: 'HOURS') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'windows', + distro_version: '2016', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--unit', + python_version: 'py2', + testrun_timeout: 8, + use_spot_instances: false) // vim: ft=groovy diff --git a/.ci/kitchen-windows2016-py3 b/.ci/kitchen-windows2016-py3 index 8cd0f6fa5659..5d1d811407e8 100644 --- a/.ci/kitchen-windows2016-py3 +++ b/.ci/kitchen-windows2016-py3 @@ -1,158 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 8 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'windows' -def distro_version = '2016' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--unit' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout, unit: 'HOURS') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'windows', + distro_version: '2016', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--unit', + python_version: 'py3', + testrun_timeout: 8, + use_spot_instances: false) // vim: ft=groovy diff --git a/.ci/kitchen-windows2019-py2 b/.ci/kitchen-windows2019-py2 index 1337d7e81aad..a33e583f0fb8 100644 --- a/.ci/kitchen-windows2019-py2 +++ b/.ci/kitchen-windows2019-py2 @@ -1,158 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 8 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'windows' -def distro_version = '2019' -def python_version = 'py2' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--unit' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout, unit: 'HOURS') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'windows', + distro_version: '2019', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--unit', + python_version: 'py2', + testrun_timeout: 8, + use_spot_instances: false) // vim: ft=groovy diff --git a/.ci/kitchen-windows2019-py3 b/.ci/kitchen-windows2019-py3 index 1327620ddfd0..b69af1f118d2 100644 --- a/.ci/kitchen-windows2019-py3 +++ b/.ci/kitchen-windows2019-py3 @@ -1,158 +1,16 @@ -@Library('salt@1.1') _ - -// Define the maximum time, in hours, that a test run should run for -def testrun_timeout = 8 -// Now define a global pipeline timeout. This is the test run timeout with one(1) additional -// hour to allow for artifacts to be downloaded, if possible. -def global_timeout = testrun_timeout + 1; - -def distro_name = 'windows' -def distro_version = '2019' -def python_version = 'py3' -def test_transport = 'ZeroMQ' -def salt_target_branch = 'master' -def golden_images_branch = '2019.2' -def nox_passthrough_opts = '--unit' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), - parameters([ - booleanParam(defaultValue: true, description: 'Run full test suite', name: 'runFull') - ]) -]) - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - - -wrappedNode('kitchen-slave', global_timeout, '#jenkins-prod-pr') { - withEnv([ - 'SALT_KITCHEN_PLATFORMS=/var/jenkins/workspace/nox-platforms.yml', - 'SALT_KITCHEN_VERIFIER=/var/jenkins/workspace/nox-verifier.yml', - 'SALT_KITCHEN_DRIVER=/var/jenkins/workspace/driver.yml', - "NOX_ENV_NAME=runtests-${test_transport.toLowerCase()}", - 'NOX_ENABLE_FROM_FILENAMES=true', - "NOX_PASSTHROUGH_OPTS=${nox_passthrough_opts}", - "SALT_TARGET_BRANCH=${salt_target_branch}", - "GOLDEN_IMAGES_CI_BRANCH=${golden_images_branch}", - "CODECOV_FLAGS=${distro_name}${distro_version},${python_version},${test_transport.toLowerCase()}", - 'PATH=~/.rbenv/shims:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin', - 'RBENV_VERSION=2.6.3', - "TEST_SUITE=${python_version}", - "TEST_PLATFORM=${distro_name}-${distro_version}", - "TEST_TRANSPORT=${test_transport}", - "FORCE_FULL=${params.runFull}", - ]) { - // Checkout the repo - stage('Clone') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh 'bundle install --with ec2 windows --without docker macos opennebula vagrant' - } - - stage('Create VM') { - retry(3) { - sh ''' - t=$(shuf -i 30-120 -n 1); echo "Sleeping $t seconds"; sleep $t - bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-create.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-create.log" - fi - """ - } - sh ''' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'image_id:' - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM | grep 'instance_type:' -A5 - ''' - } - - try { - timeout(time: testrun_timeout, unit: 'HOURS') { - stage('Converge VM') { - sh ''' - ssh-agent /bin/bash -c 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"' - ''' - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-converge.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-converge.log" - fi - """ - } - stage('Run Tests') { - withEnv(["DONT_DOWNLOAD_ARTEFACTS=1"]) { - sh 'bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?";' - } - } - } - } finally { - try { - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-verify.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-verify.log" - fi - """ - stage('Download Artefacts') { - withEnv(["ONLY_DOWNLOAD_ARTEFACTS=1"]){ - sh ''' - bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM || exit 0 - ''' - } - sh """ - if [ -s ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ]; then - mv ".kitchen/logs/${python_version}-${distro_name}-${distro_version}.log" ".kitchen/logs/${python_version}-${distro_name}-${distro_version}-download.log" - fi - if [ -s ".kitchen/logs/kitchen.log" ]; then - mv ".kitchen/logs/kitchen.log" ".kitchen/logs/kitchen-download.log" - fi - """ - } - archiveArtifacts( - artifacts: "artifacts/*,artifacts/**/*,.kitchen/logs/*-create.log,.kitchen/logs/*-converge.log,.kitchen/logs/*-verify.log,.kitchen/logs/*-download.log,artifacts/xml-unittests-output/*.xml", - allowEmptyArchive: true - ) - junit 'artifacts/xml-unittests-output/*.xml' - } finally { - stage('Cleanup') { - sh ''' - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; echo "ExitCode: $?"; - ''' - } - stage('Upload Coverage') { - script { - withCredentials([[$class: 'StringBinding', credentialsId: 'codecov-upload-token-salt', variable: 'CODECOV_TOKEN']]) { - sh ''' - if [ -n "${FORCE_FULL}" -a "${FORCE_FULL}" = "true" -a -f artifacts/coverage/coverage.xml ]; then - (curl -L https://codecov.io/bash | /bin/sh -s -- -R $(pwd) -s artifacts/coverage/ -F "${CODECOV_FLAGS}") || true - fi - ''' - } - } - } - } - } - } -} +@Library('salt@master-1.5') _ + +runTestSuite( + concurrent_builds: 1, + distro_name: 'windows', + distro_version: '2019', + env: env, + golden_images_branch: 'master', + jenkins_slave_label: 'kitchen-slave', + nox_env_name: 'runtests-zeromq', + nox_passthrough_opts: '--unit', + python_version: 'py3', + testrun_timeout: 8, + use_spot_instances: false) // vim: ft=groovy diff --git a/.ci/lint b/.ci/lint index fd61de5b8814..2923e0bb6e17 100644 --- a/.ci/lint +++ b/.ci/lint @@ -1,182 +1,6 @@ -@Library('salt@1.1') _ +@Library('salt@master-1.5') _ -// Define the maximum time, in hours, that a test run should run for -def global_timeout = 3 -def salt_target_branch = 'master' - -properties([ - buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '10')), -]) - -def shell_header - -// Be sure to cancel any previously running builds -def buildNumber = env.BUILD_NUMBER as int -if (buildNumber > 1) { - // This will cancel the previous build which also defined a matching milestone - milestone(buildNumber - 1) -} -// Define a milestone for this build so that, if another build starts, this one will be aborted -milestone(buildNumber) - -def lint_report_issues = [] - -wrappedNode('lint', global_timeout, '#jenkins-prod-pr') { - try { - shell_header = '' - - withEnv(["SALT_TARGET_BRANCH=${salt_target_branch}"]) { - // Checkout the repo - stage('checkout-scm') { - cleanWs notFailBuild: true - checkout scm - sh 'git fetch --no-tags https://github.com/saltstack/salt.git +refs/heads/${SALT_TARGET_BRANCH}:refs/remotes/origin/${SALT_TARGET_BRANCH}' - } - - // Setup the kitchen required bundle - stage('Setup') { - sh shell_header + ''' - # Need -M to detect renames otherwise they are reported as Delete and Add, need -C to detect copies, -C includes -M - # -M is on by default in git 2.9+ - git diff --name-status -l99999 -C "origin/${SALT_TARGET_BRANCH}" > file-list-status.log - # the -l increase the search limit, lets use awk so we do not need to repeat the search above. - gawk 'BEGIN {FS="\\t"} {if ($1 != "D") {print $NF}}' file-list-status.log > file-list-changed.log - gawk 'BEGIN {FS="\\t"} {if ($1 == "D") {print $NF}}' file-list-status.log > file-list-deleted.log - (git diff --name-status -l99999 -C "origin/${SALT_TARGET_BRANCH}" "origin/$BRANCH_NAME";echo "---";git diff --name-status -l99999 -C "origin/$BRANCH_NAME";printenv|grep -E '=[0-9a-z]{40,}+$|COMMIT=|BRANCH') > file-list-experiment.log - eval "$(pyenv init -)" - pyenv --version - pyenv install --skip-existing 2.7.15 - pyenv install --skip-existing 3.6.8 - pyenv shell 3.6.8 2.7.15 - python --version - pip3 install -U nox-py2 - nox --version - # Create the required virtualenvs in serial - nox --install-only -e lint-salt - nox --install-only -e lint-tests - ''' - } - archiveArtifacts( - artifacts: 'file-list-status.log,file-list-changed.log,file-list-deleted.log,file-list-experiment.log', - allowEmptyArchive: true - ) - } - - stage('Lint Changes') { - try { - parallel( - lintSalt: { - stage('Lint Salt Changes') { - if (readFile('file-list-changed.log') =~ /(?i)(^|\n)(salt\/.*\.py|setup\.py)\n/) { - sh shell_header + ''' - eval "$(pyenv init - --no-rehash)" - pyenv shell 3.6.8 2.7.15 - EC=254 - export PYLINT_REPORT=pylint-report-salt-chg.log - grep -Ei '^salt/.*\\.py$|^setup\\.py$' file-list-changed.log | xargs -r '--delimiter=\\n' nox -e lint-salt -- - EC=$? - exit $EC - ''' - } else { - // Always lint something so reporting doesn't fail - sh shell_header + ''' - eval "$(pyenv init - --no-rehash)" - pyenv shell 3.6.8 2.7.15 - EC=254 - export PYLINT_REPORT=pylint-report-salt-chg.log - nox -e lint-salt -- salt/ext/__init__.py - EC=$? - exit $EC - ''' - } - } - }, - lintTests: { - stage('Lint Test Changes') { - if (readFile('file-list-changed.log') =~ /(?i)(^|\n)tests\/.*\.py\n/) { - sh shell_header + ''' - eval "$(pyenv init - --no-rehash)" - pyenv shell 3.6.8 2.7.15 - EC=254 - export PYLINT_REPORT=pylint-report-tests-chg.log - grep -Ei '^tests/.*\\.py$' file-list-changed.log | xargs -r '--delimiter=\\n' nox -e lint-tests -- - EC=$? - exit $EC - ''' - } - } - } - ) - } finally { - def changed_logs_pattern = 'pylint-report-*-chg.log' - archiveArtifacts( - artifacts: changed_logs_pattern, - allowEmptyArchive: true - ) - lint_report_issues.add( - scanForIssues( - tool: pyLint(pattern: changed_logs_pattern, reportEncoding: 'UTF-8') - ) - ) - } - } - stage('Lint Full') { - if (env.CHANGE_BRANCH =~ /(?i)^merge[._-]/) { - // perform a full linit if this is a merge forward and the change only lint passed. - try { - parallel( - lintSaltFull: { - stage('Lint Salt Full') { - sh shell_header + ''' - eval "$(pyenv init - --no-rehash)" - pyenv shell 3.6.8 2.7.15 - EC=254 - export PYLINT_REPORT=pylint-report-salt-full.log - nox -e lint-salt - EC=$? - exit $EC - ''' - } - }, - lintTestsFull: { - stage('Lint Tests Full') { - sh shell_header + ''' - eval "$(pyenv init - --no-rehash)" - pyenv shell 3.6.8 2.7.15 - EC=254 - export PYLINT_REPORT=pylint-report-tests-full.log - nox -e lint-salt - EC=$? - exit $EC - ''' - } - } - ) - } finally { - def full_logs_pattern = 'pylint-report-*-full.log' - archiveArtifacts( - artifacts: full_logs_pattern, - allowEmptyArchive: true - ) - lint_report_issues.add( - scanForIssues( - tool: pyLint(pattern: full_logs_pattern, reportEncoding: 'UTF-8') - ) - ) - } - } - } - } finally { - publishIssues( - enabledForFailure: true, - aggregatingResults: true, - referenceJobName: "${salt_target_branch}/salt-${salt_target_branch}-lint", - qualityGates: [ - [threshold: 1, type: 'TOTAL', unstable: false] - ], - issues: lint_report_issues - ) - } -} +runLint( + env: env) // vim: ft=groovy diff --git a/.codecov.yml b/.codecov.yml index 1b52ce88ddb4..2b63ab379039 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -6,12 +6,11 @@ codecov: branch: master notify: - require_ci_to_pass: no + require_ci_to_pass: yes # Less spammy. Only notify on passing builds. ignore: - - ^*.py$ - - doc/.* - - tests/.* + - ^*.py$ # python files at the repo root, ie, setup.py + - doc/.* # ignore any code under doc/ coverage: round: up @@ -20,30 +19,61 @@ coverage: status: project: # measuring the overall project coverage - default: + default: false # disable the default status that measures entire project + salt: # declare a new status context "salt" enabled: yes # must be yes|true to enable this status - if_no_uploads: error # will post commit status of "error" if no coverage reports we uploaded + paths: "!tests/" # remove all files in "tests/" + target: auto # will use the coverage from the base commit (pull request base or parent commit) coverage to compare against. + base: auto # will use the pull request base if the commit is on a pull request. If not, the parent commit will be used. + if_no_uploads: error # will post commit status of "error" if no coverage reports were uploaded # options: success, error, failure if_not_found: success # if parent is not found report status as success, error, or failure - if_ci_failed: success # if ci fails report status as success, error, or failure + if_ci_failed: error # if ci fails report status as success, error, or failure + tests: # declare a new status context "tests" + enabled: yes # must be yes|true to enable this status + #target: 100% # we always want 100% coverage here + target: auto # auto while we get this going + base: auto # will use the pull request base if the commit is on a pull request. If not, the parent commit will be used. + paths: "!salt/" # only include coverage in "tests/" folder + if_no_uploads: error # will post commit status of "error" if no coverage reports were uploaded + # options: success, error, failure + if_not_found: success # if parent is not found report status as success, error, or failure + if_ci_failed: error # if ci fails report status as success, error, or failure patch: # pull requests only: this commit status will measure the # entire pull requests Coverage Diff. Checking if the lines # adjusted are covered at least X%. default: - enabled: no # must be yes|true to enable this status - target: 80% # specify the target "X%" coverage to hit - if_no_uploads: error # will post commit status of "error" if no coverage reports we uploaded - # options: success, error, failure + enabled: yes # must be yes|true to enable this status + target: 100% # Newly added lines must have 100% coverage + if_no_uploads: error # will post commit status of "error" if no coverage reports were uploaded + # options: success, error, failure if_not_found: success - if_ci_failed: success + if_ci_failed: error changes: # if there are any unexpected changes in coverage default: - enabled: no # must be yes|true to enable this status - if_no_uploads: success + enabled: yes # must be yes|true to enable this status + if_no_uploads: error if_not_found: success - if_ci_failed: success + if_ci_failed: error + +flags: + salt: + paths: + - salt/ + tests: + paths: + - tests/ + +comment: + layout: "reach, diff, flags, files" +# after_n_builds: 46 # Only comment on PRs after N builds +# # This value is the output of: +# # sh -c 'echo "$(ls .ci/ | grep kitchen | wc -l)"' -# No commends because we're not yet running the full test suite on PRs -comment: off + behavior: new # Comment posting behaviour + # default: update, if exists. Otherwise post new. + # once: update, if exists. Otherwise post new. Skip if deleted. + # new: delete old and post new. + # spammy: post new (do not delete old comments). diff --git a/.coveragerc b/.coveragerc index 7ae232c1f372..f31d4bf09d47 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,14 +1,11 @@ [run] branch = True cover_pylib = False -source = - salt parallel = True concurrency = multiprocessing omit = - tests/*.py setup.py - salt/daemons/test/* + .nox/* [report] # Regexes for lines to exclude from consideration @@ -30,7 +27,3 @@ exclude_lines = ignore_errors = True - -[paths] -source = - salt diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 33d58a2f77ad..cf12c8b8d145 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,72 +8,58 @@ # This file uses an fnmatch-style matching pattern. -# Team Boto -salt/*/*boto* @saltstack/team-boto - # Team Core -requirements/* @saltstack/team-core -rfcs/* @saltstack/team-core -salt/auth/* @saltstack/team-core -salt/cache/* @saltstack/team-core -salt/cli/* @saltstack/team-core -salt/client/* @saltstack/team-core -salt/config/* @saltstack/team-core -salt/daemons/* @saltstack/team-core -salt/pillar/* @saltstack/team-core -salt/loader.py @saltstack/team-core -salt/payload.py @saltstack/team-core -salt/master.py @saltstack/team-core -salt/*/master* @saltstack/team-core -salt/minion.py @saltstack/team-core -salt/*/minion* @saltstack/team-core +* @saltstack/team-core + +# Team Boto +salt/*/*boto* @saltstack/team-boto @saltstack/team-core # Team Cloud -salt/cloud/* @saltstack/team-cloud -salt/utils/openstack/* @saltstack/team-cloud -salt/utils/aws.py @saltstack/team-cloud -salt/*/*cloud* @saltstack/team-cloud +salt/cloud/* @saltstack/team-cloud @saltstack/team-core +salt/utils/openstack/* @saltstack/team-cloud @saltstack/team-core +salt/utils/aws.py @saltstack/team-cloud @saltstack/team-core +salt/*/*cloud* @saltstack/team-cloud @saltstack/team-core # Team NetAPI -salt/cli/api.py @saltstack/team-netapi -salt/client/netapi.py @saltstack/team-netapi -salt/netapi/* @saltstack/team-netapi +salt/cli/api.py @saltstack/team-netapi @saltstack/team-core +salt/client/netapi.py @saltstack/team-netapi @saltstack/team-core +salt/netapi/* @saltstack/team-netapi @saltstack/team-core # Team Network -salt/proxy/* @saltstack/team-proxy +salt/proxy/* @saltstack/team-proxy @saltstack/team-core # Team SPM -salt/cli/spm.py @saltstack/team-spm -salt/spm/* @saltstack/team-spm +salt/cli/spm.py @saltstack/team-spm @saltstack/team-core +salt/spm/* @saltstack/team-spm @saltstack/team-core # 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 +salt/cli/ssh.py @saltstack/team-ssh @saltstack/team-core +salt/client/ssh/* @saltstack/team-ssh @saltstack/team-core +salt/roster/* @saltstack/team-ssh @saltstack/team-core +salt/runners/ssh.py @saltstack/team-ssh @saltstack/team-core +salt/*/thin.py @saltstack/team-ssh @saltstack/team-core # Team State -salt/state.py @saltstack/team-state +salt/state.py @saltstack/team-state @saltstack/team-core # Team SUSE -salt/*/*btrfs* @saltstack/team-suse -salt/*/*kubernetes* @saltstack/team-suse -salt/*/*pkg* @saltstack/team-suse -salt/*/*snapper* @saltstack/team-suse -salt/*/*xfs* @saltstack/team-suse -salt/*/*zypper* @saltstack/team-suse +salt/*/*btrfs* @saltstack/team-suse @saltstack/team-core +salt/*/*kubernetes* @saltstack/team-suse @saltstack/team-core +salt/*/*pkg* @saltstack/team-suse @saltstack/team-core +salt/*/*snapper* @saltstack/team-suse @saltstack/team-core +salt/*/*xfs* @saltstack/team-suse @saltstack/team-core +salt/*/*zypper* @saltstack/team-suse @saltstack/team-core # Team Transport -salt/transport/* @saltstack/team-transport -salt/utils/zeromq.py @saltstack/team-transport +salt/transport/* @saltstack/team-transport @saltstack/team-core +salt/utils/zeromq.py @saltstack/team-transport @saltstack/team-core # Team Windows -salt/*/*win* @saltstack/team-windows -salt/modules/reg.py @saltstack/team-windows -salt/states/reg.py @saltstack/team-windows -tests/*/*win* @saltstack/team-windows -tests/*/test_reg.py @saltstack/team-windows +salt/*/*win* @saltstack/team-windows @saltstack/team-core +salt/modules/reg.py @saltstack/team-windows @saltstack/team-core +salt/states/reg.py @saltstack/team-windows @saltstack/team-core +tests/*/*win* @saltstack/team-windows @saltstack/team-core +tests/*/test_reg.py @saltstack/team-windows @saltstack/team-core # Jenkins Integration .ci/* @saltstack/saltstack-sre-team @saltstack/team-core diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ff255b5aecf8..4849806867f7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -9,6 +9,8 @@ Remove this section if not relevant Remove this section if not relevant ### Tests written? +**[NOTICE] Bug fixes or features added to Salt require tests.** +Please review the [test documentation](https://docs.saltstack.com/en/latest/topics/tutorials/writing_tests.html) for details on how to implement tests into Salt's test suite. Yes/No diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 014c08ace473..9a12175cf670 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,26 +6,24 @@ repos: - id: pip-tools-compile alias: compile-linux-py2.7-zmq-requirements name: Linux Py2.7 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/(.*)\.in)$ - exclude: ^requirements/static/(lint|cloud|docs|osx|windows)\.in$ + files: ^requirements/((base|zeromq|pytest)\.txt|static/linux\.in)$ + exclude: ^requirements/static/(lint|cloud|docs|darwin|windows)\.in$ args: - -v - --py-version=2.7 - --platform=linux - - --out-prefix=zeromq - --include=requirements/base.txt - --include=requirements/zeromq.txt - --include=requirements/pytest.txt - --remove-line=^pycrypto==(.*)$ - id: pip-tools-compile - alias: compile-osx-py2.7-zmq-requirements - name: OSX Py2.7 ZeroMQ Requirements - files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/osx\.in))$ + alias: compile-darwin-py2.7-zmq-requirements + name: Darwin Py2.7 ZeroMQ Requirements + files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/darwin\.in))$ args: - -v - --py-version=2.7 - --platform=darwin - - --out-prefix=zeromq - --include=pkg/osx/req.txt - --include=pkg/osx/req_ext.txt - --include=requirements/base.txt @@ -41,7 +39,6 @@ repos: - -v - --py-version=2.7 - --platform=windows - - --out-prefix=zeromq - --include=pkg/windows/req.txt - --include=pkg/windows/req_win.txt - --include=requirements/base.txt @@ -66,17 +63,44 @@ repos: - -v - --py-version=2.7 + - id: pip-tools-compile + alias: compile-linux-crypto-py2.7-requirements + name: Linux Py2.7 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=2.7 + - --platform=linux + - --out-prefix=linux + - id: pip-tools-compile + alias: compile-darwin-crypto-py2.7-requirements + name: Darwin Py2.7 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=2.7 + - --platform=darwin + - --out-prefix=darwin + - id: pip-tools-compile + alias: compile-windows-crypto-py2.7-requirements + name: Windows Py2.7 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=2.7 + - --platform=windows + - --out-prefix=windows + - id: pip-tools-compile alias: compile-linux-py3.4-zmq-requirements name: Linux Py3.4 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/(.*)\.in)$ - exclude: ^requirements/static/(centos-6|amzn-2018\.03|lint|cloud|docs|osx|windows)\.in$ + files: ^requirements/((base|zeromq|pytest)\.txt|static/linux\.in)$ + exclude: ^requirements/static/(centos-6|amzn-2018\.03|lint|cloud|docs|darwin|windows)\.in$ args: - -v - --py-version=3.4 - --platform=linux - - --out-prefix=zeromq - --include=requirements/base.txt - --include=requirements/zeromq.txt - --include=requirements/pytest.txt @@ -90,29 +114,37 @@ repos: - -v - --py-version=3.4 + - id: pip-tools-compile + alias: compile-linux-crypto-py3.4-requirements + name: Linux Py3.4 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=3.4 + - --platform=linux + - --out-prefix=linux + - id: pip-tools-compile alias: compile-linux-py3.5-zmq-requirements name: Linux Py3.5 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/(.*)\.in)$ - exclude: ^requirements/static/(centos-6|amzn-2018\.03|lint|cloud|docs|osx|windows)\.in$ + files: ^requirements/((base|zeromq|pytest)\.txt|static/linux\.in)$ + exclude: ^requirements/static/(centos-6|amzn-2018\.03|lint|cloud|docs|darwin|windows)\.in$ args: - -v - --py-version=3.5 - --platform=linux - - --out-prefix=zeromq - --include=requirements/base.txt - --include=requirements/zeromq.txt - --include=requirements/pytest.txt - --remove-line=^pycrypto==(.*)$ - id: pip-tools-compile - alias: compile-osx-py3.5-zmq-requirements - name: OSX Py3.5 ZeroMQ Requirements - files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/osx\.in))$ + alias: compile-darwin-py3.5-zmq-requirements + name: Darwin Py3.5 ZeroMQ Requirements + files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/darwin\.in))$ args: - -v - --py-version=3.5 - --platform=darwin - - --out-prefix=zeromq - --include=pkg/osx/req.txt - --include=pkg/osx/req_ext.txt - --include=requirements/base.txt @@ -128,7 +160,6 @@ repos: - -v - --py-version=3.5 - --platform=windows - - --out-prefix=zeromq - --include=pkg/windows/req.txt - --include=pkg/windows/req_win.txt - --include=requirements/base.txt @@ -153,29 +184,55 @@ repos: - --py-version=3.5 - --platform=linux + - id: pip-tools-compile + alias: compile-linux-crypto-py3.5-requirements + name: Linux Py3.5 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=3.5 + - --platform=linux + - --out-prefix=linux + - id: pip-tools-compile + alias: compile-darwin-crypto-py3.5-requirements + name: Darwin Py3.5 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=3.5 + - --platform=darwin + - --out-prefix=darwin + - id: pip-tools-compile + alias: compile-windows-crypto-py3.5-requirements + name: Windows Py3.5 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=3.5 + - --platform=windows + - --out-prefix=windows + - id: pip-tools-compile alias: compile-linux-py3.6-zmq-requirements name: Linux Py3.6 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/(.*)\.in)$ - exclude: ^requirements/static/(centos-6|amzn-2018\.03|lint|cloud|docs|osx|windows)\.in$ + files: ^requirements/((base|zeromq|pytest)\.txt|static/linux\.in)$ + exclude: ^requirements/static/(centos-6|amzn-2018\.03|lint|cloud|docs|darwin|windows)\.in$ args: - -v - --py-version=3.6 - --platform=linux - - --out-prefix=zeromq - --include=requirements/base.txt - --include=requirements/zeromq.txt - --include=requirements/pytest.txt - --remove-line=^pycrypto==(.*)$ - id: pip-tools-compile - alias: compile-osx-py3.6-zmq-requirements - name: OSX Py3.6 ZeroMQ Requirements - files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/osx\.in))$ + alias: compile-darwin-py3.6-zmq-requirements + name: Darwin Py3.6 ZeroMQ Requirements + files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/darwin\.in))$ args: - -v - --py-version=3.6 - --platform=darwin - - --out-prefix=zeromq - --include=pkg/osx/req.txt - --include=pkg/osx/req_ext.txt - --include=requirements/base.txt @@ -191,7 +248,6 @@ repos: - -v - --py-version=3.6 - --platform=windows - - --out-prefix=zeromq - --include=pkg/windows/req.txt - --include=pkg/windows/req_win.txt - --include=requirements/base.txt @@ -216,29 +272,55 @@ repos: - --py-version=3.6 - --platform=linux + - id: pip-tools-compile + alias: compile-linux-crypto-py3.6-requirements + name: Linux Py3.6 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=3.6 + - --platform=linux + - --out-prefix=linux + - id: pip-tools-compile + alias: compile-darwin-crypto-py3.6-requirements + name: Darwin Py3.6 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=3.6 + - --platform=darwin + - --out-prefix=darwin + - id: pip-tools-compile + alias: compile-windows-crypto-py3.6-requirements + name: Windows Py3.6 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=3.6 + - --platform=windows + - --out-prefix=windows + - id: pip-tools-compile alias: compile-linux-py3.7-zmq-requirements name: Linux Py3.7 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/(.*)\.in)$ - exclude: ^requirements/static/(centos-6|amzn-2018\.03|lint|cloud|docs|osx|windows)\.in$ + files: ^requirements/((base|zeromq|pytest)\.txt|static/linux\.in)$ + exclude: ^requirements/static/(centos-6|amzn-2018\.03|lint|cloud|docs|darwin|windows)\.in$ args: - -v - --py-version=3.7 - --platform=linux - - --out-prefix=zeromq - --include=requirements/base.txt - --include=requirements/zeromq.txt - --include=requirements/pytest.txt - --remove-line=^pycrypto==(.*)$ - id: pip-tools-compile - alias: compile-osx-py3.7-zmq-requirements - name: OSX Py3.7 ZeroMQ Requirements - files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/osx\.in))$ + alias: compile-darwin-py3.7-zmq-requirements + name: Darwin Py3.7 ZeroMQ Requirements + files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/darwin\.in))$ args: - -v - --py-version=3.7 - --platform=darwin - - --out-prefix=zeromq - --include=pkg/osx/req.txt - --include=pkg/osx/req_ext.txt - --include=requirements/base.txt @@ -254,7 +336,6 @@ repos: - -v - --py-version=3.7 - --platform=windows - - --out-prefix=zeromq - --include=pkg/windows/req.txt - --include=pkg/windows/req_win.txt - --include=requirements/base.txt @@ -278,3 +359,56 @@ repos: - -v - --py-version=3.7 - --platform=linux + + - id: pip-tools-compile + alias: compile-linux-crypto-py3.7-requirements + name: Linux Py3.7 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=3.7 + - --platform=linux + - --out-prefix=linux + - id: pip-tools-compile + alias: compile-darwin-crypto-py3.7-requirements + name: Darwin Py3.7 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=3.7 + - --platform=darwin + - --out-prefix=darwin + - id: pip-tools-compile + alias: compile-windows-crypto-py3.7-requirements + name: Windows Py3.7 Crypto Requirements + files: ^requirements/(crypto\.txt|static/crypto\.in)$ + args: + - -v + - --py-version=3.7 + - --platform=windows + - --out-prefix=windows + + + - repo: https://github.com/saltstack/salt-nox-pre-commit + rev: master + hooks: + - id: nox-py2 + alias: lint-salt + name: Lint Salt + files: ^((setup|noxfile)|salt/.*)\.py$ + args: + - -e + - lint-salt + - -- + + - repo: https://github.com/saltstack/salt-nox-pre-commit + rev: master + hooks: + - id: nox-py2 + alias: lint-tests + name: Lint Tests + files: ^tests/.*\.py$ + args: + - -e + - lint-tests + - -- diff --git a/.pylintrc b/.pylintrc index ea2705cb9988..60b87d20bee5 100644 --- a/.pylintrc +++ b/.pylintrc @@ -26,7 +26,8 @@ load-plugins=saltpylint.pep8, saltpylint.smartup, saltpylint.minpyver, saltpylint.blacklist, - saltpylint.thirdparty + saltpylint.thirdparty, + saltpylint.dunder_del # Use multiple processes to speed up Pylint. # Don't bump this values on PyLint 1.4.0 - Know bug that ignores the passed --rcfile diff --git a/.testing.pylintrc b/.testing.pylintrc index 86237fd582ed..99b7d80a7abc 100644 --- a/.testing.pylintrc +++ b/.testing.pylintrc @@ -23,7 +23,8 @@ load-plugins=saltpylint.pep8, saltpylint.smartup, saltpylint.minpyver, saltpylint.blacklist, - saltpylint.thirdparty + saltpylint.thirdparty, + saltpylint.dunder_del # Use multiple processes to speed up Pylint. # Don't bump this values on PyLint 1.4.0 - Know bug that ignores the passed --rcfile diff --git a/AUTHORS b/AUTHORS index 72d424f72f19..917bd8f5eae3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -84,7 +84,7 @@ Maxim Burgerhout Mickey Malone Michael Steed Mike Place -Mircea Ulinic +Mircea Ulinic Mitch Anderson Mostafa Hussein Nathaniel Whiteinge diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000000..b8ca498675e3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,63 @@ +# Changelog +All notable changes to Salt will be documented in this file. + +This changelog follows [keepachangelog](https://keepachangelog.com/en/1.0.0/) format, and is intended for human consumption. + +This project versioning is *similar* to [Semantic Versioning](https://semver.org), and is documented in [SEP 14](https://github.com/saltstack/salt-enhancement-proposals/pull/20/files). +Versions are `MAJOR.PATCH`. + +## Unreleased (Neon) + +### Removed + +- [#54943](https://github.com/saltstack/salt/pull/54943) - RAET transport method has been removed per the deprecation schedule - [@s0undt3ch](https://github.com/s0undt3ch) + +### Deprecated +- [#55552](https://github.com/saltstack/salt/pull/55552) - The config options `hgfs_env_whitelist`, `hgfs_env_blacklist`, `svnfs_env_whitelist`, and `svnfs_env_whitelist` have been deprecated in favor of `hgfs_saltenv_whitelist`, `hgfs_saltenv_blacklist`, `svnfs_saltenv_whitelist`, `svnfs_saltenv_blacklist`. + +- [#55609](https://github.com/saltstack/salt/pull/55609) - Remove smartos grains `hypervisor_uuid` and `datacenter` in favor of `mdata:sdc:server_uuid` and `mdata:sdc:datacenter_name`. +- [#55539](https://github.com/saltstack/salt/pull/55539) - Deprecate salt.auth.Authorize class and the any_auth method + + +### Changed + +- [SEP 14](https://github.com/saltstack/salt-enhancement-proposals/pull/20) - Changed to numeric versions. +- [SEP 1](https://github.com/saltstack/salt-enhancement-proposals/blob/master/accepted/0001-changelog-format.md), [SEP 14](https://github.com/saltstack/salt-enhancement-proposals/pull/20) - Adopted keepachangelog format. + +### Fixed + +### Added + +- [#54917](https://github.com/saltstack/salt/pull/54917) - Added get_settings, put_settings and flush_synced methods for Elasticsearch module. - [@Oloremo](https://github.com/Oloremo) +- [#55418](https://github.com/saltstack/salt/pull/55418) - Added clean_parent argument for the archive state. - [@Oloremo](https://github.com/Oloremo) +- [#55593](https://github.com/saltstack/salt/issues/55593) - Added a support for a global proxy to pip module. - [@Oloremo](https://github.com/Oloremo) + +--- + +## [2019.2.2] + +### Changed + +- [#54758](https://github.com/saltstack/salt/issues/54758) - Missing sls file during `state.show_states` displays message instead of failing - [@Ch3LL](https://github.com/Ch3LL) + +### Fixed + +- [#54521](https://github.com/saltstack/salt/issues/54521) - `failhard` during orchestration now fails as expected - [@mattp-](https://github.com/mattp-) / [@Oloremo](https://github.com/Oloremo) +- [#54741](https://github.com/saltstack/salt/issues/54741) - `schedule.run_job` without time element now works as expected - [@garethgreenaway](https://github.com/garethgreenaway) +- [#54755](https://github.com/saltstack/salt/issues/54755) - Pip state ensures pip was imported before trying to remove - [@dwoz](https://github.com/dwoz) +- [#54760](https://github.com/saltstack/salt/issues/54760) - Fix `salt-cloud -Q` for OpenStack driver - [@vdloo](https://github.com/vdloo) / [@Akm0d](https://github.com/Akm0d) +- [#54762](https://github.com/saltstack/salt/issues/54762) - IPv6 addresses with brackets no longer break master/minion communication - [@dhiltonp](https://github.com/dhiltonp) +- [#54765](https://github.com/saltstack/salt/issues/54765) - Masterless jinja imports - [@dwoz](https://github.com/dwoz) +- [#54776](https://github.com/saltstack/salt/issues/54776) - `ping_interval` in minion config no longer prevents startup - [@dwoz](https://github.com/dwoz) +- [#54820](https://github.com/saltstack/salt/issues/54820) - `scheduler.present` no longer always reports changes when scheduler is disabled - [@garethgreenaway](https://github.com/garethgreenaway) +- [#54941](https://github.com/saltstack/salt/issues/54941) - Pillar data is no longer refreshed on every call - [@dwoz](https://github.com/dwoz) + + +### Added + +- [#54919](https://github.com/saltstack/salt/pull/54919) - Added missing `win_wusa` state and module docs - [@twangboy](https://github.com/twangboy) + +## [2019.2.1] - 2019-09-25 [YANKED] + + +- See [old release notes](https://docs.saltstack.com/en/latest/topics/releases/2019.2.1.html) diff --git a/MANIFEST.in b/MANIFEST.in index 2cc0c36f05d7..26e3067ecab0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,11 +1,15 @@ include AUTHORS +include CODE_OF_CONDUCT.md +include CONTRIBUTING.rst include HACKING.rst include LICENSE +include NOTICE include README.rst include requirements/base.txt include requirements/raet.txt include requirements/cloud.txt include requirements/zeromq.txt +include SUPPORT.rst include tests/*.py recursive-include tests * include tests/integration/modules/files/* @@ -13,9 +17,10 @@ include tests/integration/files/* include tests/unit/templates/files/* recursive-include doc * recursive-include scripts * -include conf/* +recursive-include conf * recursive-include pkg * recursive-include salt *.jinja recursive-include salt *.flo +recursive-include templates * include salt/templates/git/* include salt/templates/lxc/* diff --git a/README.rst b/README.rst index 0f05649a96c5..d4e116c8c617 100644 --- a/README.rst +++ b/README.rst @@ -60,7 +60,6 @@ services`_ offerings. * Facebook - ``_ * LinkedIn - ``_ * LinkedIn Group - ``_ -* Google+ - ``_ .. _global community: http://www.meetup.com/pro/saltstack/ .. _SaltConf: http://saltconf.com/ diff --git a/conf/master b/conf/master index 546db5cb28df..5179f42ebc5a 100644 --- a/conf/master +++ b/conf/master @@ -27,6 +27,9 @@ # modified files cause conflicts, set verify_env to False. #user: root +# Tell the master to also use salt-ssh when running commands against minions. +#enable_ssh_minions: False + # The port used by the communication interface. The ret (return) port is the # interface used for the file server, authentication, job returns, etc. #ret_port: 4506 diff --git a/conf/minion b/conf/minion index f2b66559321e..8bc64e61f53a 100644 --- a/conf/minion +++ b/conf/minion @@ -391,7 +391,7 @@ #mine_interval: 60 # Windows platforms lack posix IPC and must rely on slower TCP based inter- -# process communications. Set ipc_mode to 'tcp' on such systems +# process communications. ipc_mode is set to 'tcp' on such systems. #ipc_mode: ipc # Overwrite the default tcp ports used by the minion when ipc_mode is set to 'tcp' diff --git a/doc/.scripts/compile-translation-catalogs b/doc/.scripts/compile-translation-catalogs deleted file mode 100755 index 181462e9a840..000000000000 --- a/doc/.scripts/compile-translation-catalogs +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -''' - :codeauthor: Pedro Algarvio (pedro@algarvio.me) - - - compile-translation-catalogs - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Compile the existing translation catalogs. -''' - -# Import python libs -import os -import sys -import fnmatch - -# Import 3rd-party libs -HAS_BABEL = False -try: - from babel.messages import mofile, pofile - HAS_BABEL = True -except ImportError: - try: - import polib - except ImportError: - print( - 'You need to install either babel or pofile in order to compile ' - 'the message catalogs. One of:\n' - ' pip install babel\n' - ' pip install polib' - ) - sys.exit(1) - -DOC_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) -LOCALES_DIR = os.path.join(DOC_DIR, 'locale') - - -def main(): - ''' - Run the compile code - ''' - - print('Gathering the translation catalogs to compile...'), - sys.stdout.flush() - entries = {} - for locale in os.listdir(os.path.join(LOCALES_DIR)): - if locale == 'pot': - continue - - locale_path = os.path.join(LOCALES_DIR, locale) - entries[locale] = [] - - for dirpath, _, filenames in os.walk(locale_path): - for filename in fnmatch.filter(filenames, '*.po'): - entries[locale].append(os.path.join(dirpath, filename)) - print('DONE') - - for locale, po_files in sorted(entries.items()): - lc_messages_path = os.path.join(LOCALES_DIR, locale, 'LC_MESSAGES') - print('\nCompiling the \'{0}\' locale:'.format(locale)) - for po_file in sorted(po_files): - relpath = os.path.relpath(po_file, lc_messages_path) - print ' {0}.po -> {0}.mo'.format(relpath.split('.po', 1)[0]) - if HAS_BABEL: - catalog = pofile.read_po(open(po_file)) - mofile.write_mo( - open(po_file.replace('.po', '.mo'), 'wb'), catalog - ) - continue - - catalog = polib.pofile(po_file) - catalog.save_as_mofile(fpath=po_file.replace('.po', '.mo')) - - print('Done') - - -if __name__ == '__main__': - main() diff --git a/doc/.scripts/download-translation-catalog b/doc/.scripts/download-translation-catalog deleted file mode 100755 index 81bdf1822ad0..000000000000 --- a/doc/.scripts/download-translation-catalog +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -''' - :codeauthor: Pedro Algarvio (pedro@algarvio.me) - - - download-translation-catalog - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Download a translation catalog from Transifex. -''' - -# Import python libs -import os -import sys - -# Import 3rd-party libs -try: - import txclib.utils -except ImportError: - print( - 'The \'transifex-client\' library needs to be installed. ' - 'Please execute one of \'pip install transifex-client\' or ' - '\'easy_install transifex-client\'' - ) - sys.exit(1) - -DOC_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) -LOCALES_DIR = os.path.join(DOC_DIR, 'locale') - - -def main(): - ''' - Run the compile code - ''' - - os.chdir(DOC_DIR) - tx_root = txclib.utils.find_dot_tx() - - if len(sys.argv) < 2: - print('You need to pass a locale to this script. For example: ' - 'pt_PT, zh_CN, ru, etc...') - sys.exit(1) - - for locale in sys.argv[1:]: - print('Download \'{0}\' translations catalog...'.format(locale)) - txclib.utils.exec_command('pull', ['-l', locale], tx_root) - - print('Done') - - -if __name__ == '__main__': - main() diff --git a/doc/.scripts/update-transifex-source-translations b/doc/.scripts/update-transifex-source-translations deleted file mode 100755 index b82363397efc..000000000000 --- a/doc/.scripts/update-transifex-source-translations +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -''' - :codeauthor: Pedro Algarvio (pedro@algarvio.me) - - - update-transifex-source-translations - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Update the transifex sources configuration file and push the source -''' - -# Import python libs -import os -import sys -import time -import logging -import subprocess -import ConfigParser - -try: - import txclib.utils -except ImportError: - sys.stdout.write( - 'The \'transifex-client\' library needs to be installed. ' - 'Please execute one of \'pip install transifex-client\' or ' - '\'easy_install transifex-client\'\n' - ) - sys.exit(1) - -DOC_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) - - -def main(): - ''' - Run the update code - ''' - os.chdir(DOC_DIR) - - sys.stdout.write('Extracting translatable strings....\n') - try: - subprocess.check_call(['make', 'gettext']) - except subprocess.CalledProcessError as exc: - sys.stdout.write('An error occurred while extracting the translation ' - 'strings: {0}\n'.format(exc)) - sys.exit(1) - - locale_dir = os.path.join(DOC_DIR, 'locale') - pot_dir = os.path.join(DOC_DIR, '_build', 'locale') - tx_root = txclib.utils.find_dot_tx() - tx_config = os.path.join(tx_root, '.tx', 'config') - - if not tx_root: - sys.stdout.write( - 'Unable to find the \'.tx/\' directory. Unable to continue\n' - ) - sys.exit(1) - - # We do not want the txclib INFO or WARNING logging - logging.getLogger('txclib').setLevel(logging.ERROR) - - sys.stdout.write('Gathering the translation template files...') - sys.stdout.flush() - entries = [] - for dirpath, dirnames, filenames in os.walk(pot_dir): - for filename in filenames: - pot_file = os.path.join(dirpath, filename) - base, ext = os.path.splitext(pot_file) - if ext != '.pot': - continue - resource_path = os.path.relpath(base, pot_dir) - try: - import babel.messages.pofile - if not len(babel.messages.pofile.read_po(open(pot_file))): - # Empty pot file, continue - continue - except ImportError: - # No babel package, let's keep on going - pass - - resource_name = resource_path.replace( - '\\', '/').replace('/', '--').replace('.', '_') - entries.append((resource_path, resource_name)) - sys.stdout.write('Done\n') - - # Let's load the resources already present in the configuration file - cfg = ConfigParser.SafeConfigParser() - cfg.read([tx_config]) - handled_resources = set( - section for section in - cfg.sections() if section.startswith('salt.') - ) - - sys.stdout.write('Updating the entries in \'.tx/config\'...\n') - sys.stdout.flush() - total_entries = len(entries) - for idx, (resource_path, resource_name) in enumerate(sorted(entries)): - sys.stdout.write( - '[{0:>{pad}}/{1}] Updating resource for ' - '{resource_path}.pot ({resource_name})'.format( - idx + 1, - total_entries, - pad=len(str(total_entries)), - locale_dir=locale_dir, - resource_name=resource_name, - resource_path=resource_path - ) - ) - sys.stdout.flush() - try: - txclib.utils.exec_command( - 'set', - '--auto-local -r salt.{resource_name} ' - '{locale_dir}//LC_MESSAGES/{resource_path}.po ' - '--source-lang en ' - '--source-file {pot_dir}/{resource_path}.pot ' - '--source-name {resource_path}.rst ' - '--execute'.format( - resource_name=resource_name, - resource_path=resource_path, - locale_dir=locale_dir, - pot_dir=pot_dir.rstrip('/') - ).split(), - tx_root - ) - sys.stdout.write('\n') - if 'salt.{0}'.format(resource_name) in handled_resources: - handled_resources.remove('salt.{0}'.format(resource_name)) - except Exception as err: - sys.stdout.write('An error occurred: {0}\n'.format(err)) - except KeyboardInterrupt: - sys.stdout.write('\n') - sys.exit(1) - time.sleep(0.025) - - if handled_resources: - non_handled_resources = len(handled_resources) - sys.stdout.write( - 'Removing old resources from configuration and upstream' - '(if possible)\n' - ) - for idx, resource_name in enumerate(sorted(handled_resources)): - sys.stdout.write( - '[{0:>{pad}}/{1}] Removing resource \'{resource_name}\''.format( - idx + 1, - non_handled_resources, - pad=len(str(non_handled_resources)), - resource_name=resource_name, - ) - ) - sys.stdout.flush() - try: - txclib.utils.exec_command( - 'delete', - ['-r', resource_name], - tx_root - ) - handled_resources.remove(resource_name) - except Exception as err: - sys.stdout.write('An error occurred: {0}\n'.format(err)) - finally: - if cfg.has_section(resource_name): - cfg.remove_section(resource_name) - sys.stdout.write('\n') - time.sleep(0.025) - cfg.write(open(tx_config, 'w')) - sys.stdout.write('\n') - - # Set the translations file type we're using - txclib.utils.exec_command('set', ['-t', 'PO'], tx_root) - time.sleep(0.025) - - if 'TRANSIFEX_NO_PUSH' not in os.environ: - - sys.stdout.write('\n') - sys.stdout.write('Pushing translation template files...\n') - for idx, (resource_path, resource_name) in enumerate(sorted(entries)): - sys.stdout.write( - '[{0:>{pad}}/{1}] Pushing resource for ' - '{resource_path}.pot ({resource_name})'.format( - idx + 1, - total_entries, - pad=len(str(total_entries)), - locale_dir=locale_dir, - resource_name=resource_name, - resource_path=resource_path - ) - ) - sys.stdout.flush() - try: - txclib.utils.exec_command( - 'push', - '--resource salt.{resource_name} ' - '--source ' - '--skip ' - '--no-interactive'.format( - resource_name=resource_name, - resource_path=resource_path, - locale_dir=locale_dir - ).split(), - tx_root - ) - sys.stdout.write('\n') - except Exception as err: - sys.stdout.write('An error occurred: {0}\n'.format(err)) - except KeyboardInterrupt: - sys.stdout.write('\n') - sys.exit(1) - time.sleep(0.025) - - if handled_resources: - sys.stdout.write('=' * 80) - sys.stdout.write( - '\nDon\'t forget to delete the following remote resources:\n') - for resource_name in sorted(handled_resources): - sys.stdout.write(' {0}\n'.format(resource_name)) - sys.stdout.write('=' * 80) - - sys.stdout.write('\nDONE\n') - - -if __name__ == '__main__': - main() diff --git a/doc/.tx/config b/doc/.tx/config deleted file mode 100644 index f766c4a9100e..000000000000 --- a/doc/.tx/config +++ /dev/null @@ -1,4834 +0,0 @@ -[main] -host = https://www.transifex.com -type = PO - -[salt.contents] -file_filter = locale//LC_MESSAGES/contents.po -source_file = _build/locale/contents.pot -source_lang = en -source_name = contents.rst - -[salt.faq] -file_filter = locale//LC_MESSAGES/faq.po -source_file = _build/locale/faq.pot -source_lang = en -source_name = faq.rst - -[salt.index] -file_filter = locale//LC_MESSAGES/index.po -source_file = _build/locale/index.pot -source_lang = en -source_name = index.rst - -[salt.ref--auth--all--index] -file_filter = locale//LC_MESSAGES/ref/auth/all/index.po -source_file = _build/locale/ref/auth/all/index.pot -source_lang = en -source_name = ref/auth/all/index.rst - -[salt.ref--auth--all--salt_auth_keystone] -file_filter = locale//LC_MESSAGES/ref/auth/all/salt.auth.keystone.po -source_file = _build/locale/ref/auth/all/salt.auth.keystone.pot -source_lang = en -source_name = ref/auth/all/salt.auth.keystone.rst - -[salt.ref--auth--all--salt_auth_ldap] -file_filter = locale//LC_MESSAGES/ref/auth/all/salt.auth.ldap.po -source_file = _build/locale/ref/auth/all/salt.auth.ldap.pot -source_lang = en -source_name = ref/auth/all/salt.auth.ldap.rst - -[salt.ref--auth--all--salt_auth_pam] -file_filter = locale//LC_MESSAGES/ref/auth/all/salt.auth.pam.po -source_file = _build/locale/ref/auth/all/salt.auth.pam.pot -source_lang = en -source_name = ref/auth/all/salt.auth.pam.rst - -[salt.ref--auth--all--salt_auth_stormpath_mod] -file_filter = locale//LC_MESSAGES/ref/auth/all/salt.auth.stormpath_mod.po -source_file = _build/locale/ref/auth/all/salt.auth.stormpath_mod.pot -source_lang = en -source_name = ref/auth/all/salt.auth.stormpath_mod.rst - -[salt.ref--cli--index] -file_filter = locale//LC_MESSAGES/ref/cli/index.po -source_file = _build/locale/ref/cli/index.pot -source_lang = en -source_name = ref/cli/index.rst - -[salt.ref--cli--salt] -file_filter = locale//LC_MESSAGES/ref/cli/salt.po -source_file = _build/locale/ref/cli/salt.pot -source_lang = en -source_name = ref/cli/salt.rst - -[salt.ref--cli--salt-call] -file_filter = locale//LC_MESSAGES/ref/cli/salt-call.po -source_file = _build/locale/ref/cli/salt-call.pot -source_lang = en -source_name = ref/cli/salt-call.rst - -[salt.ref--cli--salt-cp] -file_filter = locale//LC_MESSAGES/ref/cli/salt-cp.po -source_file = _build/locale/ref/cli/salt-cp.pot -source_lang = en -source_name = ref/cli/salt-cp.rst - -[salt.ref--cli--salt-key] -file_filter = locale//LC_MESSAGES/ref/cli/salt-key.po -source_file = _build/locale/ref/cli/salt-key.pot -source_lang = en -source_name = ref/cli/salt-key.rst - -[salt.ref--cli--salt-master] -file_filter = locale//LC_MESSAGES/ref/cli/salt-master.po -source_file = _build/locale/ref/cli/salt-master.pot -source_lang = en -source_name = ref/cli/salt-master.rst - -[salt.ref--cli--salt-minion] -file_filter = locale//LC_MESSAGES/ref/cli/salt-minion.po -source_file = _build/locale/ref/cli/salt-minion.pot -source_lang = en -source_name = ref/cli/salt-minion.rst - -[salt.ref--cli--salt-run] -file_filter = locale//LC_MESSAGES/ref/cli/salt-run.po -source_file = _build/locale/ref/cli/salt-run.pot -source_lang = en -source_name = ref/cli/salt-run.rst - -[salt.ref--cli--salt-ssh] -file_filter = locale//LC_MESSAGES/ref/cli/salt-ssh.po -source_file = _build/locale/ref/cli/salt-ssh.pot -source_lang = en -source_name = ref/cli/salt-ssh.rst - -[salt.ref--cli--salt-syndic] -file_filter = locale//LC_MESSAGES/ref/cli/salt-syndic.po -source_file = _build/locale/ref/cli/salt-syndic.pot -source_lang = en -source_name = ref/cli/salt-syndic.rst - -[salt.ref--clientacl] -file_filter = locale//LC_MESSAGES/ref/clientacl.po -source_file = _build/locale/ref/clientacl.pot -source_lang = en -source_name = ref/clientacl.rst - -[salt.ref--clients--index] -file_filter = locale//LC_MESSAGES/ref/clients/index.po -source_file = _build/locale/ref/clients/index.pot -source_lang = en -source_name = ref/clients/index.rst - -[salt.ref--configuration--examples] -file_filter = locale//LC_MESSAGES/ref/configuration/examples.po -source_file = _build/locale/ref/configuration/examples.pot -source_lang = en -source_name = ref/configuration/examples.rst - -[salt.ref--configuration--logging--handlers--index] -file_filter = locale//LC_MESSAGES/ref/configuration/logging/handlers/index.po -source_file = _build/locale/ref/configuration/logging/handlers/index.pot -source_lang = en -source_name = ref/configuration/logging/handlers/index.rst - -[salt.ref--configuration--logging--handlers--salt_log_handlers_logstash_mod] -file_filter = locale//LC_MESSAGES/ref/configuration/logging/handlers/salt.log.handlers.logstash_mod.po -source_file = _build/locale/ref/configuration/logging/handlers/salt.log.handlers.logstash_mod.pot -source_lang = en -source_name = ref/configuration/logging/handlers/salt.log.handlers.logstash_mod.rst - -[salt.ref--configuration--logging--handlers--salt_log_handlers_sentry_mod] -file_filter = locale//LC_MESSAGES/ref/configuration/logging/handlers/salt.log.handlers.sentry_mod.po -source_file = _build/locale/ref/configuration/logging/handlers/salt.log.handlers.sentry_mod.pot -source_lang = en -source_name = ref/configuration/logging/handlers/salt.log.handlers.sentry_mod.rst - -[salt.ref--configuration--logging--index] -file_filter = locale//LC_MESSAGES/ref/configuration/logging/index.po -source_file = _build/locale/ref/configuration/logging/index.pot -source_lang = en -source_name = ref/configuration/logging/index.rst - -[salt.ref--configuration--master] -file_filter = locale//LC_MESSAGES/ref/configuration/master.po -source_file = _build/locale/ref/configuration/master.pot -source_lang = en -source_name = ref/configuration/master.rst - -[salt.ref--configuration--minion] -file_filter = locale//LC_MESSAGES/ref/configuration/minion.po -source_file = _build/locale/ref/configuration/minion.pot -source_lang = en -source_name = ref/configuration/minion.rst - -[salt.ref--file_server--all--index] -file_filter = locale//LC_MESSAGES/ref/file_server/all/index.po -source_file = _build/locale/ref/file_server/all/index.pot -source_lang = en -source_name = ref/file_server/all/index.rst - -[salt.ref--file_server--all--salt_fileserver_gitfs] -file_filter = locale//LC_MESSAGES/ref/file_server/all/salt.fileserver.gitfs.po -source_file = _build/locale/ref/file_server/all/salt.fileserver.gitfs.pot -source_lang = en -source_name = ref/file_server/all/salt.fileserver.gitfs.rst - -[salt.ref--file_server--all--salt_fileserver_hgfs] -file_filter = locale//LC_MESSAGES/ref/file_server/all/salt.fileserver.hgfs.po -source_file = _build/locale/ref/file_server/all/salt.fileserver.hgfs.pot -source_lang = en -source_name = ref/file_server/all/salt.fileserver.hgfs.rst - -[salt.ref--file_server--all--salt_fileserver_roots] -file_filter = locale//LC_MESSAGES/ref/file_server/all/salt.fileserver.roots.po -source_file = _build/locale/ref/file_server/all/salt.fileserver.roots.pot -source_lang = en -source_name = ref/file_server/all/salt.fileserver.roots.rst - -[salt.ref--file_server--all--salt_fileserver_s3fs] -file_filter = locale//LC_MESSAGES/ref/file_server/all/salt.fileserver.s3fs.po -source_file = _build/locale/ref/file_server/all/salt.fileserver.s3fs.pot -source_lang = en -source_name = ref/file_server/all/salt.fileserver.s3fs.rst - -[salt.ref--file_server--backends] -file_filter = locale//LC_MESSAGES/ref/file_server/backends.po -source_file = _build/locale/ref/file_server/backends.pot -source_lang = en -source_name = ref/file_server/backends.rst - -[salt.ref--file_server--dynamic-modules] -file_filter = locale//LC_MESSAGES/ref/file_server/dynamic-modules.po -source_file = _build/locale/ref/file_server/dynamic-modules.pot -source_lang = en -source_name = ref/file_server/dynamic-modules.rst - -[salt.ref--file_server--file_roots] -file_filter = locale//LC_MESSAGES/ref/file_server/file_roots.po -source_file = _build/locale/ref/file_server/file_roots.pot -source_lang = en -source_name = ref/file_server/file_roots.rst - -[salt.ref--file_server--index] -file_filter = locale//LC_MESSAGES/ref/file_server/index.po -source_file = _build/locale/ref/file_server/index.pot -source_lang = en -source_name = ref/file_server/index.rst - -[salt.ref--index] -file_filter = locale//LC_MESSAGES/ref/index.po -source_file = _build/locale/ref/index.pot -source_lang = en -source_name = ref/index.rst - -[salt.ref--internals--exceptions] -file_filter = locale//LC_MESSAGES/ref/internals/exceptions.po -source_file = _build/locale/ref/internals/exceptions.pot -source_lang = en -source_name = ref/internals/exceptions.rst - -[salt.ref--internals--index] -file_filter = locale//LC_MESSAGES/ref/internals/index.po -source_file = _build/locale/ref/internals/index.pot -source_lang = en -source_name = ref/internals/index.rst - -[salt.ref--internals--salt_exceptions] -file_filter = locale//LC_MESSAGES/ref/internals/salt.exceptions.po -source_file = _build/locale/ref/internals/salt.exceptions.pot -source_lang = en -source_name = ref/internals/salt.exceptions.rst - -[salt.ref--modules--all--index] -file_filter = locale//LC_MESSAGES/ref/modules/all/index.po -source_file = _build/locale/ref/modules/all/index.pot -source_lang = en -source_name = ref/modules/all/index.rst - -[salt.ref--modules--all--salt_modules_aliases] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.aliases.po -source_file = _build/locale/ref/modules/all/salt.modules.aliases.pot -source_lang = en -source_name = ref/modules/all/salt.modules.aliases.rst - -[salt.ref--modules--all--salt_modules_alternatives] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.alternatives.po -source_file = _build/locale/ref/modules/all/salt.modules.alternatives.pot -source_lang = en -source_name = ref/modules/all/salt.modules.alternatives.rst - -[salt.ref--modules--all--salt_modules_apache] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.apache.po -source_file = _build/locale/ref/modules/all/salt.modules.apache.pot -source_lang = en -source_name = ref/modules/all/salt.modules.apache.rst - -[salt.ref--modules--all--salt_modules_archive] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.archive.po -source_file = _build/locale/ref/modules/all/salt.modules.archive.pot -source_lang = en -source_name = ref/modules/all/salt.modules.archive.rst - -[salt.ref--modules--all--salt_modules_at] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.at.po -source_file = _build/locale/ref/modules/all/salt.modules.at.pot -source_lang = en -source_name = ref/modules/all/salt.modules.at.rst - -[salt.ref--modules--all--salt_modules_augeas_cfg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.augeas_cfg.po -source_file = _build/locale/ref/modules/all/salt.modules.augeas_cfg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.augeas_cfg.rst - -[salt.ref--modules--all--salt_modules_bluez] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.bluez.po -source_file = _build/locale/ref/modules/all/salt.modules.bluez.pot -source_lang = en -source_name = ref/modules/all/salt.modules.bluez.rst - -[salt.ref--modules--all--salt_modules_brew] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.brew.po -source_file = _build/locale/ref/modules/all/salt.modules.brew.pot -source_lang = en -source_name = ref/modules/all/salt.modules.brew.rst - -[salt.ref--modules--all--salt_modules_bridge] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.bridge.po -source_file = _build/locale/ref/modules/all/salt.modules.bridge.pot -source_lang = en -source_name = ref/modules/all/salt.modules.bridge.rst - -[salt.ref--modules--all--salt_modules_bsd_shadow] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.bsd_shadow.po -source_file = _build/locale/ref/modules/all/salt.modules.bsd_shadow.pot -source_lang = en -source_name = ref/modules/all/salt.modules.bsd_shadow.rst - -[salt.ref--modules--all--salt_modules_cassandra] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.cassandra.po -source_file = _build/locale/ref/modules/all/salt.modules.cassandra.pot -source_lang = en -source_name = ref/modules/all/salt.modules.cassandra.rst - -[salt.ref--modules--all--salt_modules_cmdmod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.cmdmod.po -source_file = _build/locale/ref/modules/all/salt.modules.cmdmod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.cmdmod.rst - -[salt.ref--modules--all--salt_modules_config] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.config.po -source_file = _build/locale/ref/modules/all/salt.modules.config.pot -source_lang = en -source_name = ref/modules/all/salt.modules.config.rst - -[salt.ref--modules--all--salt_modules_cp] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.cp.po -source_file = _build/locale/ref/modules/all/salt.modules.cp.pot -source_lang = en -source_name = ref/modules/all/salt.modules.cp.rst - -[salt.ref--modules--all--salt_modules_cron] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.cron.po -source_file = _build/locale/ref/modules/all/salt.modules.cron.pot -source_lang = en -source_name = ref/modules/all/salt.modules.cron.rst - -[salt.ref--modules--all--salt_modules_daemontools] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.daemontools.po -source_file = _build/locale/ref/modules/all/salt.modules.daemontools.pot -source_lang = en -source_name = ref/modules/all/salt.modules.daemontools.rst - -[salt.ref--modules--all--salt_modules_darwin_sysctl] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.darwin_sysctl.po -source_file = _build/locale/ref/modules/all/salt.modules.darwin_sysctl.pot -source_lang = en -source_name = ref/modules/all/salt.modules.darwin_sysctl.rst - -[salt.ref--modules--all--salt_modules_data] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.data.po -source_file = _build/locale/ref/modules/all/salt.modules.data.pot -source_lang = en -source_name = ref/modules/all/salt.modules.data.rst - -[salt.ref--modules--all--salt_modules_ddns] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.ddns.po -source_file = _build/locale/ref/modules/all/salt.modules.ddns.pot -source_lang = en -source_name = ref/modules/all/salt.modules.ddns.rst - -[salt.ref--modules--all--salt_modules_debconfmod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.debconfmod.po -source_file = _build/locale/ref/modules/all/salt.modules.debconfmod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.debconfmod.rst - -[salt.ref--modules--all--salt_modules_debian_service] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.debian_service.po -source_file = _build/locale/ref/modules/all/salt.modules.debian_service.pot -source_lang = en -source_name = ref/modules/all/salt.modules.debian_service.rst - -[salt.ref--modules--all--salt_modules_dig] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.dig.po -source_file = _build/locale/ref/modules/all/salt.modules.dig.pot -source_lang = en -source_name = ref/modules/all/salt.modules.dig.rst - -[salt.ref--modules--all--salt_modules_disk] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.disk.po -source_file = _build/locale/ref/modules/all/salt.modules.disk.pot -source_lang = en -source_name = ref/modules/all/salt.modules.disk.rst - -[salt.ref--modules--all--salt_modules_djangomod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.djangomod.po -source_file = _build/locale/ref/modules/all/salt.modules.djangomod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.djangomod.rst - -[salt.ref--modules--all--salt_modules_dnsmasq] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.dnsmasq.po -source_file = _build/locale/ref/modules/all/salt.modules.dnsmasq.pot -source_lang = en -source_name = ref/modules/all/salt.modules.dnsmasq.rst - -[salt.ref--modules--all--salt_modules_dnsutil] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.dnsutil.po -source_file = _build/locale/ref/modules/all/salt.modules.dnsutil.pot -source_lang = en -source_name = ref/modules/all/salt.modules.dnsutil.rst - -[salt.ref--modules--all--salt_modules_dpkg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.dpkg.po -source_file = _build/locale/ref/modules/all/salt.modules.dpkg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.dpkg.rst - -[salt.ref--modules--all--salt_modules_ebuild] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.ebuild.po -source_file = _build/locale/ref/modules/all/salt.modules.ebuild.pot -source_lang = en -source_name = ref/modules/all/salt.modules.ebuild.rst - -[salt.ref--modules--all--salt_modules_eix] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.eix.po -source_file = _build/locale/ref/modules/all/salt.modules.eix.pot -source_lang = en -source_name = ref/modules/all/salt.modules.eix.rst - -[salt.ref--modules--all--salt_modules_eselect] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.eselect.po -source_file = _build/locale/ref/modules/all/salt.modules.eselect.pot -source_lang = en -source_name = ref/modules/all/salt.modules.eselect.rst - -[salt.ref--modules--all--salt_modules_event] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.event.po -source_file = _build/locale/ref/modules/all/salt.modules.event.pot -source_lang = en -source_name = ref/modules/all/salt.modules.event.rst - -[salt.ref--modules--all--salt_modules_extfs] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.extfs.po -source_file = _build/locale/ref/modules/all/salt.modules.extfs.pot -source_lang = en -source_name = ref/modules/all/salt.modules.extfs.rst - -[salt.ref--modules--all--salt_modules_file] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.file.po -source_file = _build/locale/ref/modules/all/salt.modules.file.pot -source_lang = en -source_name = ref/modules/all/salt.modules.file.rst - -[salt.ref--modules--all--salt_modules_freebsd_sysctl] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.freebsd_sysctl.po -source_file = _build/locale/ref/modules/all/salt.modules.freebsd_sysctl.pot -source_lang = en -source_name = ref/modules/all/salt.modules.freebsd_sysctl.rst - -[salt.ref--modules--all--salt_modules_freebsdjail] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.freebsdjail.po -source_file = _build/locale/ref/modules/all/salt.modules.freebsdjail.pot -source_lang = en -source_name = ref/modules/all/salt.modules.freebsdjail.rst - -[salt.ref--modules--all--salt_modules_freebsdkmod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.freebsdkmod.po -source_file = _build/locale/ref/modules/all/salt.modules.freebsdkmod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.freebsdkmod.rst - -[salt.ref--modules--all--salt_modules_freebsdpkg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.freebsdpkg.po -source_file = _build/locale/ref/modules/all/salt.modules.freebsdpkg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.freebsdpkg.rst - -[salt.ref--modules--all--salt_modules_freebsdservice] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.freebsdservice.po -source_file = _build/locale/ref/modules/all/salt.modules.freebsdservice.pot -source_lang = en -source_name = ref/modules/all/salt.modules.freebsdservice.rst - -[salt.ref--modules--all--salt_modules_gem] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.gem.po -source_file = _build/locale/ref/modules/all/salt.modules.gem.pot -source_lang = en -source_name = ref/modules/all/salt.modules.gem.rst - -[salt.ref--modules--all--salt_modules_gentoo_service] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.gentoo_service.po -source_file = _build/locale/ref/modules/all/salt.modules.gentoo_service.pot -source_lang = en -source_name = ref/modules/all/salt.modules.gentoo_service.rst - -[salt.ref--modules--all--salt_modules_gentoolkitmod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.gentoolkitmod.po -source_file = _build/locale/ref/modules/all/salt.modules.gentoolkitmod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.gentoolkitmod.rst - -[salt.ref--modules--all--salt_modules_git] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.git.po -source_file = _build/locale/ref/modules/all/salt.modules.git.pot -source_lang = en -source_name = ref/modules/all/salt.modules.git.rst - -[salt.ref--modules--all--salt_modules_glance] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.glance.po -source_file = _build/locale/ref/modules/all/salt.modules.glance.pot -source_lang = en -source_name = ref/modules/all/salt.modules.glance.rst - -[salt.ref--modules--all--salt_modules_grains] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.grains.po -source_file = _build/locale/ref/modules/all/salt.modules.grains.pot -source_lang = en -source_name = ref/modules/all/salt.modules.grains.rst - -[salt.ref--modules--all--salt_modules_groupadd] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.groupadd.po -source_file = _build/locale/ref/modules/all/salt.modules.groupadd.pot -source_lang = en -source_name = ref/modules/all/salt.modules.groupadd.rst - -[salt.ref--modules--all--salt_modules_grub_legacy] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.grub_legacy.po -source_file = _build/locale/ref/modules/all/salt.modules.grub_legacy.pot -source_lang = en -source_name = ref/modules/all/salt.modules.grub_legacy.rst - -[salt.ref--modules--all--salt_modules_guestfs] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.guestfs.po -source_file = _build/locale/ref/modules/all/salt.modules.guestfs.pot -source_lang = en -source_name = ref/modules/all/salt.modules.guestfs.rst - -[salt.ref--modules--all--salt_modules_hg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.hg.po -source_file = _build/locale/ref/modules/all/salt.modules.hg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.hg.rst - -[salt.ref--modules--all--salt_modules_hosts] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.hosts.po -source_file = _build/locale/ref/modules/all/salt.modules.hosts.pot -source_lang = en -source_name = ref/modules/all/salt.modules.hosts.rst - -[salt.ref--modules--all--salt_modules_img] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.img.po -source_file = _build/locale/ref/modules/all/salt.modules.img.pot -source_lang = en -source_name = ref/modules/all/salt.modules.img.rst - -[salt.ref--modules--all--salt_modules_iptables] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.iptables.po -source_file = _build/locale/ref/modules/all/salt.modules.iptables.pot -source_lang = en -source_name = ref/modules/all/salt.modules.iptables.rst - -[salt.ref--modules--all--salt_modules_key] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.key.po -source_file = _build/locale/ref/modules/all/salt.modules.key.pot -source_lang = en -source_name = ref/modules/all/salt.modules.key.rst - -[salt.ref--modules--all--salt_modules_keyboard] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.keyboard.po -source_file = _build/locale/ref/modules/all/salt.modules.keyboard.pot -source_lang = en -source_name = ref/modules/all/salt.modules.keyboard.rst - -[salt.ref--modules--all--salt_modules_keystone] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.keystone.po -source_file = _build/locale/ref/modules/all/salt.modules.keystone.pot -source_lang = en -source_name = ref/modules/all/salt.modules.keystone.rst - -[salt.ref--modules--all--salt_modules_kmod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.kmod.po -source_file = _build/locale/ref/modules/all/salt.modules.kmod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.kmod.rst - -[salt.ref--modules--all--salt_modules_launchctl] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.launchctl.po -source_file = _build/locale/ref/modules/all/salt.modules.launchctl.pot -source_lang = en -source_name = ref/modules/all/salt.modules.launchctl.rst - -[salt.ref--modules--all--salt_modules_layman] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.layman.po -source_file = _build/locale/ref/modules/all/salt.modules.layman.pot -source_lang = en -source_name = ref/modules/all/salt.modules.layman.rst - -[salt.ref--modules--all--salt_modules_ldapmod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.ldapmod.po -source_file = _build/locale/ref/modules/all/salt.modules.ldapmod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.ldapmod.rst - -[salt.ref--modules--all--salt_modules_linux_acl] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.linux_acl.po -source_file = _build/locale/ref/modules/all/salt.modules.linux_acl.pot -source_lang = en -source_name = ref/modules/all/salt.modules.linux_acl.rst - -[salt.ref--modules--all--salt_modules_linux_lvm] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.linux_lvm.po -source_file = _build/locale/ref/modules/all/salt.modules.linux_lvm.pot -source_lang = en -source_name = ref/modules/all/salt.modules.linux_lvm.rst - -[salt.ref--modules--all--salt_modules_linux_sysctl] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.linux_sysctl.po -source_file = _build/locale/ref/modules/all/salt.modules.linux_sysctl.pot -source_lang = en -source_name = ref/modules/all/salt.modules.linux_sysctl.rst - -[salt.ref--modules--all--salt_modules_localemod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.localemod.po -source_file = _build/locale/ref/modules/all/salt.modules.localemod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.localemod.rst - -[salt.ref--modules--all--salt_modules_locate] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.locate.po -source_file = _build/locale/ref/modules/all/salt.modules.locate.pot -source_lang = en -source_name = ref/modules/all/salt.modules.locate.rst - -[salt.ref--modules--all--salt_modules_logrotate] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.logrotate.po -source_file = _build/locale/ref/modules/all/salt.modules.logrotate.pot -source_lang = en -source_name = ref/modules/all/salt.modules.logrotate.rst - -[salt.ref--modules--all--salt_modules_lxc] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.lxc.po -source_file = _build/locale/ref/modules/all/salt.modules.lxc.pot -source_lang = en -source_name = ref/modules/all/salt.modules.lxc.rst - -[salt.ref--modules--all--salt_modules_mac_group] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.mac_group.po -source_file = _build/locale/ref/modules/all/salt.modules.mac_group.pot -source_lang = en -source_name = ref/modules/all/salt.modules.mac_group.rst - -[salt.ref--modules--all--salt_modules_mac_user] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.mac_user.po -source_file = _build/locale/ref/modules/all/salt.modules.mac_user.pot -source_lang = en -source_name = ref/modules/all/salt.modules.mac_user.rst - -[salt.ref--modules--all--salt_modules_makeconf] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.makeconf.po -source_file = _build/locale/ref/modules/all/salt.modules.makeconf.pot -source_lang = en -source_name = ref/modules/all/salt.modules.makeconf.rst - -[salt.ref--modules--all--salt_modules_match] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.match.po -source_file = _build/locale/ref/modules/all/salt.modules.match.pot -source_lang = en -source_name = ref/modules/all/salt.modules.match.rst - -[salt.ref--modules--all--salt_modules_mdadm] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.mdadm.po -source_file = _build/locale/ref/modules/all/salt.modules.mdadm.pot -source_lang = en -source_name = ref/modules/all/salt.modules.mdadm.rst - -[salt.ref--modules--all--salt_modules_mine] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.mine.po -source_file = _build/locale/ref/modules/all/salt.modules.mine.pot -source_lang = en -source_name = ref/modules/all/salt.modules.mine.rst - -[salt.ref--modules--all--salt_modules_modjk] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.modjk.po -source_file = _build/locale/ref/modules/all/salt.modules.modjk.pot -source_lang = en -source_name = ref/modules/all/salt.modules.modjk.rst - -[salt.ref--modules--all--salt_modules_mongodb] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.mongodb.po -source_file = _build/locale/ref/modules/all/salt.modules.mongodb.pot -source_lang = en -source_name = ref/modules/all/salt.modules.mongodb.rst - -[salt.ref--modules--all--salt_modules_monit] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.monit.po -source_file = _build/locale/ref/modules/all/salt.modules.monit.pot -source_lang = en -source_name = ref/modules/all/salt.modules.monit.rst - -[salt.ref--modules--all--salt_modules_moosefs] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.moosefs.po -source_file = _build/locale/ref/modules/all/salt.modules.moosefs.pot -source_lang = en -source_name = ref/modules/all/salt.modules.moosefs.rst - -[salt.ref--modules--all--salt_modules_mount] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.mount.po -source_file = _build/locale/ref/modules/all/salt.modules.mount.pot -source_lang = en -source_name = ref/modules/all/salt.modules.mount.rst - -[salt.ref--modules--all--salt_modules_munin] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.munin.po -source_file = _build/locale/ref/modules/all/salt.modules.munin.pot -source_lang = en -source_name = ref/modules/all/salt.modules.munin.rst - -[salt.ref--modules--all--salt_modules_mysql] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.mysql.po -source_file = _build/locale/ref/modules/all/salt.modules.mysql.pot -source_lang = en -source_name = ref/modules/all/salt.modules.mysql.rst - -[salt.ref--modules--all--salt_modules_netbsd_sysctl] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.netbsd_sysctl.po -source_file = _build/locale/ref/modules/all/salt.modules.netbsd_sysctl.pot -source_lang = en -source_name = ref/modules/all/salt.modules.netbsd_sysctl.rst - -[salt.ref--modules--all--salt_modules_netbsdservice] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.netbsdservice.po -source_file = _build/locale/ref/modules/all/salt.modules.netbsdservice.pot -source_lang = en -source_name = ref/modules/all/salt.modules.netbsdservice.rst - -[salt.ref--modules--all--salt_modules_network] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.network.po -source_file = _build/locale/ref/modules/all/salt.modules.network.pot -source_lang = en -source_name = ref/modules/all/salt.modules.network.rst - -[salt.ref--modules--all--salt_modules_nfs3] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.nfs3.po -source_file = _build/locale/ref/modules/all/salt.modules.nfs3.pot -source_lang = en -source_name = ref/modules/all/salt.modules.nfs3.rst - -[salt.ref--modules--all--salt_modules_nginx] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.nginx.po -source_file = _build/locale/ref/modules/all/salt.modules.nginx.pot -source_lang = en -source_name = ref/modules/all/salt.modules.nginx.rst - -[salt.ref--modules--all--salt_modules_nova] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.nova.po -source_file = _build/locale/ref/modules/all/salt.modules.nova.pot -source_lang = en -source_name = ref/modules/all/salt.modules.nova.rst - -[salt.ref--modules--all--salt_modules_npm] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.npm.po -source_file = _build/locale/ref/modules/all/salt.modules.npm.pot -source_lang = en -source_name = ref/modules/all/salt.modules.npm.rst - -[salt.ref--modules--all--salt_modules_openbsdpkg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.openbsdpkg.po -source_file = _build/locale/ref/modules/all/salt.modules.openbsdpkg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.openbsdpkg.rst - -[salt.ref--modules--all--salt_modules_openbsdservice] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.openbsdservice.po -source_file = _build/locale/ref/modules/all/salt.modules.openbsdservice.pot -source_lang = en -source_name = ref/modules/all/salt.modules.openbsdservice.rst - -[salt.ref--modules--all--salt_modules_osxdesktop] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.osxdesktop.po -source_file = _build/locale/ref/modules/all/salt.modules.osxdesktop.pot -source_lang = en -source_name = ref/modules/all/salt.modules.osxdesktop.rst - -[salt.ref--modules--all--salt_modules_pacman] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pacman.po -source_file = _build/locale/ref/modules/all/salt.modules.pacman.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pacman.rst - -[salt.ref--modules--all--salt_modules_pam] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pam.po -source_file = _build/locale/ref/modules/all/salt.modules.pam.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pam.rst - -[salt.ref--modules--all--salt_modules_parted] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.parted.po -source_file = _build/locale/ref/modules/all/salt.modules.parted.pot -source_lang = en -source_name = ref/modules/all/salt.modules.parted.rst - -[salt.ref--modules--all--salt_modules_pecl] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pecl.po -source_file = _build/locale/ref/modules/all/salt.modules.pecl.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pecl.rst - -[salt.ref--modules--all--salt_modules_pillar] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pillar.po -source_file = _build/locale/ref/modules/all/salt.modules.pillar.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pillar.rst - -[salt.ref--modules--all--salt_modules_pip] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pip.po -source_file = _build/locale/ref/modules/all/salt.modules.pip.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pip.rst - -[salt.ref--modules--all--salt_modules_pkg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pkg.po -source_file = _build/locale/ref/modules/all/salt.modules.pkg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pkg.rst - -[salt.ref--modules--all--salt_modules_pkg_resource] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pkg_resource.po -source_file = _build/locale/ref/modules/all/salt.modules.pkg_resource.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pkg_resource.rst - -[salt.ref--modules--all--salt_modules_pkgin] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pkgin.po -source_file = _build/locale/ref/modules/all/salt.modules.pkgin.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pkgin.rst - -[salt.ref--modules--all--salt_modules_pkgng] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pkgng.po -source_file = _build/locale/ref/modules/all/salt.modules.pkgng.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pkgng.rst - -[salt.ref--modules--all--salt_modules_pkgutil] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pkgutil.po -source_file = _build/locale/ref/modules/all/salt.modules.pkgutil.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pkgutil.rst - -[salt.ref--modules--all--salt_modules_portage_config] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.portage_config.po -source_file = _build/locale/ref/modules/all/salt.modules.portage_config.pot -source_lang = en -source_name = ref/modules/all/salt.modules.portage_config.rst - -[salt.ref--modules--all--salt_modules_postgres] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.postgres.po -source_file = _build/locale/ref/modules/all/salt.modules.postgres.pot -source_lang = en -source_name = ref/modules/all/salt.modules.postgres.rst - -[salt.ref--modules--all--salt_modules_poudriere] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.poudriere.po -source_file = _build/locale/ref/modules/all/salt.modules.poudriere.pot -source_lang = en -source_name = ref/modules/all/salt.modules.poudriere.rst - -[salt.ref--modules--all--salt_modules_ps] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.ps.po -source_file = _build/locale/ref/modules/all/salt.modules.ps.pot -source_lang = en -source_name = ref/modules/all/salt.modules.ps.rst - -[salt.ref--modules--all--salt_modules_publish] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.publish.po -source_file = _build/locale/ref/modules/all/salt.modules.publish.pot -source_lang = en -source_name = ref/modules/all/salt.modules.publish.rst - -[salt.ref--modules--all--salt_modules_puppet] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.puppet.po -source_file = _build/locale/ref/modules/all/salt.modules.puppet.pot -source_lang = en -source_name = ref/modules/all/salt.modules.puppet.rst - -[salt.ref--modules--all--salt_modules_pw_group] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pw_group.po -source_file = _build/locale/ref/modules/all/salt.modules.pw_group.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pw_group.rst - -[salt.ref--modules--all--salt_modules_pw_user] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pw_user.po -source_file = _build/locale/ref/modules/all/salt.modules.pw_user.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pw_user.rst - -[salt.ref--modules--all--salt_modules_qemu_img] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.qemu_img.po -source_file = _build/locale/ref/modules/all/salt.modules.qemu_img.pot -source_lang = en -source_name = ref/modules/all/salt.modules.qemu_img.rst - -[salt.ref--modules--all--salt_modules_qemu_nbd] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.qemu_nbd.po -source_file = _build/locale/ref/modules/all/salt.modules.qemu_nbd.pot -source_lang = en -source_name = ref/modules/all/salt.modules.qemu_nbd.rst - -[salt.ref--modules--all--salt_modules_quota] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.quota.po -source_file = _build/locale/ref/modules/all/salt.modules.quota.pot -source_lang = en -source_name = ref/modules/all/salt.modules.quota.rst - -[salt.ref--modules--all--salt_modules_rabbitmq] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.rabbitmq.po -source_file = _build/locale/ref/modules/all/salt.modules.rabbitmq.pot -source_lang = en -source_name = ref/modules/all/salt.modules.rabbitmq.rst - -[salt.ref--modules--all--salt_modules_rbenv] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.rbenv.po -source_file = _build/locale/ref/modules/all/salt.modules.rbenv.pot -source_lang = en -source_name = ref/modules/all/salt.modules.rbenv.rst - -[salt.ref--modules--all--salt_modules_reg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.reg.po -source_file = _build/locale/ref/modules/all/salt.modules.reg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.reg.rst - -[salt.ref--modules--all--salt_modules_ret] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.ret.po -source_file = _build/locale/ref/modules/all/salt.modules.ret.pot -source_lang = en -source_name = ref/modules/all/salt.modules.ret.rst - -[salt.ref--modules--all--salt_modules_rh_ip] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.rh_ip.po -source_file = _build/locale/ref/modules/all/salt.modules.rh_ip.pot -source_lang = en -source_name = ref/modules/all/salt.modules.rh_ip.rst - -[salt.ref--modules--all--salt_modules_rh_service] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.rh_service.po -source_file = _build/locale/ref/modules/all/salt.modules.rh_service.pot -source_lang = en -source_name = ref/modules/all/salt.modules.rh_service.rst - -[salt.ref--modules--all--salt_modules_rpm] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.rpm.po -source_file = _build/locale/ref/modules/all/salt.modules.rpm.pot -source_lang = en -source_name = ref/modules/all/salt.modules.rpm.rst - -[salt.ref--modules--all--salt_modules_rvm] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.rvm.po -source_file = _build/locale/ref/modules/all/salt.modules.rvm.pot -source_lang = en -source_name = ref/modules/all/salt.modules.rvm.rst - -[salt.ref--modules--all--salt_modules_s3] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.s3.po -source_file = _build/locale/ref/modules/all/salt.modules.s3.pot -source_lang = en -source_name = ref/modules/all/salt.modules.s3.rst - -[salt.ref--modules--all--salt_modules_saltutil] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.saltutil.po -source_file = _build/locale/ref/modules/all/salt.modules.saltutil.pot -source_lang = en -source_name = ref/modules/all/salt.modules.saltutil.rst - -[salt.ref--modules--all--salt_modules_seed] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.seed.po -source_file = _build/locale/ref/modules/all/salt.modules.seed.pot -source_lang = en -source_name = ref/modules/all/salt.modules.seed.rst - -[salt.ref--modules--all--salt_modules_selinux] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.selinux.po -source_file = _build/locale/ref/modules/all/salt.modules.selinux.pot -source_lang = en -source_name = ref/modules/all/salt.modules.selinux.rst - -[salt.ref--modules--all--salt_modules_service] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.service.po -source_file = _build/locale/ref/modules/all/salt.modules.service.pot -source_lang = en -source_name = ref/modules/all/salt.modules.service.rst - -[salt.ref--modules--all--salt_modules_shadow] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.shadow.po -source_file = _build/locale/ref/modules/all/salt.modules.shadow.pot -source_lang = en -source_name = ref/modules/all/salt.modules.shadow.rst - -[salt.ref--modules--all--salt_modules_smartos_imgadm] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.smartos_imgadm.po -source_file = _build/locale/ref/modules/all/salt.modules.smartos_imgadm.pot -source_lang = en -source_name = ref/modules/all/salt.modules.smartos_imgadm.rst - -[salt.ref--modules--all--salt_modules_smartos_vmadm] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.smartos_vmadm.po -source_file = _build/locale/ref/modules/all/salt.modules.smartos_vmadm.pot -source_lang = en -source_name = ref/modules/all/salt.modules.smartos_vmadm.rst - -[salt.ref--modules--all--salt_modules_smf] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.smf.po -source_file = _build/locale/ref/modules/all/salt.modules.smf.pot -source_lang = en -source_name = ref/modules/all/salt.modules.smf.rst - -[salt.ref--modules--all--salt_modules_solaris_group] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.solaris_group.po -source_file = _build/locale/ref/modules/all/salt.modules.solaris_group.pot -source_lang = en -source_name = ref/modules/all/salt.modules.solaris_group.rst - -[salt.ref--modules--all--salt_modules_solaris_shadow] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.solaris_shadow.po -source_file = _build/locale/ref/modules/all/salt.modules.solaris_shadow.pot -source_lang = en -source_name = ref/modules/all/salt.modules.solaris_shadow.rst - -[salt.ref--modules--all--salt_modules_solaris_user] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.solaris_user.po -source_file = _build/locale/ref/modules/all/salt.modules.solaris_user.pot -source_lang = en -source_name = ref/modules/all/salt.modules.solaris_user.rst - -[salt.ref--modules--all--salt_modules_solarispkg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.solarispkg.po -source_file = _build/locale/ref/modules/all/salt.modules.solarispkg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.solarispkg.rst - -[salt.ref--modules--all--salt_modules_solr] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.solr.po -source_file = _build/locale/ref/modules/all/salt.modules.solr.pot -source_lang = en -source_name = ref/modules/all/salt.modules.solr.rst - -[salt.ref--modules--all--salt_modules_sqlite3] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.sqlite3.po -source_file = _build/locale/ref/modules/all/salt.modules.sqlite3.pot -source_lang = en -source_name = ref/modules/all/salt.modules.sqlite3.rst - -[salt.ref--modules--all--salt_modules_ssh] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.ssh.po -source_file = _build/locale/ref/modules/all/salt.modules.ssh.pot -source_lang = en -source_name = ref/modules/all/salt.modules.ssh.rst - -[salt.ref--modules--all--salt_modules_state] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.state.po -source_file = _build/locale/ref/modules/all/salt.modules.state.pot -source_lang = en -source_name = ref/modules/all/salt.modules.state.rst - -[salt.ref--modules--all--salt_modules_status] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.status.po -source_file = _build/locale/ref/modules/all/salt.modules.status.pot -source_lang = en -source_name = ref/modules/all/salt.modules.status.rst - -[salt.ref--modules--all--salt_modules_supervisord] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.supervisord.po -source_file = _build/locale/ref/modules/all/salt.modules.supervisord.pot -source_lang = en -source_name = ref/modules/all/salt.modules.supervisord.rst - -[salt.ref--modules--all--salt_modules_svn] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.svn.po -source_file = _build/locale/ref/modules/all/salt.modules.svn.pot -source_lang = en -source_name = ref/modules/all/salt.modules.svn.rst - -[salt.ref--modules--all--salt_modules_sysbench] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.sysbench.po -source_file = _build/locale/ref/modules/all/salt.modules.sysbench.pot -source_lang = en -source_name = ref/modules/all/salt.modules.sysbench.rst - -[salt.ref--modules--all--salt_modules_sysmod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.sysmod.po -source_file = _build/locale/ref/modules/all/salt.modules.sysmod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.sysmod.rst - -[salt.ref--modules--all--salt_modules_system] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.system.po -source_file = _build/locale/ref/modules/all/salt.modules.system.pot -source_lang = en -source_name = ref/modules/all/salt.modules.system.rst - -[salt.ref--modules--all--salt_modules_systemd] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.systemd.po -source_file = _build/locale/ref/modules/all/salt.modules.systemd.pot -source_lang = en -source_name = ref/modules/all/salt.modules.systemd.rst - -[salt.ref--modules--all--salt_modules_test] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.test.po -source_file = _build/locale/ref/modules/all/salt.modules.test.pot -source_lang = en -source_name = ref/modules/all/salt.modules.test.rst - -[salt.ref--modules--all--salt_modules_timezone] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.timezone.po -source_file = _build/locale/ref/modules/all/salt.modules.timezone.pot -source_lang = en -source_name = ref/modules/all/salt.modules.timezone.rst - -[salt.ref--modules--all--salt_modules_tls] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.tls.po -source_file = _build/locale/ref/modules/all/salt.modules.tls.pot -source_lang = en -source_name = ref/modules/all/salt.modules.tls.rst - -[salt.ref--modules--all--salt_modules_tomcat] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.tomcat.po -source_file = _build/locale/ref/modules/all/salt.modules.tomcat.pot -source_lang = en -source_name = ref/modules/all/salt.modules.tomcat.rst - -[salt.ref--modules--all--salt_modules_upstart] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.upstart.po -source_file = _build/locale/ref/modules/all/salt.modules.upstart.pot -source_lang = en -source_name = ref/modules/all/salt.modules.upstart.rst - -[salt.ref--modules--all--salt_modules_useradd] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.useradd.po -source_file = _build/locale/ref/modules/all/salt.modules.useradd.pot -source_lang = en -source_name = ref/modules/all/salt.modules.useradd.rst - -[salt.ref--modules--all--salt_modules_virt] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.virt.po -source_file = _build/locale/ref/modules/all/salt.modules.virt.pot -source_lang = en -source_name = ref/modules/all/salt.modules.virt.rst - -[salt.ref--modules--all--salt_modules_virtualenv_mod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.virtualenv_mod.po -source_file = _build/locale/ref/modules/all/salt.modules.virtualenv_mod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.virtualenv_mod.rst - -[salt.ref--modules--all--salt_modules_win_disk] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_disk.po -source_file = _build/locale/ref/modules/all/salt.modules.win_disk.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_disk.rst - -[salt.ref--modules--all--salt_modules_win_file] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_file.po -source_file = _build/locale/ref/modules/all/salt.modules.win_file.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_file.rst - -[salt.ref--modules--all--salt_modules_win_groupadd] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_groupadd.po -source_file = _build/locale/ref/modules/all/salt.modules.win_groupadd.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_groupadd.rst - -[salt.ref--modules--all--salt_modules_win_network] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_network.po -source_file = _build/locale/ref/modules/all/salt.modules.win_network.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_network.rst - -[salt.ref--modules--all--salt_modules_win_ntp] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_ntp.po -source_file = _build/locale/ref/modules/all/salt.modules.win_ntp.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_ntp.rst - -[salt.ref--modules--all--salt_modules_win_pkg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_pkg.po -source_file = _build/locale/ref/modules/all/salt.modules.win_pkg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_pkg.rst - -[salt.ref--modules--all--salt_modules_win_service] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_service.po -source_file = _build/locale/ref/modules/all/salt.modules.win_service.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_service.rst - -[salt.ref--modules--all--salt_modules_win_shadow] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_shadow.po -source_file = _build/locale/ref/modules/all/salt.modules.win_shadow.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_shadow.rst - -[salt.ref--modules--all--salt_modules_win_status] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_status.po -source_file = _build/locale/ref/modules/all/salt.modules.win_status.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_status.rst - -[salt.ref--modules--all--salt_modules_win_system] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_system.po -source_file = _build/locale/ref/modules/all/salt.modules.win_system.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_system.rst - -[salt.ref--modules--all--salt_modules_win_useradd] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_useradd.po -source_file = _build/locale/ref/modules/all/salt.modules.win_useradd.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_useradd.rst - -[salt.ref--modules--all--salt_modules_xapi] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.xapi.po -source_file = _build/locale/ref/modules/all/salt.modules.xapi.pot -source_lang = en -source_name = ref/modules/all/salt.modules.xapi.rst - -[salt.ref--modules--all--salt_modules_yumpkg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.yumpkg.po -source_file = _build/locale/ref/modules/all/salt.modules.yumpkg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.yumpkg.rst - -[salt.ref--modules--all--salt_modules_zfs] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.zfs.po -source_file = _build/locale/ref/modules/all/salt.modules.zfs.pot -source_lang = en -source_name = ref/modules/all/salt.modules.zfs.rst - -[salt.ref--modules--all--salt_modules_zpool] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.zpool.po -source_file = _build/locale/ref/modules/all/salt.modules.zpool.pot -source_lang = en -source_name = ref/modules/all/salt.modules.zpool.rst - -[salt.ref--modules--all--salt_modules_zypper] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.zypper.po -source_file = _build/locale/ref/modules/all/salt.modules.zypper.pot -source_lang = en -source_name = ref/modules/all/salt.modules.zypper.rst - -[salt.ref--modules--index] -file_filter = locale//LC_MESSAGES/ref/modules/index.po -source_file = _build/locale/ref/modules/index.pot -source_lang = en -source_name = ref/modules/index.rst - -[salt.ref--output--all--index] -file_filter = locale//LC_MESSAGES/ref/output/all/index.po -source_file = _build/locale/ref/output/all/index.pot -source_lang = en -source_name = ref/output/all/index.rst - -[salt.ref--output--all--salt_output_grains] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.grains.po -source_file = _build/locale/ref/output/all/salt.output.grains.pot -source_lang = en -source_name = ref/output/all/salt.output.grains.rst - -[salt.ref--output--all--salt_output_highstate] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.highstate.po -source_file = _build/locale/ref/output/all/salt.output.highstate.pot -source_lang = en -source_name = ref/output/all/salt.output.highstate.rst - -[salt.ref--output--all--salt_output_json_out] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.json_out.po -source_file = _build/locale/ref/output/all/salt.output.json_out.pot -source_lang = en -source_name = ref/output/all/salt.output.json_out.rst - -[salt.ref--output--all--salt_output_key] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.key.po -source_file = _build/locale/ref/output/all/salt.output.key.pot -source_lang = en -source_name = ref/output/all/salt.output.key.rst - -[salt.ref--output--all--salt_output_nested] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.nested.po -source_file = _build/locale/ref/output/all/salt.output.nested.pot -source_lang = en -source_name = ref/output/all/salt.output.nested.rst - -[salt.ref--output--all--salt_output_no_out] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.no_out.po -source_file = _build/locale/ref/output/all/salt.output.no_out.pot -source_lang = en -source_name = ref/output/all/salt.output.no_out.rst - -[salt.ref--output--all--salt_output_no_return] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.no_return.po -source_file = _build/locale/ref/output/all/salt.output.no_return.pot -source_lang = en -source_name = ref/output/all/salt.output.no_return.rst - -[salt.ref--output--all--salt_output_overstatestage] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.overstatestage.po -source_file = _build/locale/ref/output/all/salt.output.overstatestage.pot -source_lang = en -source_name = ref/output/all/salt.output.overstatestage.rst - -[salt.ref--output--all--salt_output_pprint_out] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.pprint_out.po -source_file = _build/locale/ref/output/all/salt.output.pprint_out.pot -source_lang = en -source_name = ref/output/all/salt.output.pprint_out.rst - -[salt.ref--output--all--salt_output_raw] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.raw.po -source_file = _build/locale/ref/output/all/salt.output.raw.pot -source_lang = en -source_name = ref/output/all/salt.output.raw.rst - -[salt.ref--output--all--salt_output_txt] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.txt.po -source_file = _build/locale/ref/output/all/salt.output.txt.pot -source_lang = en -source_name = ref/output/all/salt.output.txt.rst - -[salt.ref--output--all--salt_output_virt_query] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.virt_query.po -source_file = _build/locale/ref/output/all/salt.output.virt_query.pot -source_lang = en -source_name = ref/output/all/salt.output.virt_query.rst - -[salt.ref--output--all--salt_output_yaml_out] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.yaml_out.po -source_file = _build/locale/ref/output/all/salt.output.yaml_out.pot -source_lang = en -source_name = ref/output/all/salt.output.yaml_out.rst - -[salt.ref--peer] -file_filter = locale//LC_MESSAGES/ref/peer.po -source_file = _build/locale/ref/peer.pot -source_lang = en -source_name = ref/peer.rst - -[salt.ref--pillar--all--index] -file_filter = locale//LC_MESSAGES/ref/pillar/all/index.po -source_file = _build/locale/ref/pillar/all/index.pot -source_lang = en -source_name = ref/pillar/all/index.rst - -[salt.ref--pillar--all--salt_pillar_cmd_json] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.cmd_json.po -source_file = _build/locale/ref/pillar/all/salt.pillar.cmd_json.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.cmd_json.rst - -[salt.ref--pillar--all--salt_pillar_cmd_yaml] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.cmd_yaml.po -source_file = _build/locale/ref/pillar/all/salt.pillar.cmd_yaml.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.cmd_yaml.rst - -[salt.ref--pillar--all--salt_pillar_cobbler] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.cobbler.po -source_file = _build/locale/ref/pillar/all/salt.pillar.cobbler.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.cobbler.rst - -[salt.ref--pillar--all--salt_pillar_django_orm] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.django_orm.po -source_file = _build/locale/ref/pillar/all/salt.pillar.django_orm.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.django_orm.rst - -[salt.ref--pillar--all--salt_pillar_git_pillar] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.git_pillar.po -source_file = _build/locale/ref/pillar/all/salt.pillar.git_pillar.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.git_pillar.rst - -[salt.ref--pillar--all--salt_pillar_hiera] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.hiera.po -source_file = _build/locale/ref/pillar/all/salt.pillar.hiera.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.hiera.rst - -[salt.ref--pillar--all--salt_pillar_libvirt] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.libvirt.po -source_file = _build/locale/ref/pillar/all/salt.pillar.libvirt.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.libvirt.rst - -[salt.ref--pillar--all--salt_pillar_mongo] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.mongo.po -source_file = _build/locale/ref/pillar/all/salt.pillar.mongo.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.mongo.rst - -[salt.ref--pillar--all--salt_pillar_pillar_ldap] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.pillar_ldap.po -source_file = _build/locale/ref/pillar/all/salt.pillar.pillar_ldap.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.pillar_ldap.rst - -[salt.ref--pillar--all--salt_pillar_puppet] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.puppet.po -source_file = _build/locale/ref/pillar/all/salt.pillar.puppet.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.puppet.rst - -[salt.ref--pillar--all--salt_pillar_reclass_adapter] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.reclass_adapter.po -source_file = _build/locale/ref/pillar/all/salt.pillar.reclass_adapter.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.reclass_adapter.rst - -[salt.ref--pillar--index] -file_filter = locale//LC_MESSAGES/ref/pillar/index.po -source_file = _build/locale/ref/pillar/index.pot -source_lang = en -source_name = ref/pillar/index.rst - -[salt.ref--python-api] -file_filter = locale//LC_MESSAGES/ref/python-api.po -source_file = _build/locale/ref/python-api.pot -source_lang = en -source_name = ref/python-api.rst - -[salt.ref--renderers--all--index] -file_filter = locale//LC_MESSAGES/ref/renderers/all/index.po -source_file = _build/locale/ref/renderers/all/index.pot -source_lang = en -source_name = ref/renderers/all/index.rst - -[salt.ref--renderers--all--salt_renderers_jinja] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.jinja.po -source_file = _build/locale/ref/renderers/all/salt.renderers.jinja.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.jinja.rst - -[salt.ref--renderers--all--salt_renderers_json] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.json.po -source_file = _build/locale/ref/renderers/all/salt.renderers.json.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.json.rst - -[salt.ref--renderers--all--salt_renderers_mako] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.mako.po -source_file = _build/locale/ref/renderers/all/salt.renderers.mako.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.mako.rst - -[salt.ref--renderers--all--salt_renderers_py] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.py.po -source_file = _build/locale/ref/renderers/all/salt.renderers.py.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.py.rst - -[salt.ref--renderers--all--salt_renderers_pydsl] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.pydsl.po -source_file = _build/locale/ref/renderers/all/salt.renderers.pydsl.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.pydsl.rst - -[salt.ref--renderers--all--salt_renderers_pyobjects] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.pyobjects.po -source_file = _build/locale/ref/renderers/all/salt.renderers.pyobjects.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.pyobjects.rst - -[salt.ref--renderers--all--salt_renderers_stateconf] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.stateconf.po -source_file = _build/locale/ref/renderers/all/salt.renderers.stateconf.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.stateconf.rst - -[salt.ref--renderers--all--salt_renderers_wempy] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.wempy.po -source_file = _build/locale/ref/renderers/all/salt.renderers.wempy.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.wempy.rst - -[salt.ref--renderers--all--salt_renderers_yaml] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.yaml.po -source_file = _build/locale/ref/renderers/all/salt.renderers.yaml.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.yaml.rst - -[salt.ref--renderers--index] -file_filter = locale//LC_MESSAGES/ref/renderers/index.po -source_file = _build/locale/ref/renderers/index.pot -source_lang = en -source_name = ref/renderers/index.rst - -[salt.ref--returners--all--index] -file_filter = locale//LC_MESSAGES/ref/returners/all/index.po -source_file = _build/locale/ref/returners/all/index.pot -source_lang = en -source_name = ref/returners/all/index.rst - -[salt.ref--returners--all--salt_returners_carbon_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.carbon_return.po -source_file = _build/locale/ref/returners/all/salt.returners.carbon_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.carbon_return.rst - -[salt.ref--returners--all--salt_returners_cassandra_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.cassandra_return.po -source_file = _build/locale/ref/returners/all/salt.returners.cassandra_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.cassandra_return.rst - -[salt.ref--returners--all--salt_returners_local] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.local.po -source_file = _build/locale/ref/returners/all/salt.returners.local.pot -source_lang = en -source_name = ref/returners/all/salt.returners.local.rst - -[salt.ref--returners--all--salt_returners_mongo_future_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.mongo_future_return.po -source_file = _build/locale/ref/returners/all/salt.returners.mongo_future_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.mongo_future_return.rst - -[salt.ref--returners--all--salt_returners_mongo_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.mongo_return.po -source_file = _build/locale/ref/returners/all/salt.returners.mongo_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.mongo_return.rst - -[salt.ref--returners--all--salt_returners_mysql] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.mysql.po -source_file = _build/locale/ref/returners/all/salt.returners.mysql.pot -source_lang = en -source_name = ref/returners/all/salt.returners.mysql.rst - -[salt.ref--returners--all--salt_returners_postgres] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.postgres.po -source_file = _build/locale/ref/returners/all/salt.returners.postgres.pot -source_lang = en -source_name = ref/returners/all/salt.returners.postgres.rst - -[salt.ref--returners--all--salt_returners_redis_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.redis_return.po -source_file = _build/locale/ref/returners/all/salt.returners.redis_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.redis_return.rst - -[salt.ref--returners--all--salt_returners_sentry_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.sentry_return.po -source_file = _build/locale/ref/returners/all/salt.returners.sentry_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.sentry_return.rst - -[salt.ref--returners--all--salt_returners_smtp_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.smtp_return.po -source_file = _build/locale/ref/returners/all/salt.returners.smtp_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.smtp_return.rst - -[salt.ref--returners--all--salt_returners_sqlite3_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.sqlite3_return.po -source_file = _build/locale/ref/returners/all/salt.returners.sqlite3_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.sqlite3_return.rst - -[salt.ref--returners--all--salt_returners_syslog_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.syslog_return.po -source_file = _build/locale/ref/returners/all/salt.returners.syslog_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.syslog_return.rst - -[salt.ref--returners--index] -file_filter = locale//LC_MESSAGES/ref/returners/index.po -source_file = _build/locale/ref/returners/index.pot -source_lang = en -source_name = ref/returners/index.rst - -[salt.ref--runners--all--index] -file_filter = locale//LC_MESSAGES/ref/runners/all/index.po -source_file = _build/locale/ref/runners/all/index.pot -source_lang = en -source_name = ref/runners/all/index.rst - -[salt.ref--runners--all--salt_runners_cache] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.cache.po -source_file = _build/locale/ref/runners/all/salt.runners.cache.pot -source_lang = en -source_name = ref/runners/all/salt.runners.cache.rst - -[salt.ref--runners--all--salt_runners_doc] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.doc.po -source_file = _build/locale/ref/runners/all/salt.runners.doc.pot -source_lang = en -source_name = ref/runners/all/salt.runners.doc.rst - -[salt.ref--runners--all--salt_runners_fileserver] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.fileserver.po -source_file = _build/locale/ref/runners/all/salt.runners.fileserver.pot -source_lang = en -source_name = ref/runners/all/salt.runners.fileserver.rst - -[salt.ref--runners--all--salt_runners_jobs] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.jobs.po -source_file = _build/locale/ref/runners/all/salt.runners.jobs.pot -source_lang = en -source_name = ref/runners/all/salt.runners.jobs.rst - -[salt.ref--runners--all--salt_runners_launchd] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.launchd.po -source_file = _build/locale/ref/runners/all/salt.runners.launchd.pot -source_lang = en -source_name = ref/runners/all/salt.runners.launchd.rst - -[salt.ref--runners--all--salt_runners_manage] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.manage.po -source_file = _build/locale/ref/runners/all/salt.runners.manage.pot -source_lang = en -source_name = ref/runners/all/salt.runners.manage.rst - -[salt.ref--runners--all--salt_runners_network] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.network.po -source_file = _build/locale/ref/runners/all/salt.runners.network.pot -source_lang = en -source_name = ref/runners/all/salt.runners.network.rst - -[salt.ref--runners--all--salt_runners_search] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.search.po -source_file = _build/locale/ref/runners/all/salt.runners.search.pot -source_lang = en -source_name = ref/runners/all/salt.runners.search.rst - -[salt.ref--runners--all--salt_runners_state] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.state.po -source_file = _build/locale/ref/runners/all/salt.runners.state.pot -source_lang = en -source_name = ref/runners/all/salt.runners.state.rst - -[salt.ref--runners--all--salt_runners_virt] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.virt.po -source_file = _build/locale/ref/runners/all/salt.runners.virt.pot -source_lang = en -source_name = ref/runners/all/salt.runners.virt.rst - -[salt.ref--runners--all--salt_runners_winrepo] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.winrepo.po -source_file = _build/locale/ref/runners/all/salt.runners.winrepo.pot -source_lang = en -source_name = ref/runners/all/salt.runners.winrepo.rst - -[salt.ref--runners--index] -file_filter = locale//LC_MESSAGES/ref/runners/index.po -source_file = _build/locale/ref/runners/index.pot -source_lang = en -source_name = ref/runners/index.rst - -[salt.ref--states--all--index] -file_filter = locale//LC_MESSAGES/ref/states/all/index.po -source_file = _build/locale/ref/states/all/index.pot -source_lang = en -source_name = ref/states/all/index.rst - -[salt.ref--states--all--salt_states_alias] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.alias.po -source_file = _build/locale/ref/states/all/salt.states.alias.pot -source_lang = en -source_name = ref/states/all/salt.states.alias.rst - -[salt.ref--states--all--salt_states_alternatives] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.alternatives.po -source_file = _build/locale/ref/states/all/salt.states.alternatives.pot -source_lang = en -source_name = ref/states/all/salt.states.alternatives.rst - -[salt.ref--states--all--salt_states_apt] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.apt.po -source_file = _build/locale/ref/states/all/salt.states.apt.pot -source_lang = en -source_name = ref/states/all/salt.states.apt.rst - -[salt.ref--states--all--salt_states_augeas] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.augeas.po -source_file = _build/locale/ref/states/all/salt.states.augeas.pot -source_lang = en -source_name = ref/states/all/salt.states.augeas.rst - -[salt.ref--states--all--salt_states_cmd] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.cmd.po -source_file = _build/locale/ref/states/all/salt.states.cmd.pot -source_lang = en -source_name = ref/states/all/salt.states.cmd.rst - -[salt.ref--states--all--salt_states_cron] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.cron.po -source_file = _build/locale/ref/states/all/salt.states.cron.pot -source_lang = en -source_name = ref/states/all/salt.states.cron.rst - -[salt.ref--states--all--salt_states_debconfmod] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.debconfmod.po -source_file = _build/locale/ref/states/all/salt.states.debconfmod.pot -source_lang = en -source_name = ref/states/all/salt.states.debconfmod.rst - -[salt.ref--states--all--salt_states_disk] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.disk.po -source_file = _build/locale/ref/states/all/salt.states.disk.pot -source_lang = en -source_name = ref/states/all/salt.states.disk.rst - -[salt.ref--states--all--salt_states_eselect] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.eselect.po -source_file = _build/locale/ref/states/all/salt.states.eselect.pot -source_lang = en -source_name = ref/states/all/salt.states.eselect.rst - -[salt.ref--states--all--salt_states_file] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.file.po -source_file = _build/locale/ref/states/all/salt.states.file.pot -source_lang = en -source_name = ref/states/all/salt.states.file.rst - -[salt.ref--states--all--salt_states_gem] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.gem.po -source_file = _build/locale/ref/states/all/salt.states.gem.pot -source_lang = en -source_name = ref/states/all/salt.states.gem.rst - -[salt.ref--states--all--salt_states_git] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.git.po -source_file = _build/locale/ref/states/all/salt.states.git.pot -source_lang = en -source_name = ref/states/all/salt.states.git.rst - -[salt.ref--states--all--salt_states_grains] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.grains.po -source_file = _build/locale/ref/states/all/salt.states.grains.pot -source_lang = en -source_name = ref/states/all/salt.states.grains.rst - -[salt.ref--states--all--salt_states_group] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.group.po -source_file = _build/locale/ref/states/all/salt.states.group.pot -source_lang = en -source_name = ref/states/all/salt.states.group.rst - -[salt.ref--states--all--salt_states_hg] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.hg.po -source_file = _build/locale/ref/states/all/salt.states.hg.pot -source_lang = en -source_name = ref/states/all/salt.states.hg.rst - -[salt.ref--states--all--salt_states_host] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.host.po -source_file = _build/locale/ref/states/all/salt.states.host.pot -source_lang = en -source_name = ref/states/all/salt.states.host.rst - -[salt.ref--states--all--salt_states_iptables] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.iptables.po -source_file = _build/locale/ref/states/all/salt.states.iptables.pot -source_lang = en -source_name = ref/states/all/salt.states.iptables.rst - -[salt.ref--states--all--salt_states_keyboard] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.keyboard.po -source_file = _build/locale/ref/states/all/salt.states.keyboard.pot -source_lang = en -source_name = ref/states/all/salt.states.keyboard.rst - -[salt.ref--states--all--salt_states_kmod] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.kmod.po -source_file = _build/locale/ref/states/all/salt.states.kmod.pot -source_lang = en -source_name = ref/states/all/salt.states.kmod.rst - -[salt.ref--states--all--salt_states_layman] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.layman.po -source_file = _build/locale/ref/states/all/salt.states.layman.pot -source_lang = en -source_name = ref/states/all/salt.states.layman.rst - -[salt.ref--states--all--salt_states_libvirt] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.libvirt.po -source_file = _build/locale/ref/states/all/salt.states.libvirt.pot -source_lang = en -source_name = ref/states/all/salt.states.libvirt.rst - -[salt.ref--states--all--salt_states_locale] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.locale.po -source_file = _build/locale/ref/states/all/salt.states.locale.pot -source_lang = en -source_name = ref/states/all/salt.states.locale.rst - -[salt.ref--states--all--salt_states_lvm] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.lvm.po -source_file = _build/locale/ref/states/all/salt.states.lvm.pot -source_lang = en -source_name = ref/states/all/salt.states.lvm.rst - -[salt.ref--states--all--salt_states_makeconf] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.makeconf.po -source_file = _build/locale/ref/states/all/salt.states.makeconf.pot -source_lang = en -source_name = ref/states/all/salt.states.makeconf.rst - -[salt.ref--states--all--salt_states_mdadm] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.mdadm.po -source_file = _build/locale/ref/states/all/salt.states.mdadm.pot -source_lang = en -source_name = ref/states/all/salt.states.mdadm.rst - -[salt.ref--states--all--salt_states_modjk_worker] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.modjk_worker.po -source_file = _build/locale/ref/states/all/salt.states.modjk_worker.pot -source_lang = en -source_name = ref/states/all/salt.states.modjk_worker.rst - -[salt.ref--states--all--salt_states_module] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.module.po -source_file = _build/locale/ref/states/all/salt.states.module.pot -source_lang = en -source_name = ref/states/all/salt.states.module.rst - -[salt.ref--states--all--salt_states_mongodb_database] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.mongodb_database.po -source_file = _build/locale/ref/states/all/salt.states.mongodb_database.pot -source_lang = en -source_name = ref/states/all/salt.states.mongodb_database.rst - -[salt.ref--states--all--salt_states_mongodb_user] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.mongodb_user.po -source_file = _build/locale/ref/states/all/salt.states.mongodb_user.pot -source_lang = en -source_name = ref/states/all/salt.states.mongodb_user.rst - -[salt.ref--states--all--salt_states_mount] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.mount.po -source_file = _build/locale/ref/states/all/salt.states.mount.pot -source_lang = en -source_name = ref/states/all/salt.states.mount.rst - -[salt.ref--states--all--salt_states_mysql_database] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.mysql_database.po -source_file = _build/locale/ref/states/all/salt.states.mysql_database.pot -source_lang = en -source_name = ref/states/all/salt.states.mysql_database.rst - -[salt.ref--states--all--salt_states_mysql_grants] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.mysql_grants.po -source_file = _build/locale/ref/states/all/salt.states.mysql_grants.pot -source_lang = en -source_name = ref/states/all/salt.states.mysql_grants.rst - -[salt.ref--states--all--salt_states_mysql_user] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.mysql_user.po -source_file = _build/locale/ref/states/all/salt.states.mysql_user.pot -source_lang = en -source_name = ref/states/all/salt.states.mysql_user.rst - -[salt.ref--states--all--salt_states_network] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.network.po -source_file = _build/locale/ref/states/all/salt.states.network.pot -source_lang = en -source_name = ref/states/all/salt.states.network.rst - -[salt.ref--states--all--salt_states_npm] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.npm.po -source_file = _build/locale/ref/states/all/salt.states.npm.pot -source_lang = en -source_name = ref/states/all/salt.states.npm.rst - -[salt.ref--states--all--salt_states_ntp] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.ntp.po -source_file = _build/locale/ref/states/all/salt.states.ntp.pot -source_lang = en -source_name = ref/states/all/salt.states.ntp.rst - -[salt.ref--states--all--salt_states_pecl] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.pecl.po -source_file = _build/locale/ref/states/all/salt.states.pecl.pot -source_lang = en -source_name = ref/states/all/salt.states.pecl.rst - -[salt.ref--states--all--salt_states_pip_state] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.pip_state.po -source_file = _build/locale/ref/states/all/salt.states.pip_state.pot -source_lang = en -source_name = ref/states/all/salt.states.pip_state.rst - -[salt.ref--states--all--salt_states_pkg] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.pkg.po -source_file = _build/locale/ref/states/all/salt.states.pkg.pot -source_lang = en -source_name = ref/states/all/salt.states.pkg.rst - -[salt.ref--states--all--salt_states_pkgng] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.pkgng.po -source_file = _build/locale/ref/states/all/salt.states.pkgng.pot -source_lang = en -source_name = ref/states/all/salt.states.pkgng.rst - -[salt.ref--states--all--salt_states_pkgrepo] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.pkgrepo.po -source_file = _build/locale/ref/states/all/salt.states.pkgrepo.pot -source_lang = en -source_name = ref/states/all/salt.states.pkgrepo.rst - -[salt.ref--states--all--salt_states_portage_config] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.portage_config.po -source_file = _build/locale/ref/states/all/salt.states.portage_config.pot -source_lang = en -source_name = ref/states/all/salt.states.portage_config.rst - -[salt.ref--states--all--salt_states_postgres_database] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.postgres_database.po -source_file = _build/locale/ref/states/all/salt.states.postgres_database.pot -source_lang = en -source_name = ref/states/all/salt.states.postgres_database.rst - -[salt.ref--states--all--salt_states_postgres_group] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.postgres_group.po -source_file = _build/locale/ref/states/all/salt.states.postgres_group.pot -source_lang = en -source_name = ref/states/all/salt.states.postgres_group.rst - -[salt.ref--states--all--salt_states_postgres_user] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.postgres_user.po -source_file = _build/locale/ref/states/all/salt.states.postgres_user.pot -source_lang = en -source_name = ref/states/all/salt.states.postgres_user.rst - -[salt.ref--states--all--salt_states_quota] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.quota.po -source_file = _build/locale/ref/states/all/salt.states.quota.pot -source_lang = en -source_name = ref/states/all/salt.states.quota.rst - -[salt.ref--states--all--salt_states_rabbitmq_user] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.rabbitmq_user.po -source_file = _build/locale/ref/states/all/salt.states.rabbitmq_user.pot -source_lang = en -source_name = ref/states/all/salt.states.rabbitmq_user.rst - -[salt.ref--states--all--salt_states_rabbitmq_vhost] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.rabbitmq_vhost.po -source_file = _build/locale/ref/states/all/salt.states.rabbitmq_vhost.pot -source_lang = en -source_name = ref/states/all/salt.states.rabbitmq_vhost.rst - -[salt.ref--states--all--salt_states_rbenv] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.rbenv.po -source_file = _build/locale/ref/states/all/salt.states.rbenv.pot -source_lang = en -source_name = ref/states/all/salt.states.rbenv.rst - -[salt.ref--states--all--salt_states_rvm] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.rvm.po -source_file = _build/locale/ref/states/all/salt.states.rvm.pot -source_lang = en -source_name = ref/states/all/salt.states.rvm.rst - -[salt.ref--states--all--salt_states_selinux] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.selinux.po -source_file = _build/locale/ref/states/all/salt.states.selinux.pot -source_lang = en -source_name = ref/states/all/salt.states.selinux.rst - -[salt.ref--states--all--salt_states_service] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.service.po -source_file = _build/locale/ref/states/all/salt.states.service.pot -source_lang = en -source_name = ref/states/all/salt.states.service.rst - -[salt.ref--states--all--salt_states_ssh_auth] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.ssh_auth.po -source_file = _build/locale/ref/states/all/salt.states.ssh_auth.pot -source_lang = en -source_name = ref/states/all/salt.states.ssh_auth.rst - -[salt.ref--states--all--salt_states_ssh_known_hosts] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.ssh_known_hosts.po -source_file = _build/locale/ref/states/all/salt.states.ssh_known_hosts.pot -source_lang = en -source_name = ref/states/all/salt.states.ssh_known_hosts.rst - -[salt.ref--states--all--salt_states_stateconf] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.stateconf.po -source_file = _build/locale/ref/states/all/salt.states.stateconf.pot -source_lang = en -source_name = ref/states/all/salt.states.stateconf.rst - -[salt.ref--states--all--salt_states_supervisord] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.supervisord.po -source_file = _build/locale/ref/states/all/salt.states.supervisord.pot -source_lang = en -source_name = ref/states/all/salt.states.supervisord.rst - -[salt.ref--states--all--salt_states_svn] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.svn.po -source_file = _build/locale/ref/states/all/salt.states.svn.pot -source_lang = en -source_name = ref/states/all/salt.states.svn.rst - -[salt.ref--states--all--salt_states_sysctl] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.sysctl.po -source_file = _build/locale/ref/states/all/salt.states.sysctl.pot -source_lang = en -source_name = ref/states/all/salt.states.sysctl.rst - -[salt.ref--states--all--salt_states_timezone] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.timezone.po -source_file = _build/locale/ref/states/all/salt.states.timezone.pot -source_lang = en -source_name = ref/states/all/salt.states.timezone.rst - -[salt.ref--states--all--salt_states_tomcat] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.tomcat.po -source_file = _build/locale/ref/states/all/salt.states.tomcat.pot -source_lang = en -source_name = ref/states/all/salt.states.tomcat.rst - -[salt.ref--states--all--salt_states_user] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.user.po -source_file = _build/locale/ref/states/all/salt.states.user.pot -source_lang = en -source_name = ref/states/all/salt.states.user.rst - -[salt.ref--states--all--salt_states_virtualenv_mod] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.virtualenv_mod.po -source_file = _build/locale/ref/states/all/salt.states.virtualenv_mod.pot -source_lang = en -source_name = ref/states/all/salt.states.virtualenv_mod.rst - -[salt.ref--states--all--salt_states_win_dns_client] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.win_dns_client.po -source_file = _build/locale/ref/states/all/salt.states.win_dns_client.pot -source_lang = en -source_name = ref/states/all/salt.states.win_dns_client.rst - -[salt.ref--states--all--salt_states_win_firewall] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.win_firewall.po -source_file = _build/locale/ref/states/all/salt.states.win_firewall.pot -source_lang = en -source_name = ref/states/all/salt.states.win_firewall.rst - -[salt.ref--states--all--salt_states_win_path] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.win_path.po -source_file = _build/locale/ref/states/all/salt.states.win_path.pot -source_lang = en -source_name = ref/states/all/salt.states.win_path.rst - -[salt.ref--states--all--salt_states_win_servermanager] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.win_servermanager.po -source_file = _build/locale/ref/states/all/salt.states.win_servermanager.pot -source_lang = en -source_name = ref/states/all/salt.states.win_servermanager.rst - -[salt.ref--states--all--salt_states_win_system] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.win_system.po -source_file = _build/locale/ref/states/all/salt.states.win_system.pot -source_lang = en -source_name = ref/states/all/salt.states.win_system.rst - -[salt.ref--states--backup_mode] -file_filter = locale//LC_MESSAGES/ref/states/backup_mode.po -source_file = _build/locale/ref/states/backup_mode.pot -source_lang = en -source_name = ref/states/backup_mode.rst - -[salt.ref--states--extend] -file_filter = locale//LC_MESSAGES/ref/states/extend.po -source_file = _build/locale/ref/states/extend.pot -source_lang = en -source_name = ref/states/extend.rst - -[salt.ref--states--failhard] -file_filter = locale//LC_MESSAGES/ref/states/failhard.po -source_file = _build/locale/ref/states/failhard.pot -source_lang = en -source_name = ref/states/failhard.rst - -[salt.ref--states--highstate] -file_filter = locale//LC_MESSAGES/ref/states/highstate.po -source_file = _build/locale/ref/states/highstate.pot -source_lang = en -source_name = ref/states/highstate.rst - -[salt.ref--states--include] -file_filter = locale//LC_MESSAGES/ref/states/include.po -source_file = _build/locale/ref/states/include.pot -source_lang = en -source_name = ref/states/include.rst - -[salt.ref--states--index] -file_filter = locale//LC_MESSAGES/ref/states/index.po -source_file = _build/locale/ref/states/index.pot -source_lang = en -source_name = ref/states/index.rst - -[salt.ref--states--layers] -file_filter = locale//LC_MESSAGES/ref/states/layers.po -source_file = _build/locale/ref/states/layers.pot -source_lang = en -source_name = ref/states/layers.rst - -[salt.ref--states--master_side] -file_filter = locale//LC_MESSAGES/ref/states/master_side.po -source_file = _build/locale/ref/states/master_side.pot -source_lang = en -source_name = ref/states/master_side.rst - -[salt.ref--states--ordering] -file_filter = locale//LC_MESSAGES/ref/states/ordering.po -source_file = _build/locale/ref/states/ordering.pot -source_lang = en -source_name = ref/states/ordering.rst - -[salt.ref--states--overstate] -file_filter = locale//LC_MESSAGES/ref/states/overstate.po -source_file = _build/locale/ref/states/overstate.pot -source_lang = en -source_name = ref/states/overstate.rst - -[salt.ref--states--providers] -file_filter = locale//LC_MESSAGES/ref/states/providers.po -source_file = _build/locale/ref/states/providers.pot -source_lang = en -source_name = ref/states/providers.rst - -[salt.ref--states--requisites] -file_filter = locale//LC_MESSAGES/ref/states/requisites.po -source_file = _build/locale/ref/states/requisites.pot -source_lang = en -source_name = ref/states/requisites.rst - -[salt.ref--states--startup] -file_filter = locale//LC_MESSAGES/ref/states/startup.po -source_file = _build/locale/ref/states/startup.pot -source_lang = en -source_name = ref/states/startup.rst - -[salt.ref--states--testing] -file_filter = locale//LC_MESSAGES/ref/states/testing.po -source_file = _build/locale/ref/states/testing.pot -source_lang = en -source_name = ref/states/testing.rst - -[salt.ref--states--top] -file_filter = locale//LC_MESSAGES/ref/states/top.po -source_file = _build/locale/ref/states/top.pot -source_lang = en -source_name = ref/states/top.rst - -[salt.ref--states--vars] -file_filter = locale//LC_MESSAGES/ref/states/vars.po -source_file = _build/locale/ref/states/vars.pot -source_lang = en -source_name = ref/states/vars.rst - -[salt.ref--states--writing] -file_filter = locale//LC_MESSAGES/ref/states/writing.po -source_file = _build/locale/ref/states/writing.pot -source_lang = en -source_name = ref/states/writing.rst - -[salt.ref--tops--all--index] -file_filter = locale//LC_MESSAGES/ref/tops/all/index.po -source_file = _build/locale/ref/tops/all/index.pot -source_lang = en -source_name = ref/tops/all/index.rst - -[salt.ref--tops--all--salt_tops_cobbler] -file_filter = locale//LC_MESSAGES/ref/tops/all/salt.tops.cobbler.po -source_file = _build/locale/ref/tops/all/salt.tops.cobbler.pot -source_lang = en -source_name = ref/tops/all/salt.tops.cobbler.rst - -[salt.ref--tops--all--salt_tops_ext_nodes] -file_filter = locale//LC_MESSAGES/ref/tops/all/salt.tops.ext_nodes.po -source_file = _build/locale/ref/tops/all/salt.tops.ext_nodes.pot -source_lang = en -source_name = ref/tops/all/salt.tops.ext_nodes.rst - -[salt.ref--tops--all--salt_tops_mongo] -file_filter = locale//LC_MESSAGES/ref/tops/all/salt.tops.mongo.po -source_file = _build/locale/ref/tops/all/salt.tops.mongo.pot -source_lang = en -source_name = ref/tops/all/salt.tops.mongo.rst - -[salt.ref--tops--all--salt_tops_reclass_adapter] -file_filter = locale//LC_MESSAGES/ref/tops/all/salt.tops.reclass_adapter.po -source_file = _build/locale/ref/tops/all/salt.tops.reclass_adapter.pot -source_lang = en -source_name = ref/tops/all/salt.tops.reclass_adapter.rst - -[salt.ref--tops--index] -file_filter = locale//LC_MESSAGES/ref/tops/index.po -source_file = _build/locale/ref/tops/index.pot -source_lang = en -source_name = ref/tops/index.rst - -[salt.ref--wheel--all--index] -file_filter = locale//LC_MESSAGES/ref/wheel/all/index.po -source_file = _build/locale/ref/wheel/all/index.pot -source_lang = en -source_name = ref/wheel/all/index.rst - -[salt.ref--wheel--all--salt_wheel_config] -file_filter = locale//LC_MESSAGES/ref/wheel/all/salt.wheel.config.po -source_file = _build/locale/ref/wheel/all/salt.wheel.config.pot -source_lang = en -source_name = ref/wheel/all/salt.wheel.config.rst - -[salt.ref--wheel--all--salt_wheel_file_roots] -file_filter = locale//LC_MESSAGES/ref/wheel/all/salt.wheel.file_roots.po -source_file = _build/locale/ref/wheel/all/salt.wheel.file_roots.pot -source_lang = en -source_name = ref/wheel/all/salt.wheel.file_roots.rst - -[salt.ref--wheel--all--salt_wheel_key] -file_filter = locale//LC_MESSAGES/ref/wheel/all/salt.wheel.key.po -source_file = _build/locale/ref/wheel/all/salt.wheel.key.pot -source_lang = en -source_name = ref/wheel/all/salt.wheel.key.rst - -[salt.ref--wheel--all--salt_wheel_pillar_roots] -file_filter = locale//LC_MESSAGES/ref/wheel/all/salt.wheel.pillar_roots.po -source_file = _build/locale/ref/wheel/all/salt.wheel.pillar_roots.pot -source_lang = en -source_name = ref/wheel/all/salt.wheel.pillar_roots.rst - -[salt.topics--development--deprecations] -file_filter = locale//LC_MESSAGES/topics/development/deprecations.po -source_file = _build/locale/topics/development/deprecations.pot -source_lang = en -source_name = topics/development/deprecations.rst - -[salt.topics--development--dunder_dictionaries] -file_filter = locale//LC_MESSAGES/topics/development/dunder_dictionaries.po -source_file = _build/locale/topics/development/dunder_dictionaries.pot -source_lang = en -source_name = topics/development/dunder_dictionaries.rst - -[salt.topics--development--external_pillars] -file_filter = locale//LC_MESSAGES/topics/development/external_pillars.po -source_file = _build/locale/topics/development/external_pillars.pot -source_lang = en -source_name = topics/development/external_pillars.rst - -[salt.topics--development--index] -file_filter = locale//LC_MESSAGES/topics/development/index.po -source_file = _build/locale/topics/development/index.pot -source_lang = en -source_name = topics/development/index.rst - -[salt.topics--development--logging] -file_filter = locale//LC_MESSAGES/topics/development/logging.po -source_file = _build/locale/topics/development/logging.pot -source_lang = en -source_name = topics/development/logging.rst - -[salt.topics--development--modular_systems] -file_filter = locale//LC_MESSAGES/topics/development/modular_systems.po -source_file = _build/locale/topics/development/modular_systems.pot -source_lang = en -source_name = topics/development/modular_systems.rst - -[salt.topics--development--package_providers] -file_filter = locale//LC_MESSAGES/topics/development/package_providers.po -source_file = _build/locale/topics/development/package_providers.pot -source_lang = en -source_name = topics/development/package_providers.rst - -[salt.topics--eauth--access_control] -file_filter = locale//LC_MESSAGES/topics/eauth/access_control.po -source_file = _build/locale/topics/eauth/access_control.pot -source_lang = en -source_name = topics/eauth/access_control.rst - -[salt.topics--eauth--index] -file_filter = locale//LC_MESSAGES/topics/eauth/index.po -source_file = _build/locale/topics/eauth/index.pot -source_lang = en -source_name = topics/eauth/index.rst - -[salt.topics--event--index] -file_filter = locale//LC_MESSAGES/topics/event/index.po -source_file = _build/locale/topics/event/index.pot -source_lang = en -source_name = topics/event/index.rst - -[salt.topics--index] -file_filter = locale//LC_MESSAGES/topics/index.po -source_file = _build/locale/topics/index.pot -source_lang = en -source_name = topics/index.rst - -[salt.topics--installation--arch] -file_filter = locale//LC_MESSAGES/topics/installation/arch.po -source_file = _build/locale/topics/installation/arch.pot -source_lang = en -source_name = topics/installation/arch.rst - -[salt.topics--installation--debian] -file_filter = locale//LC_MESSAGES/topics/installation/debian.po -source_file = _build/locale/topics/installation/debian.pot -source_lang = en -source_name = topics/installation/debian.rst - -[salt.topics--installation--fedora] -file_filter = locale//LC_MESSAGES/topics/installation/fedora.po -source_file = _build/locale/topics/installation/fedora.pot -source_lang = en -source_name = topics/installation/fedora.rst - -[salt.topics--installation--freebsd] -file_filter = locale//LC_MESSAGES/topics/installation/freebsd.po -source_file = _build/locale/topics/installation/freebsd.pot -source_lang = en -source_name = topics/installation/freebsd.rst - -[salt.topics--installation--gentoo] -file_filter = locale//LC_MESSAGES/topics/installation/gentoo.po -source_file = _build/locale/topics/installation/gentoo.pot -source_lang = en -source_name = topics/installation/gentoo.rst - -[salt.topics--installation--index] -file_filter = locale//LC_MESSAGES/topics/installation/index.po -source_file = _build/locale/topics/installation/index.pot -source_lang = en -source_name = topics/installation/index.rst - -[salt.topics--installation--osx] -file_filter = locale//LC_MESSAGES/topics/installation/osx.po -source_file = _build/locale/topics/installation/osx.pot -source_lang = en -source_name = topics/installation/osx.rst - -[salt.topics--installation--rhel] -file_filter = locale//LC_MESSAGES/topics/installation/rhel.po -source_file = _build/locale/topics/installation/rhel.pot -source_lang = en -source_name = topics/installation/rhel.rst - -[salt.topics--installation--solaris] -file_filter = locale//LC_MESSAGES/topics/installation/solaris.po -source_file = _build/locale/topics/installation/solaris.pot -source_lang = en -source_name = topics/installation/solaris.rst - -[salt.topics--installation--suse] -file_filter = locale//LC_MESSAGES/topics/installation/suse.po -source_file = _build/locale/topics/installation/suse.pot -source_lang = en -source_name = topics/installation/suse.rst - -[salt.topics--installation--ubuntu] -file_filter = locale//LC_MESSAGES/topics/installation/ubuntu.po -source_file = _build/locale/topics/installation/ubuntu.pot -source_lang = en -source_name = topics/installation/ubuntu.rst - -[salt.topics--installation--windows] -file_filter = locale//LC_MESSAGES/topics/installation/windows.po -source_file = _build/locale/topics/installation/windows.pot -source_lang = en -source_name = topics/installation/windows.rst - -[salt.topics--jobs--index] -file_filter = locale//LC_MESSAGES/topics/jobs/index.po -source_file = _build/locale/topics/jobs/index.pot -source_lang = en -source_name = topics/jobs/index.rst - -[salt.topics--jobs--schedule] -file_filter = locale//LC_MESSAGES/topics/jobs/schedule.po -source_file = _build/locale/topics/jobs/schedule.pot -source_lang = en -source_name = topics/jobs/schedule.rst - -[salt.topics--master_tops--index] -file_filter = locale//LC_MESSAGES/topics/master_tops/index.po -source_file = _build/locale/topics/master_tops/index.pot -source_lang = en -source_name = topics/master_tops/index.rst - -[salt.topics--mine--index] -file_filter = locale//LC_MESSAGES/topics/mine/index.po -source_file = _build/locale/topics/mine/index.pot -source_lang = en -source_name = topics/mine/index.rst - -[salt.topics--pillar--index] -file_filter = locale//LC_MESSAGES/topics/pillar/index.po -source_file = _build/locale/topics/pillar/index.pot -source_lang = en -source_name = topics/pillar/index.rst - -[salt.topics--projects--index] -file_filter = locale//LC_MESSAGES/topics/projects/index.po -source_file = _build/locale/topics/projects/index.pot -source_lang = en -source_name = topics/projects/index.rst - -[salt.topics--reactor--index] -file_filter = locale//LC_MESSAGES/topics/reactor/index.po -source_file = _build/locale/topics/reactor/index.pot -source_lang = en -source_name = topics/reactor/index.rst - -[salt.topics--releases--0_10_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.10.0.po -source_file = _build/locale/topics/releases/0.10.0.pot -source_lang = en -source_name = topics/releases/0.10.0.rst - -[salt.topics--releases--0_10_2] -file_filter = locale//LC_MESSAGES/topics/releases/0.10.2.po -source_file = _build/locale/topics/releases/0.10.2.pot -source_lang = en -source_name = topics/releases/0.10.2.rst - -[salt.topics--releases--0_10_3] -file_filter = locale//LC_MESSAGES/topics/releases/0.10.3.po -source_file = _build/locale/topics/releases/0.10.3.pot -source_lang = en -source_name = topics/releases/0.10.3.rst - -[salt.topics--releases--0_10_4] -file_filter = locale//LC_MESSAGES/topics/releases/0.10.4.po -source_file = _build/locale/topics/releases/0.10.4.pot -source_lang = en -source_name = topics/releases/0.10.4.rst - -[salt.topics--releases--0_10_5] -file_filter = locale//LC_MESSAGES/topics/releases/0.10.5.po -source_file = _build/locale/topics/releases/0.10.5.pot -source_lang = en -source_name = topics/releases/0.10.5.rst - -[salt.topics--releases--0_11_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.11.0.po -source_file = _build/locale/topics/releases/0.11.0.pot -source_lang = en -source_name = topics/releases/0.11.0.rst - -[salt.topics--releases--0_12_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.12.0.po -source_file = _build/locale/topics/releases/0.12.0.pot -source_lang = en -source_name = topics/releases/0.12.0.rst - -[salt.topics--releases--0_13_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.13.0.po -source_file = _build/locale/topics/releases/0.13.0.pot -source_lang = en -source_name = topics/releases/0.13.0.rst - -[salt.topics--releases--0_14_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.14.0.po -source_file = _build/locale/topics/releases/0.14.0.pot -source_lang = en -source_name = topics/releases/0.14.0.rst - -[salt.topics--releases--0_15_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.15.0.po -source_file = _build/locale/topics/releases/0.15.0.pot -source_lang = en -source_name = topics/releases/0.15.0.rst - -[salt.topics--releases--0_15_1] -file_filter = locale//LC_MESSAGES/topics/releases/0.15.1.po -source_file = _build/locale/topics/releases/0.15.1.pot -source_lang = en -source_name = topics/releases/0.15.1.rst - -[salt.topics--releases--0_16_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.16.0.po -source_file = _build/locale/topics/releases/0.16.0.pot -source_lang = en -source_name = topics/releases/0.16.0.rst - -[salt.topics--releases--0_16_2] -file_filter = locale//LC_MESSAGES/topics/releases/0.16.2.po -source_file = _build/locale/topics/releases/0.16.2.pot -source_lang = en -source_name = topics/releases/0.16.2.rst - -[salt.topics--releases--0_16_3] -file_filter = locale//LC_MESSAGES/topics/releases/0.16.3.po -source_file = _build/locale/topics/releases/0.16.3.pot -source_lang = en -source_name = topics/releases/0.16.3.rst - -[salt.topics--releases--0_16_4] -file_filter = locale//LC_MESSAGES/topics/releases/0.16.4.po -source_file = _build/locale/topics/releases/0.16.4.pot -source_lang = en -source_name = topics/releases/0.16.4.rst - -[salt.topics--releases--0_17_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.17.0.po -source_file = _build/locale/topics/releases/0.17.0.pot -source_lang = en -source_name = topics/releases/0.17.0.rst - -[salt.topics--releases--0_6_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.6.0.po -source_file = _build/locale/topics/releases/0.6.0.pot -source_lang = en -source_name = topics/releases/0.6.0.rst - -[salt.topics--releases--0_7_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.7.0.po -source_file = _build/locale/topics/releases/0.7.0.pot -source_lang = en -source_name = topics/releases/0.7.0.rst - -[salt.topics--releases--0_8_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.8.0.po -source_file = _build/locale/topics/releases/0.8.0.pot -source_lang = en -source_name = topics/releases/0.8.0.rst - -[salt.topics--releases--0_8_7] -file_filter = locale//LC_MESSAGES/topics/releases/0.8.7.po -source_file = _build/locale/topics/releases/0.8.7.pot -source_lang = en -source_name = topics/releases/0.8.7.rst - -[salt.topics--releases--0_8_8] -file_filter = locale//LC_MESSAGES/topics/releases/0.8.8.po -source_file = _build/locale/topics/releases/0.8.8.pot -source_lang = en -source_name = topics/releases/0.8.8.rst - -[salt.topics--releases--0_8_9] -file_filter = locale//LC_MESSAGES/topics/releases/0.8.9.po -source_file = _build/locale/topics/releases/0.8.9.pot -source_lang = en -source_name = topics/releases/0.8.9.rst - -[salt.topics--releases--0_9_0] -file_filter = locale//LC_MESSAGES/topics/releases/0.9.0.po -source_file = _build/locale/topics/releases/0.9.0.pot -source_lang = en -source_name = topics/releases/0.9.0.rst - -[salt.topics--releases--0_9_2] -file_filter = locale//LC_MESSAGES/topics/releases/0.9.2.po -source_file = _build/locale/topics/releases/0.9.2.pot -source_lang = en -source_name = topics/releases/0.9.2.rst - -[salt.topics--releases--0_9_3] -file_filter = locale//LC_MESSAGES/topics/releases/0.9.3.po -source_file = _build/locale/topics/releases/0.9.3.pot -source_lang = en -source_name = topics/releases/0.9.3.rst - -[salt.topics--releases--0_9_4] -file_filter = locale//LC_MESSAGES/topics/releases/0.9.4.po -source_file = _build/locale/topics/releases/0.9.4.pot -source_lang = en -source_name = topics/releases/0.9.4.rst - -[salt.topics--releases--0_9_5] -file_filter = locale//LC_MESSAGES/topics/releases/0.9.5.po -source_file = _build/locale/topics/releases/0.9.5.pot -source_lang = en -source_name = topics/releases/0.9.5.rst - -[salt.topics--releases--0_9_6] -file_filter = locale//LC_MESSAGES/topics/releases/0.9.6.po -source_file = _build/locale/topics/releases/0.9.6.pot -source_lang = en -source_name = topics/releases/0.9.6.rst - -[salt.topics--releases--0_9_7] -file_filter = locale//LC_MESSAGES/topics/releases/0.9.7.po -source_file = _build/locale/topics/releases/0.9.7.pot -source_lang = en -source_name = topics/releases/0.9.7.rst - -[salt.topics--releases--0_9_8] -file_filter = locale//LC_MESSAGES/topics/releases/0.9.8.po -source_file = _build/locale/topics/releases/0.9.8.pot -source_lang = en -source_name = topics/releases/0.9.8.rst - -[salt.topics--releases--0_9_9] -file_filter = locale//LC_MESSAGES/topics/releases/0.9.9.po -source_file = _build/locale/topics/releases/0.9.9.pot -source_lang = en -source_name = topics/releases/0.9.9.rst - -[salt.topics--releases--index] -file_filter = locale//LC_MESSAGES/topics/releases/index.po -source_file = _build/locale/topics/releases/index.pot -source_lang = en -source_name = topics/releases/index.rst - -[salt.topics--ssh--index] -file_filter = locale//LC_MESSAGES/topics/ssh/index.po -source_file = _build/locale/topics/ssh/index.pot -source_lang = en -source_name = topics/ssh/index.rst - -[salt.topics--ssh--roster] -file_filter = locale//LC_MESSAGES/topics/ssh/roster.po -source_file = _build/locale/topics/ssh/roster.pot -source_lang = en -source_name = topics/ssh/roster.rst - -[salt.topics--targeting--batch] -file_filter = locale//LC_MESSAGES/topics/targeting/batch.po -source_file = _build/locale/topics/targeting/batch.pot -source_lang = en -source_name = topics/targeting/batch.rst - -[salt.topics--targeting--compound] -file_filter = locale//LC_MESSAGES/topics/targeting/compound.po -source_file = _build/locale/topics/targeting/compound.pot -source_lang = en -source_name = topics/targeting/compound.rst - -[salt.topics--targeting--globbing] -file_filter = locale//LC_MESSAGES/topics/targeting/globbing.po -source_file = _build/locale/topics/targeting/globbing.pot -source_lang = en -source_name = topics/targeting/globbing.rst - -[salt.topics--targeting--grains] -file_filter = locale//LC_MESSAGES/topics/targeting/grains.po -source_file = _build/locale/topics/targeting/grains.pot -source_lang = en -source_name = topics/targeting/grains.rst - -[salt.topics--targeting--index] -file_filter = locale//LC_MESSAGES/topics/targeting/index.po -source_file = _build/locale/topics/targeting/index.pot -source_lang = en -source_name = topics/targeting/index.rst - -[salt.topics--targeting--nodegroups] -file_filter = locale//LC_MESSAGES/topics/targeting/nodegroups.po -source_file = _build/locale/topics/targeting/nodegroups.pot -source_lang = en -source_name = topics/targeting/nodegroups.rst - -[salt.topics--troubleshooting--index] -file_filter = locale//LC_MESSAGES/topics/troubleshooting/index.po -source_file = _build/locale/topics/troubleshooting/index.pot -source_lang = en -source_name = topics/troubleshooting/index.rst - -[salt.topics--troubleshooting--yaml_idiosyncrasies] -file_filter = locale//LC_MESSAGES/topics/troubleshooting/yaml_idiosyncrasies.po -source_file = _build/locale/topics/troubleshooting/yaml_idiosyncrasies.pot -source_lang = en -source_name = topics/troubleshooting/yaml_idiosyncrasies.rst - -[salt.topics--tutorials--cloud_controller] -file_filter = locale//LC_MESSAGES/topics/tutorials/cloud_controller.po -source_file = _build/locale/topics/tutorials/cloud_controller.pot -source_lang = en -source_name = topics/tutorials/cloud_controller.rst - -[salt.topics--tutorials--cron] -file_filter = locale//LC_MESSAGES/topics/tutorials/cron.po -source_file = _build/locale/topics/tutorials/cron.pot -source_lang = en -source_name = topics/tutorials/cron.rst - -[salt.topics--tutorials--esky] -file_filter = locale//LC_MESSAGES/topics/tutorials/esky.po -source_file = _build/locale/topics/tutorials/esky.pot -source_lang = en -source_name = topics/tutorials/esky.rst - -[salt.topics--tutorials--firewall] -file_filter = locale//LC_MESSAGES/topics/tutorials/firewall.po -source_file = _build/locale/topics/tutorials/firewall.pot -source_lang = en -source_name = topics/tutorials/firewall.rst - -[salt.topics--tutorials--gitfs] -file_filter = locale//LC_MESSAGES/topics/tutorials/gitfs.po -source_file = _build/locale/topics/tutorials/gitfs.pot -source_lang = en -source_name = topics/tutorials/gitfs.rst - -[salt.topics--tutorials--index] -file_filter = locale//LC_MESSAGES/topics/tutorials/index.po -source_file = _build/locale/topics/tutorials/index.pot -source_lang = en -source_name = topics/tutorials/index.rst - -[salt.topics--tutorials--modules] -file_filter = locale//LC_MESSAGES/topics/tutorials/modules.po -source_file = _build/locale/topics/tutorials/modules.pot -source_lang = en -source_name = topics/tutorials/modules.rst - -[salt.topics--tutorials--multimaster] -file_filter = locale//LC_MESSAGES/topics/tutorials/multimaster.po -source_file = _build/locale/topics/tutorials/multimaster.pot -source_lang = en -source_name = topics/tutorials/multimaster.rst - -[salt.topics--tutorials--pillar] -file_filter = locale//LC_MESSAGES/topics/tutorials/pillar.po -source_file = _build/locale/topics/tutorials/pillar.pot -source_lang = en -source_name = topics/tutorials/pillar.rst - -[salt.topics--tutorials--preseed_key] -file_filter = locale//LC_MESSAGES/topics/tutorials/preseed_key.po -source_file = _build/locale/topics/tutorials/preseed_key.pot -source_lang = en -source_name = topics/tutorials/preseed_key.rst - -[salt.topics--tutorials--quickstart] -file_filter = locale//LC_MESSAGES/topics/tutorials/quickstart.po -source_file = _build/locale/topics/tutorials/quickstart.pot -source_lang = en -source_name = topics/tutorials/quickstart.rst - -[salt.topics--tutorials--standalone_minion] -file_filter = locale//LC_MESSAGES/topics/tutorials/standalone_minion.po -source_file = _build/locale/topics/tutorials/standalone_minion.pot -source_lang = en -source_name = topics/tutorials/standalone_minion.rst - -[salt.topics--tutorials--starting_states] -file_filter = locale//LC_MESSAGES/topics/tutorials/starting_states.po -source_file = _build/locale/topics/tutorials/starting_states.pot -source_lang = en -source_name = topics/tutorials/starting_states.rst - -[salt.topics--tutorials--states_pt1] -file_filter = locale//LC_MESSAGES/topics/tutorials/states_pt1.po -source_file = _build/locale/topics/tutorials/states_pt1.pot -source_lang = en -source_name = topics/tutorials/states_pt1.rst - -[salt.topics--tutorials--states_pt2] -file_filter = locale//LC_MESSAGES/topics/tutorials/states_pt2.po -source_file = _build/locale/topics/tutorials/states_pt2.pot -source_lang = en -source_name = topics/tutorials/states_pt2.rst - -[salt.topics--tutorials--states_pt3] -file_filter = locale//LC_MESSAGES/topics/tutorials/states_pt3.po -source_file = _build/locale/topics/tutorials/states_pt3.pot -source_lang = en -source_name = topics/tutorials/states_pt3.rst - -[salt.topics--tutorials--states_pt4] -file_filter = locale//LC_MESSAGES/topics/tutorials/states_pt4.po -source_file = _build/locale/topics/tutorials/states_pt4.pot -source_lang = en -source_name = topics/tutorials/states_pt4.rst - -[salt.topics--tutorials--walkthrough] -file_filter = locale//LC_MESSAGES/topics/tutorials/walkthrough.po -source_file = _build/locale/topics/tutorials/walkthrough.pot -source_lang = en -source_name = topics/tutorials/walkthrough.rst - -[salt.topics--virt--disk] -file_filter = locale//LC_MESSAGES/topics/virt/disk.po -source_file = _build/locale/topics/virt/disk.pot -source_lang = en -source_name = topics/virt/disk.rst - -[salt.topics--virt--index] -file_filter = locale//LC_MESSAGES/topics/virt/index.po -source_file = _build/locale/topics/virt/index.pot -source_lang = en -source_name = topics/virt/index.rst - -[salt.topics--virt--nic] -file_filter = locale//LC_MESSAGES/topics/virt/nic.po -source_file = _build/locale/topics/virt/nic.pot -source_lang = en -source_name = topics/virt/nic.rst - -[salt.ref--auth--all--salt_auth_auto] -file_filter = locale//LC_MESSAGES/ref/auth/all/salt.auth.auto.po -source_file = _build/locale/ref/auth/all/salt.auth.auto.pot -source_lang = en -source_name = ref/auth/all/salt.auth.auto.rst - -[salt.ref--modules--all--salt_modules_chocolatey] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.chocolatey.po -source_file = _build/locale/ref/modules/all/salt.modules.chocolatey.pot -source_lang = en -source_name = ref/modules/all/salt.modules.chocolatey.rst - -[salt.ref--modules--all--salt_modules_dockerio] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.dockerio.po -source_file = _build/locale/ref/modules/all/salt.modules.dockerio.pot -source_lang = en -source_name = ref/modules/all/salt.modules.dockerio.rst - -[salt.ref--modules--all--salt_modules_freebsdports] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.freebsdports.po -source_file = _build/locale/ref/modules/all/salt.modules.freebsdports.pot -source_lang = en -source_name = ref/modules/all/salt.modules.freebsdports.rst - -[salt.ref--modules--all--salt_modules_omapi] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.omapi.po -source_file = _build/locale/ref/modules/all/salt.modules.omapi.pot -source_lang = en -source_name = ref/modules/all/salt.modules.omapi.rst - -[salt.ref--modules--all--salt_modules_powerpath] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.powerpath.po -source_file = _build/locale/ref/modules/all/salt.modules.powerpath.pot -source_lang = en -source_name = ref/modules/all/salt.modules.powerpath.rst - -[salt.ref--modules--all--salt_modules_rdp] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.rdp.po -source_file = _build/locale/ref/modules/all/salt.modules.rdp.pot -source_lang = en -source_name = ref/modules/all/salt.modules.rdp.rst - -[salt.ref--modules--all--salt_modules_riak] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.riak.po -source_file = _build/locale/ref/modules/all/salt.modules.riak.pot -source_lang = en -source_name = ref/modules/all/salt.modules.riak.rst - -[salt.ref--modules--all--salt_modules_saltcloudmod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.saltcloudmod.po -source_file = _build/locale/ref/modules/all/salt.modules.saltcloudmod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.saltcloudmod.rst - -[salt.ref--modules--all--salt_modules_win_autoruns] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_autoruns.po -source_file = _build/locale/ref/modules/all/salt.modules.win_autoruns.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_autoruns.rst - -[salt.ref--modules--all--salt_modules_win_dns_client] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_dns_client.po -source_file = _build/locale/ref/modules/all/salt.modules.win_dns_client.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_dns_client.rst - -[salt.ref--modules--all--salt_modules_win_firewall] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_firewall.po -source_file = _build/locale/ref/modules/all/salt.modules.win_firewall.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_firewall.rst - -[salt.ref--modules--all--salt_modules_win_ip] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_ip.po -source_file = _build/locale/ref/modules/all/salt.modules.win_ip.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_ip.rst - -[salt.ref--modules--all--salt_modules_win_path] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_path.po -source_file = _build/locale/ref/modules/all/salt.modules.win_path.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_path.rst - -[salt.ref--modules--all--salt_modules_win_repo] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_repo.po -source_file = _build/locale/ref/modules/all/salt.modules.win_repo.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_repo.rst - -[salt.ref--modules--all--salt_modules_win_servermanager] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_servermanager.po -source_file = _build/locale/ref/modules/all/salt.modules.win_servermanager.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_servermanager.rst - -[salt.ref--modules--all--salt_modules_win_timezone] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_timezone.po -source_file = _build/locale/ref/modules/all/salt.modules.win_timezone.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_timezone.rst - -[salt.ref--pillar--all--salt_pillar_virtkey] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.virtkey.po -source_file = _build/locale/ref/pillar/all/salt.pillar.virtkey.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.virtkey.rst - -[salt.ref--returners--all--salt_returners_couchdb_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.couchdb_return.po -source_file = _build/locale/ref/returners/all/salt.returners.couchdb_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.couchdb_return.rst - -[salt.ref--returners--all--salt_returners_memcache_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.memcache_return.po -source_file = _build/locale/ref/returners/all/salt.returners.memcache_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.memcache_return.rst - -[salt.ref--runners--all--salt_runners_thin] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.thin.po -source_file = _build/locale/ref/runners/all/salt.runners.thin.pot -source_lang = en -source_name = ref/runners/all/salt.runners.thin.rst - -[salt.ref--states--all--salt_states_ddns] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.ddns.po -source_file = _build/locale/ref/states/all/salt.states.ddns.pot -source_lang = en -source_name = ref/states/all/salt.states.ddns.rst - -[salt.ref--states--all--salt_states_dockerio] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.dockerio.po -source_file = _build/locale/ref/states/all/salt.states.dockerio.pot -source_lang = en -source_name = ref/states/all/salt.states.dockerio.rst - -[salt.ref--states--all--salt_states_keystone] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.keystone.po -source_file = _build/locale/ref/states/all/salt.states.keystone.pot -source_lang = en -source_name = ref/states/all/salt.states.keystone.rst - -[salt.ref--states--all--salt_states_ports] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.ports.po -source_file = _build/locale/ref/states/all/salt.states.ports.pot -source_lang = en -source_name = ref/states/all/salt.states.ports.rst - -[salt.ref--states--all--salt_states_powerpath] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.powerpath.po -source_file = _build/locale/ref/states/all/salt.states.powerpath.pot -source_lang = en -source_name = ref/states/all/salt.states.powerpath.rst - -[salt.ref--states--all--salt_states_process] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.process.po -source_file = _build/locale/ref/states/all/salt.states.process.pot -source_lang = en -source_name = ref/states/all/salt.states.process.rst - -[salt.ref--states--all--salt_states_rabbitmq_cluster] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.rabbitmq_cluster.po -source_file = _build/locale/ref/states/all/salt.states.rabbitmq_cluster.pot -source_lang = en -source_name = ref/states/all/salt.states.rabbitmq_cluster.rst - -[salt.ref--states--all--salt_states_rabbitmq_plugin] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.rabbitmq_plugin.po -source_file = _build/locale/ref/states/all/salt.states.rabbitmq_plugin.pot -source_lang = en -source_name = ref/states/all/salt.states.rabbitmq_plugin.rst - -[salt.ref--states--all--salt_states_rabbitmq_policy] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.rabbitmq_policy.po -source_file = _build/locale/ref/states/all/salt.states.rabbitmq_policy.pot -source_lang = en -source_name = ref/states/all/salt.states.rabbitmq_policy.rst - -[salt.ref--states--all--salt_states_rdp] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.rdp.po -source_file = _build/locale/ref/states/all/salt.states.rdp.pot -source_lang = en -source_name = ref/states/all/salt.states.rdp.rst - -[salt.ref--states--all--salt_states_reg] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.reg.po -source_file = _build/locale/ref/states/all/salt.states.reg.pot -source_lang = en -source_name = ref/states/all/salt.states.reg.rst - -[salt.ref--states--all--salt_states_saltmod] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.saltmod.po -source_file = _build/locale/ref/states/all/salt.states.saltmod.pot -source_lang = en -source_name = ref/states/all/salt.states.saltmod.rst - -[salt.ref--states--all--salt_states_win_network] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.win_network.po -source_file = _build/locale/ref/states/all/salt.states.win_network.pot -source_lang = en -source_name = ref/states/all/salt.states.win_network.rst - -[salt.topics--releases--0_17_1] -file_filter = locale//LC_MESSAGES/topics/releases/0.17.1.po -source_file = _build/locale/topics/releases/0.17.1.pot -source_lang = en -source_name = topics/releases/0.17.1.rst - -[salt.topics--tutorials--halite] -file_filter = locale//LC_MESSAGES/topics/tutorials/halite.po -source_file = _build/locale/topics/tutorials/halite.pot -source_lang = en -source_name = topics/tutorials/halite.rst - -[salt.ref--cli--salt-cloud] -file_filter = locale//LC_MESSAGES/ref/cli/salt-cloud.po -source_file = _build/locale/ref/cli/salt-cloud.pot -source_lang = en -source_name = ref/cli/salt-cloud.rst - -[salt.ref--clouds--all--index] -file_filter = locale//LC_MESSAGES/ref/clouds/all/index.po -source_file = _build/locale/ref/clouds/all/index.pot -source_lang = en -source_name = ref/clouds/all/index.rst - -[salt.ref--clouds--all--salt_cloud_clouds_cloudstack] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.cloudstack.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.cloudstack.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.cloudstack.rst - -[salt.ref--clouds--all--salt_cloud_clouds_digital_ocean] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.digital_ocean.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.digital_ocean.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.digital_ocean.rst - -[salt.ref--clouds--all--salt_cloud_clouds_aliyun] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.aliyun.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.aliyun.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.aliyun.rst - -[salt.ref--clouds--all--salt_cloud_clouds_ec2] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.ec2.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.ec2.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.ec2.rst - -[salt.ref--clouds--all--salt_cloud_clouds_gogrid] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.gogrid.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.gogrid.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.gogrid.rst - -[salt.ref--clouds--all--salt_cloud_clouds_joyent] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.joyent.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.joyent.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.joyent.rst - -[salt.ref--clouds--all--salt_cloud_clouds_linode] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.linode.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.linode.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.linode.rst - -[salt.ref--clouds--all--salt_cloud_clouds_nova] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.nova.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.nova.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.nova.rst - -[salt.ref--clouds--all--salt_cloud_clouds_openstack] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.openstack.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.openstack.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.openstack.rst - -[salt.ref--clouds--all--salt_cloud_clouds_parallels] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.parallels.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.parallels.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.parallels.rst - -[salt.ref--clouds--all--salt_cloud_clouds_rackspace] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.rackspace.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.rackspace.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.rackspace.rst - -[salt.ref--clouds--all--salt_cloud_clouds_saltify] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.saltify.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.saltify.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.saltify.rst - -[salt.ref--clouds--all--salt_cloud_clouds_softlayer] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.softlayer.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.softlayer.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.softlayer.rst - -[salt.topics--cloud--action] -file_filter = locale//LC_MESSAGES/topics/cloud/action.po -source_file = _build/locale/topics/cloud/action.pot -source_lang = en -source_name = topics/cloud/action.rst - -[salt.topics--cloud--aws] -file_filter = locale//LC_MESSAGES/topics/cloud/aws.po -source_file = _build/locale/topics/cloud/aws.pot -source_lang = en -source_name = topics/cloud/aws.rst - -[salt.topics--cloud--cloud] -file_filter = locale//LC_MESSAGES/topics/cloud/cloud.po -source_file = _build/locale/topics/cloud/cloud.pot -source_lang = en -source_name = topics/cloud/cloud.rst - -[salt.topics--cloud--config] -file_filter = locale//LC_MESSAGES/topics/cloud/config.po -source_file = _build/locale/topics/cloud/config.pot -source_lang = en -source_name = topics/cloud/config.rst - -[salt.topics--cloud--deploy] -file_filter = locale//LC_MESSAGES/topics/cloud/deploy.po -source_file = _build/locale/topics/cloud/deploy.pot -source_lang = en -source_name = topics/cloud/deploy.rst - -[salt.topics--cloud--features] -file_filter = locale//LC_MESSAGES/topics/cloud/features.po -source_file = _build/locale/topics/cloud/features.pot -source_lang = en -source_name = topics/cloud/features.rst - -[salt.topics--cloud--function] -file_filter = locale//LC_MESSAGES/topics/cloud/function.po -source_file = _build/locale/topics/cloud/function.pot -source_lang = en -source_name = topics/cloud/function.rst - -[salt.topics--cloud--index] -file_filter = locale//LC_MESSAGES/topics/cloud/index.po -source_file = _build/locale/topics/cloud/index.pot -source_lang = en -source_name = topics/cloud/index.rst - -[salt.topics--cloud--install--index] -file_filter = locale//LC_MESSAGES/topics/cloud/install/index.po -source_file = _build/locale/topics/cloud/install/index.pot -source_lang = en -source_name = topics/cloud/install/index.rst - -[salt.topics--cloud--map] -file_filter = locale//LC_MESSAGES/topics/cloud/map.po -source_file = _build/locale/topics/cloud/map.pot -source_lang = en -source_name = topics/cloud/map.rst - -[salt.topics--cloud--misc] -file_filter = locale//LC_MESSAGES/topics/cloud/misc.po -source_file = _build/locale/topics/cloud/misc.pot -source_lang = en -source_name = topics/cloud/misc.rst - -[salt.topics--cloud--parallels] -file_filter = locale//LC_MESSAGES/topics/cloud/parallels.po -source_file = _build/locale/topics/cloud/parallels.pot -source_lang = en -source_name = topics/cloud/parallels.rst - -[salt.topics--cloud--profiles] -file_filter = locale//LC_MESSAGES/topics/cloud/profiles.po -source_file = _build/locale/topics/cloud/profiles.pot -source_lang = en -source_name = topics/cloud/profiles.rst - -[salt.topics--cloud--rackspace] -file_filter = locale//LC_MESSAGES/topics/cloud/rackspace.po -source_file = _build/locale/topics/cloud/rackspace.pot -source_lang = en -source_name = topics/cloud/rackspace.rst - -[salt.topics--cloud--releases--0_6_0] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/0.6.0.po -source_file = _build/locale/topics/cloud/releases/0.6.0.pot -source_lang = en -source_name = topics/cloud/releases/0.6.0.rst - -[salt.topics--cloud--releases--0_7_0] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/0.7.0.po -source_file = _build/locale/topics/cloud/releases/0.7.0.pot -source_lang = en -source_name = topics/cloud/releases/0.7.0.rst - -[salt.topics--cloud--releases--0_8_0] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/0.8.0.po -source_file = _build/locale/topics/cloud/releases/0.8.0.pot -source_lang = en -source_name = topics/cloud/releases/0.8.0.rst - -[salt.topics--cloud--releases--0_8_1] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/0.8.1.po -source_file = _build/locale/topics/cloud/releases/0.8.1.pot -source_lang = en -source_name = topics/cloud/releases/0.8.1.rst - -[salt.topics--cloud--releases--0_8_2] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/0.8.2.po -source_file = _build/locale/topics/cloud/releases/0.8.2.pot -source_lang = en -source_name = topics/cloud/releases/0.8.2.rst - -[salt.topics--cloud--releases--0_8_3] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/0.8.3.po -source_file = _build/locale/topics/cloud/releases/0.8.3.pot -source_lang = en -source_name = topics/cloud/releases/0.8.3.rst - -[salt.topics--cloud--releases--0_8_4] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/0.8.4.po -source_file = _build/locale/topics/cloud/releases/0.8.4.pot -source_lang = en -source_name = topics/cloud/releases/0.8.4.rst - -[salt.topics--cloud--releases--0_8_5] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/0.8.5.po -source_file = _build/locale/topics/cloud/releases/0.8.5.pot -source_lang = en -source_name = topics/cloud/releases/0.8.5.rst - -[salt.topics--cloud--releases--0_8_6] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/0.8.6.po -source_file = _build/locale/topics/cloud/releases/0.8.6.pot -source_lang = en -source_name = topics/cloud/releases/0.8.6.rst - -[salt.topics--cloud--releases--0_8_7] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/0.8.7.po -source_file = _build/locale/topics/cloud/releases/0.8.7.pot -source_lang = en -source_name = topics/cloud/releases/0.8.7.rst - -[salt.topics--cloud--releases--0_8_9] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/0.8.9.po -source_file = _build/locale/topics/cloud/releases/0.8.9.pot -source_lang = en -source_name = topics/cloud/releases/0.8.9.rst - -[salt.topics--cloud--releases--index] -file_filter = locale//LC_MESSAGES/topics/cloud/releases/index.po -source_file = _build/locale/topics/cloud/releases/index.pot -source_lang = en -source_name = topics/cloud/releases/index.rst - -[salt.topics--cloud--softlayer] -file_filter = locale//LC_MESSAGES/topics/cloud/softlayer.po -source_file = _build/locale/topics/cloud/softlayer.pot -source_lang = en -source_name = topics/cloud/softlayer.rst - -[salt.topics--cloud--windows] -file_filter = locale//LC_MESSAGES/topics/cloud/windows.po -source_file = _build/locale/topics/cloud/windows.pot -source_lang = en -source_name = topics/cloud/windows.rst - -[salt.ref--clouds--all--salt_cloud_clouds_gce] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.gce.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.gce.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.gce.rst - -[salt.ref--clouds--all--salt_cloud_clouds_msazure] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.msazure.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.msazure.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.msazure.rst - -[salt.ref--file_server--all--salt_fileserver_minionfs] -file_filter = locale//LC_MESSAGES/ref/file_server/all/salt.fileserver.minionfs.po -source_file = _build/locale/ref/file_server/all/salt.fileserver.minionfs.pot -source_lang = en -source_name = ref/file_server/all/salt.fileserver.minionfs.rst - -[salt.ref--modules--all--salt_modules_aws_sqs] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.aws_sqs.po -source_file = _build/locale/ref/modules/all/salt.modules.aws_sqs.pot -source_lang = en -source_name = ref/modules/all/salt.modules.aws_sqs.rst - -[salt.ref--modules--all--salt_modules_cloud] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.cloud.po -source_file = _build/locale/ref/modules/all/salt.modules.cloud.pot -source_lang = en -source_name = ref/modules/all/salt.modules.cloud.rst - -[salt.ref--modules--all--salt_modules_composer] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.composer.po -source_file = _build/locale/ref/modules/all/salt.modules.composer.pot -source_lang = en -source_name = ref/modules/all/salt.modules.composer.rst - -[salt.ref--modules--all--salt_modules_debian_ip] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.debian_ip.po -source_file = _build/locale/ref/modules/all/salt.modules.debian_ip.pot -source_lang = en -source_name = ref/modules/all/salt.modules.debian_ip.rst - -[salt.ref--modules--all--salt_modules_gnomedesktop] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.gnomedesktop.po -source_file = _build/locale/ref/modules/all/salt.modules.gnomedesktop.pot -source_lang = en -source_name = ref/modules/all/salt.modules.gnomedesktop.rst - -[salt.ref--modules--all--salt_modules_htpasswd] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.htpasswd.po -source_file = _build/locale/ref/modules/all/salt.modules.htpasswd.pot -source_lang = en -source_name = ref/modules/all/salt.modules.htpasswd.rst - -[salt.ref--modules--all--salt_modules_lvs] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.lvs.po -source_file = _build/locale/ref/modules/all/salt.modules.lvs.pot -source_lang = en -source_name = ref/modules/all/salt.modules.lvs.rst - -[salt.ref--modules--all--salt_modules_memcached] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.memcached.po -source_file = _build/locale/ref/modules/all/salt.modules.memcached.pot -source_lang = en -source_name = ref/modules/all/salt.modules.memcached.rst - -[salt.ref--modules--all--salt_modules_openstack_config] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.openstack_config.po -source_file = _build/locale/ref/modules/all/salt.modules.openstack_config.pot -source_lang = en -source_name = ref/modules/all/salt.modules.openstack_config.rst - -[salt.ref--modules--all--salt_modules_pagerduty] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pagerduty.po -source_file = _build/locale/ref/modules/all/salt.modules.pagerduty.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pagerduty.rst - -[salt.ref--modules--all--salt_modules_xmpp] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.xmpp.po -source_file = _build/locale/ref/modules/all/salt.modules.xmpp.pot -source_lang = en -source_name = ref/modules/all/salt.modules.xmpp.rst - -[salt.ref--modules--all--salt_modules_zcbuildout] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.zcbuildout.po -source_file = _build/locale/ref/modules/all/salt.modules.zcbuildout.pot -source_lang = en -source_name = ref/modules/all/salt.modules.zcbuildout.rst - -[salt.ref--runners--all--salt_runners_lxc] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.lxc.po -source_file = _build/locale/ref/runners/all/salt.runners.lxc.pot -source_lang = en -source_name = ref/runners/all/salt.runners.lxc.rst - -[salt.ref--states--all--salt_states_archive] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.archive.po -source_file = _build/locale/ref/states/all/salt.states.archive.pot -source_lang = en -source_name = ref/states/all/salt.states.archive.rst - -[salt.ref--states--all--salt_states_aws_sqs] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.aws_sqs.po -source_file = _build/locale/ref/states/all/salt.states.aws_sqs.pot -source_lang = en -source_name = ref/states/all/salt.states.aws_sqs.rst - -[salt.ref--states--all--salt_states_cloud] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.cloud.po -source_file = _build/locale/ref/states/all/salt.states.cloud.pot -source_lang = en -source_name = ref/states/all/salt.states.cloud.rst - -[salt.ref--states--all--salt_states_composer] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.composer.po -source_file = _build/locale/ref/states/all/salt.states.composer.pot -source_lang = en -source_name = ref/states/all/salt.states.composer.rst - -[salt.ref--states--all--salt_states_gnomedesktop] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.gnomedesktop.po -source_file = _build/locale/ref/states/all/salt.states.gnomedesktop.pot -source_lang = en -source_name = ref/states/all/salt.states.gnomedesktop.rst - -[salt.ref--states--all--salt_states_lvs_server] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.lvs_server.po -source_file = _build/locale/ref/states/all/salt.states.lvs_server.pot -source_lang = en -source_name = ref/states/all/salt.states.lvs_server.rst - -[salt.ref--states--all--salt_states_lvs_service] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.lvs_service.po -source_file = _build/locale/ref/states/all/salt.states.lvs_service.pot -source_lang = en -source_name = ref/states/all/salt.states.lvs_service.rst - -[salt.ref--states--all--salt_states_memcached] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.memcached.po -source_file = _build/locale/ref/states/all/salt.states.memcached.pot -source_lang = en -source_name = ref/states/all/salt.states.memcached.rst - -[salt.ref--states--all--salt_states_openstack_config] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.openstack_config.po -source_file = _build/locale/ref/states/all/salt.states.openstack_config.pot -source_lang = en -source_name = ref/states/all/salt.states.openstack_config.rst - -[salt.ref--states--all--salt_states_pagerduty] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.pagerduty.po -source_file = _build/locale/ref/states/all/salt.states.pagerduty.pot -source_lang = en -source_name = ref/states/all/salt.states.pagerduty.rst - -[salt.ref--states--all--salt_states_postgres_extension] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.postgres_extension.po -source_file = _build/locale/ref/states/all/salt.states.postgres_extension.pot -source_lang = en -source_name = ref/states/all/salt.states.postgres_extension.rst - -[salt.ref--states--all--salt_states_status] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.status.po -source_file = _build/locale/ref/states/all/salt.states.status.pot -source_lang = en -source_name = ref/states/all/salt.states.status.rst - -[salt.ref--states--all--salt_states_xmpp] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.xmpp.po -source_file = _build/locale/ref/states/all/salt.states.xmpp.pot -source_lang = en -source_name = ref/states/all/salt.states.xmpp.rst - -[salt.ref--states--all--salt_states_zcbuildout] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.zcbuildout.po -source_file = _build/locale/ref/states/all/salt.states.zcbuildout.pot -source_lang = en -source_name = ref/states/all/salt.states.zcbuildout.rst - -[salt.topics--cloud--azure] -file_filter = locale//LC_MESSAGES/topics/cloud/azure.po -source_file = _build/locale/topics/cloud/azure.pot -source_lang = en -source_name = topics/cloud/azure.rst - -[salt.topics--cloud--digitalocean] -file_filter = locale//LC_MESSAGES/topics/cloud/digitalocean.po -source_file = _build/locale/topics/cloud/digitalocean.pot -source_lang = en -source_name = topics/cloud/digitalocean.rst - -[salt.topics--cloud--gce] -file_filter = locale//LC_MESSAGES/topics/cloud/gce.po -source_file = _build/locale/topics/cloud/gce.pot -source_lang = en -source_name = topics/cloud/gce.rst - -[salt.topics--cloud--gogrid] -file_filter = locale//LC_MESSAGES/topics/cloud/gogrid.po -source_file = _build/locale/topics/cloud/gogrid.pot -source_lang = en -source_name = topics/cloud/gogrid.rst - -[salt.topics--cloud--joyent] -file_filter = locale//LC_MESSAGES/topics/cloud/joyent.po -source_file = _build/locale/topics/cloud/joyent.pot -source_lang = en -source_name = topics/cloud/joyent.rst - -[salt.topics--cloud--linode] -file_filter = locale//LC_MESSAGES/topics/cloud/linode.po -source_file = _build/locale/topics/cloud/linode.pot -source_lang = en -source_name = topics/cloud/linode.rst - -[salt.topics--cloud--openstack] -file_filter = locale//LC_MESSAGES/topics/cloud/openstack.po -source_file = _build/locale/topics/cloud/openstack.pot -source_lang = en -source_name = topics/cloud/openstack.rst - -[salt.topics--cloud--salt] -file_filter = locale//LC_MESSAGES/topics/cloud/salt.po -source_file = _build/locale/topics/cloud/salt.pot -source_lang = en -source_name = topics/cloud/salt.rst - -[salt.topics--cloud--troubleshooting] -file_filter = locale//LC_MESSAGES/topics/cloud/troubleshooting.po -source_file = _build/locale/topics/cloud/troubleshooting.pot -source_lang = en -source_name = topics/cloud/troubleshooting.rst - -[salt.topics--releases--0_10_1] -file_filter = locale//LC_MESSAGES/topics/releases/0.10.1.po -source_file = _build/locale/topics/releases/0.10.1.pot -source_lang = en -source_name = topics/releases/0.10.1.rst - -[salt.topics--releases--0_11_1] -file_filter = locale//LC_MESSAGES/topics/releases/0.11.1.po -source_file = _build/locale/topics/releases/0.11.1.pot -source_lang = en -source_name = topics/releases/0.11.1.rst - -[salt.topics--releases--0_12_1] -file_filter = locale//LC_MESSAGES/topics/releases/0.12.1.po -source_file = _build/locale/topics/releases/0.12.1.pot -source_lang = en -source_name = topics/releases/0.12.1.rst - -[salt.topics--releases--0_13_1] -file_filter = locale//LC_MESSAGES/topics/releases/0.13.1.po -source_file = _build/locale/topics/releases/0.13.1.pot -source_lang = en -source_name = topics/releases/0.13.1.rst - -[salt.topics--releases--0_13_2] -file_filter = locale//LC_MESSAGES/topics/releases/0.13.2.po -source_file = _build/locale/topics/releases/0.13.2.pot -source_lang = en -source_name = topics/releases/0.13.2.rst - -[salt.topics--releases--0_13_3] -file_filter = locale//LC_MESSAGES/topics/releases/0.13.3.po -source_file = _build/locale/topics/releases/0.13.3.pot -source_lang = en -source_name = topics/releases/0.13.3.rst - -[salt.topics--releases--0_14_1] -file_filter = locale//LC_MESSAGES/topics/releases/0.14.1.po -source_file = _build/locale/topics/releases/0.14.1.pot -source_lang = en -source_name = topics/releases/0.14.1.rst - -[salt.topics--releases--0_15_2] -file_filter = locale//LC_MESSAGES/topics/releases/0.15.2.po -source_file = _build/locale/topics/releases/0.15.2.pot -source_lang = en -source_name = topics/releases/0.15.2.rst - -[salt.topics--releases--0_15_3] -file_filter = locale//LC_MESSAGES/topics/releases/0.15.3.po -source_file = _build/locale/topics/releases/0.15.3.pot -source_lang = en -source_name = topics/releases/0.15.3.rst - -[salt.topics--releases--0_16_1] -file_filter = locale//LC_MESSAGES/topics/releases/0.16.1.po -source_file = _build/locale/topics/releases/0.16.1.pot -source_lang = en -source_name = topics/releases/0.16.1.rst - -[salt.topics--releases--0_17_2] -file_filter = locale//LC_MESSAGES/topics/releases/0.17.2.po -source_file = _build/locale/topics/releases/0.17.2.pot -source_lang = en -source_name = topics/releases/0.17.2.rst - -[salt.topics--releases--0_17_3] -file_filter = locale//LC_MESSAGES/topics/releases/0.17.3.po -source_file = _build/locale/topics/releases/0.17.3.pot -source_lang = en -source_name = topics/releases/0.17.3.rst - -[salt.topics--releases--0_17_4] -file_filter = locale//LC_MESSAGES/topics/releases/0.17.4.po -source_file = _build/locale/topics/releases/0.17.4.pot -source_lang = en -source_name = topics/releases/0.17.4.rst - -[salt.topics--releases--0_17_5] -file_filter = locale//LC_MESSAGES/topics/releases/0.17.5.po -source_file = _build/locale/topics/releases/0.17.5.pot -source_lang = en -source_name = topics/releases/0.17.5.rst - -[salt.topics--releases--0_9_1] -file_filter = locale//LC_MESSAGES/topics/releases/0.9.1.po -source_file = _build/locale/topics/releases/0.9.1.pot -source_lang = en -source_name = topics/releases/0.9.1.rst - -[salt.topics--releases--2014_1_0] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.0.po -source_file = _build/locale/topics/releases/2014.1.0.pot -source_lang = en -source_name = topics/releases/2014.1.0.rst - -[salt.topics--troubleshooting--master] -file_filter = locale//LC_MESSAGES/topics/troubleshooting/master.po -source_file = _build/locale/topics/troubleshooting/master.pot -source_lang = en -source_name = topics/troubleshooting/master.rst - -[salt.topics--troubleshooting--minion] -file_filter = locale//LC_MESSAGES/topics/troubleshooting/minion.po -source_file = _build/locale/topics/troubleshooting/minion.pot -source_lang = en -source_name = topics/troubleshooting/minion.rst - -[salt.topics--tutorials--minionfs] -file_filter = locale//LC_MESSAGES/topics/tutorials/minionfs.po -source_file = _build/locale/topics/tutorials/minionfs.pot -source_lang = en -source_name = topics/tutorials/minionfs.rst - -[salt.topics--tutorials--salt_bootstrap] -file_filter = locale//LC_MESSAGES/topics/tutorials/salt_bootstrap.po -source_file = _build/locale/topics/tutorials/salt_bootstrap.pot -source_lang = en -source_name = topics/tutorials/salt_bootstrap.rst - -[salt.topics--tutorials--walkthrough_macosx] -file_filter = locale//LC_MESSAGES/topics/tutorials/walkthrough_macosx.po -source_file = _build/locale/topics/tutorials/walkthrough_macosx.pot -source_lang = en -source_name = topics/tutorials/walkthrough_macosx.rst - -[salt.topics--tutorials--writing_tests] -file_filter = locale//LC_MESSAGES/topics/tutorials/writing_tests.po -source_file = _build/locale/topics/tutorials/writing_tests.pot -source_lang = en -source_name = topics/tutorials/writing_tests.rst - -[salt.glossary] -file_filter = locale//LC_MESSAGES/glossary.po -source_file = _build/locale/glossary.pot -source_lang = en -source_name = glossary.rst - -[salt.ref--auth--all--salt_auth_pki] -file_filter = locale//LC_MESSAGES/ref/auth/all/salt.auth.pki.po -source_file = _build/locale/ref/auth/all/salt.auth.pki.pot -source_lang = en -source_name = ref/auth/all/salt.auth.pki.rst - -[salt.ref--clouds--all--salt_cloud_clouds_lxc] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.lxc.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.lxc.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.lxc.rst - -[salt.ref--clouds--all--salt_cloud_clouds_opennebula] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.opennebula.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.opennebula.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.opennebula.rst - -[salt.ref--clouds--all--salt_cloud_clouds_proxmox] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.proxmox.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.proxmox.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.proxmox.rst - -[salt.ref--clouds--all--salt_cloud_clouds_softlayer_hw] -file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.softlayer_hw.po -source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.softlayer_hw.pot -source_lang = en -source_name = ref/clouds/all/salt.cloud.clouds.softlayer_hw.rst - -[salt.ref--configuration--index] -file_filter = locale//LC_MESSAGES/ref/configuration/index.po -source_file = _build/locale/ref/configuration/index.pot -source_lang = en -source_name = ref/configuration/index.rst - -[salt.ref--configuration--nonroot] -file_filter = locale//LC_MESSAGES/ref/configuration/nonroot.po -source_file = _build/locale/ref/configuration/nonroot.pot -source_lang = en -source_name = ref/configuration/nonroot.rst - -[salt.ref--file_server--all--salt_fileserver_svnfs] -file_filter = locale//LC_MESSAGES/ref/file_server/all/salt.fileserver.svnfs.po -source_file = _build/locale/ref/file_server/all/salt.fileserver.svnfs.pot -source_lang = en -source_name = ref/file_server/all/salt.fileserver.svnfs.rst - -[salt.ref--internals--aggregation] -file_filter = locale//LC_MESSAGES/ref/internals/aggregation.po -source_file = _build/locale/ref/internals/aggregation.pot -source_lang = en -source_name = ref/internals/aggregation.rst - -[salt.ref--internals--serializers] -file_filter = locale//LC_MESSAGES/ref/internals/serializers.po -source_file = _build/locale/ref/internals/serializers.pot -source_lang = en -source_name = ref/internals/serializers.rst - -[salt.ref--modules--all--salt_modules_aptpkg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.aptpkg.po -source_file = _build/locale/ref/modules/all/salt.modules.aptpkg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.aptpkg.rst - -[salt.ref--modules--all--salt_modules_blockdev] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.blockdev.po -source_file = _build/locale/ref/modules/all/salt.modules.blockdev.pot -source_lang = en -source_name = ref/modules/all/salt.modules.blockdev.rst - -[salt.ref--modules--all--salt_modules_chef] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.chef.po -source_file = _build/locale/ref/modules/all/salt.modules.chef.pot -source_lang = en -source_name = ref/modules/all/salt.modules.chef.rst - -[salt.ref--modules--all--salt_modules_deb_apache] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.deb_apache.po -source_file = _build/locale/ref/modules/all/salt.modules.deb_apache.pot -source_lang = en -source_name = ref/modules/all/salt.modules.deb_apache.rst - -[salt.ref--modules--all--salt_modules_defaults] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.defaults.po -source_file = _build/locale/ref/modules/all/salt.modules.defaults.pot -source_lang = en -source_name = ref/modules/all/salt.modules.defaults.rst - -[salt.ref--modules--all--salt_modules_environ] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.environ.po -source_file = _build/locale/ref/modules/all/salt.modules.environ.pot -source_lang = en -source_name = ref/modules/all/salt.modules.environ.rst - -[salt.ref--modules--all--salt_modules_etcd_mod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.etcd_mod.po -source_file = _build/locale/ref/modules/all/salt.modules.etcd_mod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.etcd_mod.rst - -[salt.ref--modules--all--salt_modules_genesis] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.genesis.po -source_file = _build/locale/ref/modules/all/salt.modules.genesis.pot -source_lang = en -source_name = ref/modules/all/salt.modules.genesis.rst - -[salt.ref--modules--all--salt_modules_glusterfs] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.glusterfs.po -source_file = _build/locale/ref/modules/all/salt.modules.glusterfs.pot -source_lang = en -source_name = ref/modules/all/salt.modules.glusterfs.rst - -[salt.ref--modules--all--salt_modules_hadoop] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.hadoop.po -source_file = _build/locale/ref/modules/all/salt.modules.hadoop.pot -source_lang = en -source_name = ref/modules/all/salt.modules.hadoop.rst - -[salt.ref--modules--all--salt_modules_haproxyconn] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.haproxyconn.po -source_file = _build/locale/ref/modules/all/salt.modules.haproxyconn.pot -source_lang = en -source_name = ref/modules/all/salt.modules.haproxyconn.rst - -[salt.ref--modules--all--salt_modules_incron] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.incron.po -source_file = _build/locale/ref/modules/all/salt.modules.incron.pot -source_lang = en -source_name = ref/modules/all/salt.modules.incron.rst - -[salt.ref--modules--all--salt_modules_influx] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.influx.po -source_file = _build/locale/ref/modules/all/salt.modules.influx.pot -source_lang = en -source_name = ref/modules/all/salt.modules.influx.rst - -[salt.ref--modules--all--salt_modules_ini_manage] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.ini_manage.po -source_file = _build/locale/ref/modules/all/salt.modules.ini_manage.pot -source_lang = en -source_name = ref/modules/all/salt.modules.ini_manage.rst - -[salt.ref--modules--all--salt_modules_introspect] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.introspect.po -source_file = _build/locale/ref/modules/all/salt.modules.introspect.pot -source_lang = en -source_name = ref/modules/all/salt.modules.introspect.rst - -[salt.ref--modules--all--salt_modules_ipset] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.ipset.po -source_file = _build/locale/ref/modules/all/salt.modules.ipset.pot -source_lang = en -source_name = ref/modules/all/salt.modules.ipset.rst - -[salt.ref--modules--all--salt_modules_junos] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.junos.po -source_file = _build/locale/ref/modules/all/salt.modules.junos.pot -source_lang = en -source_name = ref/modules/all/salt.modules.junos.rst - -[salt.ref--modules--all--salt_modules_macports] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.macports.po -source_file = _build/locale/ref/modules/all/salt.modules.macports.pot -source_lang = en -source_name = ref/modules/all/salt.modules.macports.rst - -[salt.ref--modules--all--salt_modules_nagios] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.nagios.po -source_file = _build/locale/ref/modules/all/salt.modules.nagios.pot -source_lang = en -source_name = ref/modules/all/salt.modules.nagios.rst - -[salt.ref--modules--all--salt_modules_nftables] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.nftables.po -source_file = _build/locale/ref/modules/all/salt.modules.nftables.pot -source_lang = en -source_name = ref/modules/all/salt.modules.nftables.rst - -[salt.ref--modules--all--salt_modules_rest_package] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.rest_package.po -source_file = _build/locale/ref/modules/all/salt.modules.rest_package.pot -source_lang = en -source_name = ref/modules/all/salt.modules.rest_package.rst - -[salt.ref--modules--all--salt_modules_rest_sample] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.rest_sample.po -source_file = _build/locale/ref/modules/all/salt.modules.rest_sample.pot -source_lang = en -source_name = ref/modules/all/salt.modules.rest_sample.rst - -[salt.ref--modules--all--salt_modules_rest_service] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.rest_service.po -source_file = _build/locale/ref/modules/all/salt.modules.rest_service.pot -source_lang = en -source_name = ref/modules/all/salt.modules.rest_service.rst - -[salt.ref--modules--all--salt_modules_rsync] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.rsync.po -source_file = _build/locale/ref/modules/all/salt.modules.rsync.pot -source_lang = en -source_name = ref/modules/all/salt.modules.rsync.rst - -[salt.ref--modules--all--salt_modules_smtp] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.smtp.po -source_file = _build/locale/ref/modules/all/salt.modules.smtp.pot -source_lang = en -source_name = ref/modules/all/salt.modules.smtp.rst - -[salt.ref--modules--all--salt_modules_softwareupdate] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.softwareupdate.po -source_file = _build/locale/ref/modules/all/salt.modules.softwareupdate.pot -source_lang = en -source_name = ref/modules/all/salt.modules.softwareupdate.rst - -[salt.ref--modules--all--salt_modules_swift] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.swift.po -source_file = _build/locale/ref/modules/all/salt.modules.swift.pot -source_lang = en -source_name = ref/modules/all/salt.modules.swift.rst - -[salt.ref--modules--all--salt_modules_uwsgi] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.uwsgi.po -source_file = _build/locale/ref/modules/all/salt.modules.uwsgi.pot -source_lang = en -source_name = ref/modules/all/salt.modules.uwsgi.rst - -[salt.ref--modules--all--salt_modules_znc] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.znc.po -source_file = _build/locale/ref/modules/all/salt.modules.znc.pot -source_lang = en -source_name = ref/modules/all/salt.modules.znc.rst - -[salt.ref--pillar--all--salt_pillar_etcd_pillar] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.etcd_pillar.po -source_file = _build/locale/ref/pillar/all/salt.pillar.etcd_pillar.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.etcd_pillar.rst - -[salt.ref--pillar--all--salt_pillar_mysql] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.mysql.po -source_file = _build/locale/ref/pillar/all/salt.pillar.mysql.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.mysql.rst - -[salt.ref--renderers--all--salt_renderers_msgpack] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.msgpack.po -source_file = _build/locale/ref/renderers/all/salt.renderers.msgpack.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.msgpack.rst - -[salt.ref--returners--all--salt_returners_etcd_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.etcd_return.po -source_file = _build/locale/ref/returners/all/salt.returners.etcd_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.etcd_return.rst - -[salt.ref--runners--all--salt_runners_cloud] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.cloud.po -source_file = _build/locale/ref/runners/all/salt.runners.cloud.pot -source_lang = en -source_name = ref/runners/all/salt.runners.cloud.rst - -[salt.ref--runners--all--salt_runners_error] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.error.po -source_file = _build/locale/ref/runners/all/salt.runners.error.pot -source_lang = en -source_name = ref/runners/all/salt.runners.error.rst - -[salt.ref--runners--all--salt_runners_git_pillar] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.git_pillar.po -source_file = _build/locale/ref/runners/all/salt.runners.git_pillar.pot -source_lang = en -source_name = ref/runners/all/salt.runners.git_pillar.rst - -[salt.ref--runners--all--salt_runners_mine] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.mine.po -source_file = _build/locale/ref/runners/all/salt.runners.mine.pot -source_lang = en -source_name = ref/runners/all/salt.runners.mine.rst - -[salt.ref--runners--all--salt_runners_pillar] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.pillar.po -source_file = _build/locale/ref/runners/all/salt.runners.pillar.pot -source_lang = en -source_name = ref/runners/all/salt.runners.pillar.rst - -[salt.ref--runners--all--salt_runners_survey] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.survey.po -source_file = _build/locale/ref/runners/all/salt.runners.survey.pot -source_lang = en -source_name = ref/runners/all/salt.runners.survey.rst - -[salt.ref--states--all--salt_states_apache] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.apache.po -source_file = _build/locale/ref/states/all/salt.states.apache.pot -source_lang = en -source_name = ref/states/all/salt.states.apache.rst - -[salt.ref--states--all--salt_states_at] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.at.po -source_file = _build/locale/ref/states/all/salt.states.at.pot -source_lang = en -source_name = ref/states/all/salt.states.at.rst - -[salt.ref--states--all--salt_states_blockdev] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.blockdev.po -source_file = _build/locale/ref/states/all/salt.states.blockdev.pot -source_lang = en -source_name = ref/states/all/salt.states.blockdev.rst - -[salt.ref--states--all--salt_states_environ] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.environ.po -source_file = _build/locale/ref/states/all/salt.states.environ.pot -source_lang = en -source_name = ref/states/all/salt.states.environ.rst - -[salt.ref--states--all--salt_states_event] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.event.po -source_file = _build/locale/ref/states/all/salt.states.event.pot -source_lang = en -source_name = ref/states/all/salt.states.event.rst - -[salt.ref--states--all--salt_states_glusterfs] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.glusterfs.po -source_file = _build/locale/ref/states/all/salt.states.glusterfs.pot -source_lang = en -source_name = ref/states/all/salt.states.glusterfs.rst - -[salt.ref--states--all--salt_states_htpasswd] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.htpasswd.po -source_file = _build/locale/ref/states/all/salt.states.htpasswd.pot -source_lang = en -source_name = ref/states/all/salt.states.htpasswd.rst - -[salt.ref--states--all--salt_states_incron] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.incron.po -source_file = _build/locale/ref/states/all/salt.states.incron.pot -source_lang = en -source_name = ref/states/all/salt.states.incron.rst - -[salt.ref--states--all--salt_states_influxdb_database] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.influxdb_database.po -source_file = _build/locale/ref/states/all/salt.states.influxdb_database.pot -source_lang = en -source_name = ref/states/all/salt.states.influxdb_database.rst - -[salt.ref--states--all--salt_states_influxdb_user] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.influxdb_user.po -source_file = _build/locale/ref/states/all/salt.states.influxdb_user.pot -source_lang = en -source_name = ref/states/all/salt.states.influxdb_user.rst - -[salt.ref--states--all--salt_states_ini_manage] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.ini_manage.po -source_file = _build/locale/ref/states/all/salt.states.ini_manage.pot -source_lang = en -source_name = ref/states/all/salt.states.ini_manage.rst - -[salt.ref--states--all--salt_states_ipset] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.ipset.po -source_file = _build/locale/ref/states/all/salt.states.ipset.pot -source_lang = en -source_name = ref/states/all/salt.states.ipset.rst - -[salt.ref--states--all--salt_states_lxc] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.lxc.po -source_file = _build/locale/ref/states/all/salt.states.lxc.pot -source_lang = en -source_name = ref/states/all/salt.states.lxc.rst - -[salt.ref--states--all--salt_states_modjk] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.modjk.po -source_file = _build/locale/ref/states/all/salt.states.modjk.pot -source_lang = en -source_name = ref/states/all/salt.states.modjk.rst - -[salt.ref--states--all--salt_states_mysql_query] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.mysql_query.po -source_file = _build/locale/ref/states/all/salt.states.mysql_query.pot -source_lang = en -source_name = ref/states/all/salt.states.mysql_query.rst - -[salt.ref--states--all--salt_states_nftables] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.nftables.po -source_file = _build/locale/ref/states/all/salt.states.nftables.pot -source_lang = en -source_name = ref/states/all/salt.states.nftables.rst - -[salt.ref--states--all--salt_states_smtp] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.smtp.po -source_file = _build/locale/ref/states/all/salt.states.smtp.pot -source_lang = en -source_name = ref/states/all/salt.states.smtp.rst - -[salt.ref--states--all--salt_states_test] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.test.po -source_file = _build/locale/ref/states/all/salt.states.test.pot -source_lang = en -source_name = ref/states/all/salt.states.test.rst - -[salt.ref--states--compiler_ordering] -file_filter = locale//LC_MESSAGES/ref/states/compiler_ordering.po -source_file = _build/locale/ref/states/compiler_ordering.pot -source_lang = en -source_name = ref/states/compiler_ordering.rst - -[salt.ref--wheel--all--salt_wheel_error] -file_filter = locale//LC_MESSAGES/ref/wheel/all/salt.wheel.error.po -source_file = _build/locale/ref/wheel/all/salt.wheel.error.pot -source_lang = en -source_name = ref/wheel/all/salt.wheel.error.rst - -[salt.topics--best_practices] -file_filter = locale//LC_MESSAGES/topics/best_practices.po -source_file = _build/locale/topics/best_practices.pot -source_lang = en -source_name = topics/best_practices.rst - -[salt.topics--cloud--hpcloud] -file_filter = locale//LC_MESSAGES/topics/cloud/hpcloud.po -source_file = _build/locale/topics/cloud/hpcloud.pot -source_lang = en -source_name = topics/cloud/hpcloud.rst - -[salt.topics--cloud--lxc] -file_filter = locale//LC_MESSAGES/topics/cloud/lxc.po -source_file = _build/locale/topics/cloud/lxc.pot -source_lang = en -source_name = topics/cloud/lxc.rst - -[salt.topics--cloud--proxmox] -file_filter = locale//LC_MESSAGES/topics/cloud/proxmox.po -source_file = _build/locale/topics/cloud/proxmox.pot -source_lang = en -source_name = topics/cloud/proxmox.rst - -[salt.topics--development--conventions--formulas] -file_filter = locale//LC_MESSAGES/topics/development/conventions/formulas.po -source_file = _build/locale/topics/development/conventions/formulas.pot -source_lang = en -source_name = topics/development/conventions/formulas.rst - -[salt.topics--development--conventions--index] -file_filter = locale//LC_MESSAGES/topics/development/conventions/index.po -source_file = _build/locale/topics/development/conventions/index.pot -source_lang = en -source_name = topics/development/conventions/index.rst - -[salt.topics--development--conventions--packaging] -file_filter = locale//LC_MESSAGES/topics/development/conventions/packaging.po -source_file = _build/locale/topics/development/conventions/packaging.pot -source_lang = en -source_name = topics/development/conventions/packaging.rst - -[salt.topics--development--conventions--release] -file_filter = locale//LC_MESSAGES/topics/development/conventions/release.po -source_file = _build/locale/topics/development/conventions/release.pot -source_lang = en -source_name = topics/development/conventions/release.rst - -[salt.topics--development--conventions--style] -file_filter = locale//LC_MESSAGES/topics/development/conventions/style.po -source_file = _build/locale/topics/development/conventions/style.pot -source_lang = en -source_name = topics/development/conventions/style.rst - -[salt.topics--development--git--index] -file_filter = locale//LC_MESSAGES/topics/development/git/index.po -source_file = _build/locale/topics/development/git/index.pot -source_lang = en -source_name = topics/development/git/index.rst - -[salt.topics--development--hacking] -file_filter = locale//LC_MESSAGES/topics/development/hacking.po -source_file = _build/locale/topics/development/hacking.pot -source_lang = en -source_name = topics/development/hacking.rst - -[salt.topics--development--salt_projects] -file_filter = locale//LC_MESSAGES/topics/development/salt_projects.po -source_file = _build/locale/topics/development/salt_projects.pot -source_lang = en -source_name = topics/development/salt_projects.rst - -[salt.topics--development--tests--index] -file_filter = locale//LC_MESSAGES/topics/development/tests/index.po -source_file = _build/locale/topics/development/tests/index.pot -source_lang = en -source_name = topics/development/tests/index.rst - -[salt.topics--development--tests--integration] -file_filter = locale//LC_MESSAGES/topics/development/tests/integration.po -source_file = _build/locale/topics/development/tests/integration.pot -source_lang = en -source_name = topics/development/tests/integration.rst - -[salt.topics--development--tests--unit] -file_filter = locale//LC_MESSAGES/topics/development/tests/unit.po -source_file = _build/locale/topics/development/tests/unit.pot -source_lang = en -source_name = topics/development/tests/unit.rst - -[salt.topics--development--topology] -file_filter = locale//LC_MESSAGES/topics/development/topology.po -source_file = _build/locale/topics/development/topology.pot -source_lang = en -source_name = topics/development/topology.rst - -[salt.topics--development--translating] -file_filter = locale//LC_MESSAGES/topics/development/translating.po -source_file = _build/locale/topics/development/translating.pot -source_lang = en -source_name = topics/development/translating.rst - -[salt.topics--releases--2014_1_1] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.1.po -source_file = _build/locale/topics/releases/2014.1.1.pot -source_lang = en -source_name = topics/releases/2014.1.1.rst - -[salt.topics--releases--2014_1_2] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.2.po -source_file = _build/locale/topics/releases/2014.1.2.pot -source_lang = en -source_name = topics/releases/2014.1.2.rst - -[salt.topics--topology--index] -file_filter = locale//LC_MESSAGES/topics/topology/index.po -source_file = _build/locale/topics/topology/index.pot -source_lang = en -source_name = topics/topology/index.rst - -[salt.topics--topology--proxyminion--index] -file_filter = locale//LC_MESSAGES/topics/topology/proxyminion/index.po -source_file = _build/locale/topics/topology/proxyminion/index.pot -source_lang = en -source_name = topics/topology/proxyminion/index.rst - -[salt.topics--topology--syndic] -file_filter = locale//LC_MESSAGES/topics/topology/syndic.po -source_file = _build/locale/topics/topology/syndic.pot -source_lang = en -source_name = topics/topology/syndic.rst - -[salt.topics--tutorials--states_pt5] -file_filter = locale//LC_MESSAGES/topics/tutorials/states_pt5.po -source_file = _build/locale/topics/tutorials/states_pt5.pot -source_lang = en -source_name = topics/tutorials/states_pt5.rst - -[salt.topics--windows--index] -file_filter = locale//LC_MESSAGES/topics/windows/index.po -source_file = _build/locale/topics/windows/index.pot -source_lang = en -source_name = topics/windows/index.rst - -[salt.topics--windows--windows-package-manager] -file_filter = locale//LC_MESSAGES/topics/windows/windows-package-manager.po -source_file = _build/locale/topics/windows/windows-package-manager.pot -source_lang = en -source_name = topics/windows/windows-package-manager.rst - -[salt.topics--windows--windows-specific-behavior] -file_filter = locale//LC_MESSAGES/topics/windows/windows-specific-behavior.po -source_file = _build/locale/topics/windows/windows-specific-behavior.pot -source_lang = en -source_name = topics/windows/windows-specific-behavior.rst - -[salt.topics--yaml--index] -file_filter = locale//LC_MESSAGES/topics/yaml/index.po -source_file = _build/locale/topics/yaml/index.pot -source_lang = en -source_name = topics/yaml/index.rst - -[salt.topics--development--contributing] -file_filter = locale//LC_MESSAGES/topics/development/contributing.po -source_file = _build/locale/topics/development/contributing.pot -source_lang = en -source_name = topics/development/contributing.rst - -[salt.topics--releases--2014_1_3] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.3.po -source_file = _build/locale/topics/releases/2014.1.3.pot -source_lang = en -source_name = topics/releases/2014.1.3.rst - -[salt.topics--cloud--vexxhost] -file_filter = locale//LC_MESSAGES/topics/cloud/vexxhost.po -source_file = _build/locale/topics/cloud/vexxhost.pot -source_lang = en -source_name = topics/cloud/vexxhost.rst - -[salt.topics--event--master_events] -file_filter = locale//LC_MESSAGES/topics/event/master_events.po -source_file = _build/locale/topics/event/master_events.pot -source_lang = en -source_name = topics/event/master_events.rst - -[salt.ref--states--aggregate] -file_filter = locale//LC_MESSAGES/ref/states/aggregate.po -source_file = _build/locale/ref/states/aggregate.pot -source_lang = en -source_name = ref/states/aggregate.rst - -[salt.topics--releases--2014_1_4] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.4.po -source_file = _build/locale/topics/releases/2014.1.4.pot -source_lang = en -source_name = topics/releases/2014.1.4.rst - -[salt.ref--modules--all--salt_modules_redismod] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.redismod.po -source_file = _build/locale/ref/modules/all/salt.modules.redismod.pot -source_lang = en -source_name = ref/modules/all/salt.modules.redismod.rst - -[salt.ref--pillar--all--salt_pillar_redismod] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.redismod.po -source_file = _build/locale/ref/pillar/all/salt.pillar.redismod.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.redismod.rst - -[salt.ref--states--all--salt_states_redismod] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.redismod.po -source_file = _build/locale/ref/states/all/salt.states.redismod.pot -source_lang = en -source_name = ref/states/all/salt.states.redismod.rst - -[salt.ref--modules--all--salt_modules_boto_asg] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.boto_asg.po -source_file = _build/locale/ref/modules/all/salt.modules.boto_asg.pot -source_lang = en -source_name = ref/modules/all/salt.modules.boto_asg.rst - -[salt.ref--modules--all--salt_modules_boto_elb] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.boto_elb.po -source_file = _build/locale/ref/modules/all/salt.modules.boto_elb.pot -source_lang = en -source_name = ref/modules/all/salt.modules.boto_elb.rst - -[salt.ref--modules--all--salt_modules_boto_iam] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.boto_iam.po -source_file = _build/locale/ref/modules/all/salt.modules.boto_iam.pot -source_lang = en -source_name = ref/modules/all/salt.modules.boto_iam.rst - -[salt.ref--modules--all--salt_modules_boto_route53] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.boto_route53.po -source_file = _build/locale/ref/modules/all/salt.modules.boto_route53.pot -source_lang = en -source_name = ref/modules/all/salt.modules.boto_route53.rst - -[salt.ref--modules--all--salt_modules_boto_secgroup] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.boto_secgroup.po -source_file = _build/locale/ref/modules/all/salt.modules.boto_secgroup.pot -source_lang = en -source_name = ref/modules/all/salt.modules.boto_secgroup.rst - -[salt.ref--modules--all--salt_modules_boto_sqs] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.boto_sqs.po -source_file = _build/locale/ref/modules/all/salt.modules.boto_sqs.pot -source_lang = en -source_name = ref/modules/all/salt.modules.boto_sqs.rst - -[salt.ref--states--all--salt_states_boto_asg] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.boto_asg.po -source_file = _build/locale/ref/states/all/salt.states.boto_asg.pot -source_lang = en -source_name = ref/states/all/salt.states.boto_asg.rst - -[salt.ref--states--all--salt_states_boto_elb] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.boto_elb.po -source_file = _build/locale/ref/states/all/salt.states.boto_elb.pot -source_lang = en -source_name = ref/states/all/salt.states.boto_elb.rst - -[salt.ref--states--all--salt_states_boto_iam_role] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.boto_iam_role.po -source_file = _build/locale/ref/states/all/salt.states.boto_iam_role.pot -source_lang = en -source_name = ref/states/all/salt.states.boto_iam_role.rst - -[salt.ref--states--all--salt_states_boto_lc] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.boto_lc.po -source_file = _build/locale/ref/states/all/salt.states.boto_lc.pot -source_lang = en -source_name = ref/states/all/salt.states.boto_lc.rst - -[salt.ref--states--all--salt_states_boto_route53] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.boto_route53.po -source_file = _build/locale/ref/states/all/salt.states.boto_route53.pot -source_lang = en -source_name = ref/states/all/salt.states.boto_route53.rst - -[salt.ref--states--all--salt_states_boto_secgroup] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.boto_secgroup.po -source_file = _build/locale/ref/states/all/salt.states.boto_secgroup.pot -source_lang = en -source_name = ref/states/all/salt.states.boto_secgroup.rst - -[salt.ref--states--all--salt_states_boto_sqs] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.boto_sqs.po -source_file = _build/locale/ref/states/all/salt.states.boto_sqs.pot -source_lang = en -source_name = ref/states/all/salt.states.boto_sqs.rst - -[salt.topics--cloud--aliyun] -file_filter = locale//LC_MESSAGES/topics/cloud/aliyun.po -source_file = _build/locale/topics/cloud/aliyun.pot -source_lang = en -source_name = topics/cloud/aliyun.rst - -[salt.topics--development--conventions--documentation] -file_filter = locale//LC_MESSAGES/topics/development/conventions/documentation.po -source_file = _build/locale/topics/development/conventions/documentation.pot -source_lang = en -source_name = topics/development/conventions/documentation.rst - -[salt.ref--states--altering_states] -file_filter = locale//LC_MESSAGES/ref/states/altering_states.po -source_file = _build/locale/ref/states/altering_states.pot -source_lang = en -source_name = ref/states/altering_states.rst - -[salt.ref--renderers--all--salt_renderers_yamlex] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.yamlex.po -source_file = _build/locale/ref/renderers/all/salt.renderers.yamlex.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.yamlex.rst - -[salt.ref--modules--all--salt_modules_postfix] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.postfix.po -source_file = _build/locale/ref/modules/all/salt.modules.postfix.pot -source_lang = en -source_name = ref/modules/all/salt.modules.postfix.rst - -[salt.ref--modules--all--salt_modules_varnish] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.varnish.po -source_file = _build/locale/ref/modules/all/salt.modules.varnish.pot -source_lang = en -source_name = ref/modules/all/salt.modules.varnish.rst - -[salt.ref--modules--all--salt_modules_serverdensity_device] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.serverdensity_device.po -source_file = _build/locale/ref/modules/all/salt.modules.serverdensity_device.pot -source_lang = en -source_name = ref/modules/all/salt.modules.serverdensity_device.rst - -[salt.ref--states--all--salt_states_serverdensity_device] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.serverdensity_device.po -source_file = _build/locale/ref/states/all/salt.states.serverdensity_device.pot -source_lang = en -source_name = ref/states/all/salt.states.serverdensity_device.rst - -[salt.ref--cli--salt-api] -file_filter = locale//LC_MESSAGES/ref/cli/salt-api.po -source_file = _build/locale/ref/cli/salt-api.pot -source_lang = en -source_name = ref/cli/salt-api.rst - -[salt.ref--netapi--all--index] -file_filter = locale//LC_MESSAGES/ref/netapi/all/index.po -source_file = _build/locale/ref/netapi/all/index.pot -source_lang = en -source_name = ref/netapi/all/index.rst - -[salt.ref--netapi--all--salt_netapi_rest_cherrypy] -file_filter = locale//LC_MESSAGES/ref/netapi/all/salt.netapi.rest_cherrypy.po -source_file = _build/locale/ref/netapi/all/salt.netapi.rest_cherrypy.pot -source_lang = en -source_name = ref/netapi/all/salt.netapi.rest_cherrypy.rst - -[salt.ref--netapi--all--salt_netapi_rest_tornado] -file_filter = locale//LC_MESSAGES/ref/netapi/all/salt.netapi.rest_tornado.po -source_file = _build/locale/ref/netapi/all/salt.netapi.rest_tornado.pot -source_lang = en -source_name = ref/netapi/all/salt.netapi.rest_tornado.rst - -[salt.ref--netapi--all--salt_netapi_rest_wsgi] -file_filter = locale//LC_MESSAGES/ref/netapi/all/salt.netapi.rest_wsgi.po -source_file = _build/locale/ref/netapi/all/salt.netapi.rest_wsgi.pot -source_lang = en -source_name = ref/netapi/all/salt.netapi.rest_wsgi.rst - -[salt.ref--pillar--all--salt_pillar_foreman] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.foreman.po -source_file = _build/locale/ref/pillar/all/salt.pillar.foreman.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.foreman.rst - -[salt.topics--netapi--index] -file_filter = locale//LC_MESSAGES/topics/netapi/index.po -source_file = _build/locale/topics/netapi/index.pot -source_lang = en -source_name = topics/netapi/index.rst - -[salt.topics--netapi--writing] -file_filter = locale//LC_MESSAGES/topics/netapi/writing.po -source_file = _build/locale/topics/netapi/writing.pot -source_lang = en -source_name = topics/netapi/writing.rst - -[salt.topics--releases--2014_1_5] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.5.po -source_file = _build/locale/topics/releases/2014.1.5.pot -source_lang = en -source_name = topics/releases/2014.1.5.rst - -[salt.topics--releases--saltapi--0_5_0] -file_filter = locale//LC_MESSAGES/topics/releases/saltapi/0.5.0.po -source_file = _build/locale/topics/releases/saltapi/0.5.0.pot -source_lang = en -source_name = topics/releases/saltapi/0.5.0.rst - -[salt.topics--releases--saltapi--0_6_0] -file_filter = locale//LC_MESSAGES/topics/releases/saltapi/0.6.0.po -source_file = _build/locale/topics/releases/saltapi/0.6.0.pot -source_lang = en -source_name = topics/releases/saltapi/0.6.0.rst - -[salt.topics--releases--saltapi--0_7_0] -file_filter = locale//LC_MESSAGES/topics/releases/saltapi/0.7.0.po -source_file = _build/locale/topics/releases/saltapi/0.7.0.pot -source_lang = en -source_name = topics/releases/saltapi/0.7.0.rst - -[salt.topics--releases--saltapi--0_7_5] -file_filter = locale//LC_MESSAGES/topics/releases/saltapi/0.7.5.po -source_file = _build/locale/topics/releases/saltapi/0.7.5.pot -source_lang = en -source_name = topics/releases/saltapi/0.7.5.rst - -[salt.topics--releases--saltapi--0_8_0] -file_filter = locale//LC_MESSAGES/topics/releases/saltapi/0.8.0.po -source_file = _build/locale/topics/releases/saltapi/0.8.0.pot -source_lang = en -source_name = topics/releases/saltapi/0.8.0.rst - -[salt.topics--releases--saltapi--0_8_2] -file_filter = locale//LC_MESSAGES/topics/releases/saltapi/0.8.2.po -source_file = _build/locale/topics/releases/saltapi/0.8.2.pot -source_lang = en -source_name = topics/releases/saltapi/0.8.2.rst - -[salt.topics--releases--saltapi--0_8_3] -file_filter = locale//LC_MESSAGES/topics/releases/saltapi/0.8.3.po -source_file = _build/locale/topics/releases/saltapi/0.8.3.pot -source_lang = en -source_name = topics/releases/saltapi/0.8.3.rst - -[salt.topics--releases--saltapi--0_8_4] -file_filter = locale//LC_MESSAGES/topics/releases/saltapi/0.8.4.po -source_file = _build/locale/topics/releases/saltapi/0.8.4.pot -source_lang = en -source_name = topics/releases/saltapi/0.8.4.rst - -[salt.topics--releases--saltapi--index] -file_filter = locale//LC_MESSAGES/topics/releases/saltapi/index.po -source_file = _build/locale/topics/releases/saltapi/index.pot -source_lang = en -source_name = topics/releases/saltapi/index.rst - -[salt.topics--sdb--index] -file_filter = locale//LC_MESSAGES/topics/sdb/index.po -source_file = _build/locale/topics/sdb/index.pot -source_lang = en -source_name = topics/sdb/index.rst - -[salt.topics--targeting--ipcidr] -file_filter = locale//LC_MESSAGES/topics/targeting/ipcidr.po -source_file = _build/locale/topics/targeting/ipcidr.pot -source_lang = en -source_name = topics/targeting/ipcidr.rst - -[salt.ref--modules--all--salt_modules_boto_elasticache] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.boto_elasticache.po -source_file = _build/locale/ref/modules/all/salt.modules.boto_elasticache.pot -source_lang = en -source_name = ref/modules/all/salt.modules.boto_elasticache.rst - -[salt.ref--roster--all--index] -file_filter = locale//LC_MESSAGES/ref/roster/all/index.po -source_file = _build/locale/ref/roster/all/index.pot -source_lang = en -source_name = ref/roster/all/index.rst - -[salt.ref--roster--all--salt_roster_flat] -file_filter = locale//LC_MESSAGES/ref/roster/all/salt.roster.flat.po -source_file = _build/locale/ref/roster/all/salt.roster.flat.pot -source_lang = en -source_name = ref/roster/all/salt.roster.flat.rst - -[salt.ref--roster--all--salt_roster_scan] -file_filter = locale//LC_MESSAGES/ref/roster/all/salt.roster.scan.po -source_file = _build/locale/ref/roster/all/salt.roster.scan.pot -source_lang = en -source_name = ref/roster/all/salt.roster.scan.rst - -[salt.ref--states--all--salt_states_apache_module] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.apache_module.po -source_file = _build/locale/ref/states/all/salt.states.apache_module.pot -source_lang = en -source_name = ref/states/all/salt.states.apache_module.rst - -[salt.ref--states--all--salt_states_boto_elasticache] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.boto_elasticache.po -source_file = _build/locale/ref/states/all/salt.states.boto_elasticache.pot -source_lang = en -source_name = ref/states/all/salt.states.boto_elasticache.rst - -[salt.topics--releases--2014_1_6] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.6.po -source_file = _build/locale/topics/releases/2014.1.6.pot -source_lang = en -source_name = topics/releases/2014.1.6.rst - -[salt.topics--releases--version_numbers] -file_filter = locale//LC_MESSAGES/topics/releases/version_numbers.po -source_file = _build/locale/topics/releases/version_numbers.pot -source_lang = en -source_name = topics/releases/version_numbers.rst - -[salt.security--index] -file_filter = locale//LC_MESSAGES/security/index.po -source_file = _build/locale/security/index.pot -source_lang = en -source_name = security/index.rst - -[salt.ref--states--global_state_arguments] -file_filter = locale//LC_MESSAGES/ref/states/global_state_arguments.po -source_file = _build/locale/ref/states/global_state_arguments.pot -source_lang = en -source_name = ref/states/global_state_arguments.rst - -[salt.topics--cloud--basic] -file_filter = locale//LC_MESSAGES/topics/cloud/basic.po -source_file = _build/locale/topics/cloud/basic.pot -source_lang = en -source_name = topics/cloud/basic.rst - -[salt.topics--releases--2014_1_7] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.7.po -source_file = _build/locale/topics/releases/2014.1.7.pot -source_lang = en -source_name = topics/releases/2014.1.7.rst - -[salt.topics--releases--2014_7_0] -file_filter = locale//LC_MESSAGES/topics/releases/2014.7.0.po -source_file = _build/locale/topics/releases/2014.7.0.pot -source_lang = en -source_name = topics/releases/2014.7.0.rst - -[salt.topics--tutorials--intro_scale] -file_filter = locale//LC_MESSAGES/topics/tutorials/intro_scale.po -source_file = _build/locale/topics/tutorials/intro_scale.pot -source_lang = en -source_name = topics/tutorials/intro_scale.rst - -[salt.topics--tutorials--syslog_ng-state-usage] -file_filter = locale//LC_MESSAGES/topics/tutorials/syslog_ng-state-usage.po -source_file = _build/locale/topics/tutorials/syslog_ng-state-usage.pot -source_lang = en -source_name = topics/tutorials/syslog_ng-state-usage.rst - -[salt.ref--modules--all--salt_modules_twilio_notify] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.twilio_notify.po -source_file = _build/locale/ref/modules/all/salt.modules.twilio_notify.pot -source_lang = en -source_name = ref/modules/all/salt.modules.twilio_notify.rst - -[salt.ref--modules--all--salt_modules_hashutil] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.hashutil.po -source_file = _build/locale/ref/modules/all/salt.modules.hashutil.pot -source_lang = en -source_name = ref/modules/all/salt.modules.hashutil.rst - -[salt.ref--modules--all--salt_modules_logadm] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.logadm.po -source_file = _build/locale/ref/modules/all/salt.modules.logadm.pot -source_lang = en -source_name = ref/modules/all/salt.modules.logadm.rst - -[salt.ref--modules--all--salt_modules_mod_random] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.mod_random.po -source_file = _build/locale/ref/modules/all/salt.modules.mod_random.pot -source_lang = en -source_name = ref/modules/all/salt.modules.mod_random.rst - -[salt.ref--modules--all--salt_modules_oracle] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.oracle.po -source_file = _build/locale/ref/modules/all/salt.modules.oracle.pot -source_lang = en -source_name = ref/modules/all/salt.modules.oracle.rst - -[salt.ref--modules--all--salt_modules_pyenv] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.pyenv.po -source_file = _build/locale/ref/modules/all/salt.modules.pyenv.pot -source_lang = en -source_name = ref/modules/all/salt.modules.pyenv.rst - -[salt.ref--modules--all--salt_modules_schedule] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.schedule.po -source_file = _build/locale/ref/modules/all/salt.modules.schedule.pot -source_lang = en -source_name = ref/modules/all/salt.modules.schedule.rst - -[salt.ref--modules--all--salt_modules_sensors] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.sensors.po -source_file = _build/locale/ref/modules/all/salt.modules.sensors.pot -source_lang = en -source_name = ref/modules/all/salt.modules.sensors.rst - -[salt.ref--modules--all--salt_modules_win_update] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.win_update.po -source_file = _build/locale/ref/modules/all/salt.modules.win_update.pot -source_lang = en -source_name = ref/modules/all/salt.modules.win_update.rst - -[salt.ref--pillar--all--salt_pillar_cmd_yamlex] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.cmd_yamlex.po -source_file = _build/locale/ref/pillar/all/salt.pillar.cmd_yamlex.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.cmd_yamlex.rst - -[salt.ref--pillar--all--salt_pillar_s3] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.s3.po -source_file = _build/locale/ref/pillar/all/salt.pillar.s3.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.s3.rst - -[salt.ref--pillar--all--salt_pillar_svn_pillar] -file_filter = locale//LC_MESSAGES/ref/pillar/all/salt.pillar.svn_pillar.po -source_file = _build/locale/ref/pillar/all/salt.pillar.svn_pillar.pot -source_lang = en -source_name = ref/pillar/all/salt.pillar.svn_pillar.rst - -[salt.ref--renderers--all--salt_renderers_gpg] -file_filter = locale//LC_MESSAGES/ref/renderers/all/salt.renderers.gpg.po -source_file = _build/locale/ref/renderers/all/salt.renderers.gpg.pot -source_lang = en -source_name = ref/renderers/all/salt.renderers.gpg.rst - -[salt.ref--returners--all--salt_returners_couchbase_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.couchbase_return.po -source_file = _build/locale/ref/returners/all/salt.returners.couchbase_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.couchbase_return.rst - -[salt.ref--returners--all--salt_returners_elasticsearch_return] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.elasticsearch_return.po -source_file = _build/locale/ref/returners/all/salt.returners.elasticsearch_return.pot -source_lang = en -source_name = ref/returners/all/salt.returners.elasticsearch_return.rst - -[salt.ref--returners--all--salt_returners_local_cache] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.local_cache.po -source_file = _build/locale/ref/returners/all/salt.returners.local_cache.pot -source_lang = en -source_name = ref/returners/all/salt.returners.local_cache.rst - -[salt.ref--returners--all--salt_returners_multi_returner] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.multi_returner.po -source_file = _build/locale/ref/returners/all/salt.returners.multi_returner.pot -source_lang = en -source_name = ref/returners/all/salt.returners.multi_returner.rst - -[salt.ref--returners--all--salt_returners_odbc] -file_filter = locale//LC_MESSAGES/ref/returners/all/salt.returners.odbc.po -source_file = _build/locale/ref/returners/all/salt.returners.odbc.pot -source_lang = en -source_name = ref/returners/all/salt.returners.odbc.rst - -[salt.ref--runners--all--salt_runners_queue] -file_filter = locale//LC_MESSAGES/ref/runners/all/salt.runners.queue.po -source_file = _build/locale/ref/runners/all/salt.runners.queue.pot -source_lang = en -source_name = ref/runners/all/salt.runners.queue.rst - -[salt.ref--states--all--salt_states_pyenv] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.pyenv.po -source_file = _build/locale/ref/states/all/salt.states.pyenv.pot -source_lang = en -source_name = ref/states/all/salt.states.pyenv.rst - -[salt.ref--states--all--salt_states_schedule] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.schedule.po -source_file = _build/locale/ref/states/all/salt.states.schedule.pot -source_lang = en -source_name = ref/states/all/salt.states.schedule.rst - -[salt.ref--states--all--salt_states_win_update] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.win_update.po -source_file = _build/locale/ref/states/all/salt.states.win_update.pot -source_lang = en -source_name = ref/states/all/salt.states.win_update.rst - -[salt.ref--states--all--salt_states_winrepo] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.winrepo.po -source_file = _build/locale/ref/states/all/salt.states.winrepo.pot -source_lang = en -source_name = ref/states/all/salt.states.winrepo.rst - -[salt.ref--states--all--salt_states_zk_concurrency] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.zk_concurrency.po -source_file = _build/locale/ref/states/all/salt.states.zk_concurrency.pot -source_lang = en -source_name = ref/states/all/salt.states.zk_concurrency.rst - -[salt.topics--releases--2014_1_10] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.10.po -source_file = _build/locale/topics/releases/2014.1.10.pot -source_lang = en -source_name = topics/releases/2014.1.10.rst - -[salt.topics--releases--2014_1_8] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.8.po -source_file = _build/locale/topics/releases/2014.1.8.pot -source_lang = en -source_name = topics/releases/2014.1.8.rst - -[salt.topics--releases--2014_1_9] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.9.po -source_file = _build/locale/topics/releases/2014.1.9.pot -source_lang = en -source_name = topics/releases/2014.1.9.rst - -[salt.topics--tutorials--multimaster_pki] -file_filter = locale//LC_MESSAGES/topics/tutorials/multimaster_pki.po -source_file = _build/locale/topics/tutorials/multimaster_pki.pot -source_lang = en -source_name = topics/tutorials/multimaster_pki.rst - -[salt.ref--output--all--salt_output_newline_values_only] -file_filter = locale//LC_MESSAGES/ref/output/all/salt.output.newline_values_only.po -source_file = _build/locale/ref/output/all/salt.output.newline_values_only.pot -source_lang = en -source_name = ref/output/all/salt.output.newline_values_only.rst - -[salt.topics--releases--releasecandidate] -file_filter = locale//LC_MESSAGES/topics/releases/releasecandidate.po -source_file = _build/locale/topics/releases/releasecandidate.pot -source_lang = en -source_name = topics/releases/releasecandidate.rst - -[salt.topics--targeting--range] -file_filter = locale//LC_MESSAGES/topics/targeting/range.po -source_file = _build/locale/topics/targeting/range.pot -source_lang = en -source_name = topics/targeting/range.rst - -[salt.ref--modules--all--salt_modules_boto_cloudwatch] -file_filter = locale//LC_MESSAGES/ref/modules/all/salt.modules.boto_cloudwatch.po -source_file = _build/locale/ref/modules/all/salt.modules.boto_cloudwatch.pot -source_lang = en -source_name = ref/modules/all/salt.modules.boto_cloudwatch.rst - -[salt.ref--states--all--salt_states_boto_cloudwatch_alarm] -file_filter = locale//LC_MESSAGES/ref/states/all/salt.states.boto_cloudwatch_alarm.po -source_file = _build/locale/ref/states/all/salt.states.boto_cloudwatch_alarm.pot -source_lang = en -source_name = ref/states/all/salt.states.boto_cloudwatch_alarm.rst - -[salt.topics--releases--2014_1_11] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.11.po -source_file = _build/locale/topics/releases/2014.1.11.pot -source_lang = en -source_name = topics/releases/2014.1.11.rst - -[salt.ref--cli--salt-unity] -file_filter = locale//LC_MESSAGES/ref/cli/salt-unity.po -source_file = _build/locale/ref/cli/salt-unity.pot -source_lang = en -source_name = ref/cli/salt-unity.rst - -[salt.topics--development--architecture] -file_filter = locale//LC_MESSAGES/topics/development/architecture.po -source_file = _build/locale/topics/development/architecture.pot -source_lang = en -source_name = topics/development/architecture.rst - -[salt.topics--releases--2014_1_12] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.12.po -source_file = _build/locale/topics/releases/2014.1.12.pot -source_lang = en -source_name = topics/releases/2014.1.12.rst - -[salt.topics--releases--2014_1_13] -file_filter = locale//LC_MESSAGES/topics/releases/2014.1.13.po -source_file = _build/locale/topics/releases/2014.1.13.pot -source_lang = en -source_name = topics/releases/2014.1.13.rst - -[salt.topics--releases--2014_7_1] -file_filter = locale//LC_MESSAGES/topics/releases/2014.7.1.po -source_file = _build/locale/topics/releases/2014.7.1.pot -source_lang = en -source_name = topics/releases/2014.7.1.rst - diff --git a/doc/Makefile b/doc/Makefile index fa2734d8f493..9b1b1939a9bd 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -9,29 +9,14 @@ BUILDDIR = _build SPHINXLANG = XELATEX = xelatex -# ----- Translations Support ------------------------------------------------> -# If language is set, also set translation options -ifeq ($(shell [ "x$(SPHINXLANG)" != "x" ] && echo 0 || echo 1), 0) -TRANSLATIONOPTS = -D language='$(SPHINXLANG)' -else -TRANSLATIONOPTS = -endif - -# Reset settings if sphinx-intl is not available -ifeq ($(shell which sphinx-intl >/dev/null 2>&1; echo $$?), 1) -SPHINXLANG = -TRANSLATIONOPTS = -endif -# <---- Translations Support ------------------------------------------------- - # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(TRANSLATIONOPTS) $(SPHINXOPTS) . +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -.PHONY: help clean check_sphinx-build html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext translations download-translations +.PHONY: help clean check_sphinx-build html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @@ -53,7 +38,6 @@ help: @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @@ -68,38 +52,38 @@ clean: check_sphinx-build: @which $(SPHINXBUILD) >/dev/null 2>&1 || (echo "The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://www.sphinx-doc.org/en/master/)" >&2; false) -html: check_sphinx-build translations +html: check_sphinx-build $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." -dirhtml: check_sphinx-build translations +dirhtml: check_sphinx-build $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." -singlehtml: check_sphinx-build translations +singlehtml: check_sphinx-build $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." -pickle: check_sphinx-build translations +pickle: check_sphinx-build $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." -json: check_sphinx-build translations +json: check_sphinx-build $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." -htmlhelp: check_sphinx-build translations +htmlhelp: check_sphinx-build $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." -qthelp: check_sphinx-build translations +qthelp: check_sphinx-build $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ @@ -108,7 +92,7 @@ qthelp: check_sphinx-build translations @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Salt.qhc" -devhelp: check_sphinx-build translations +devhelp: check_sphinx-build $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @@ -117,31 +101,31 @@ devhelp: check_sphinx-build translations @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Salt" @echo "# devhelp" -epub: check_sphinx-build translations +epub: check_sphinx-build $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." -latex: check_sphinx-build translations +latex: check_sphinx-build $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." -latexpdf: check_sphinx-build translations +latexpdf: check_sphinx-build $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." -latexpdfja: check_sphinx-build translations +latexpdfja: check_sphinx-build $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." -pdf: check_sphinx-build translations +pdf: check_sphinx-build @if [ "$(XELATEX)" = "xelatex" ] || [ "x$(XELATEX)" = "x" ]; then \ echo "The '$(XELATEX)' command was not found."; \ fi @@ -150,40 +134,35 @@ pdf: check_sphinx-build translations $(MAKE) -C $(BUILDDIR)/latex -i "PDFLATEX=latexmk" "LATEXMKOPTS=-xelatex -interaction=nonstopmode -f -quiet" @echo "xelatex finished; the PDF files are in $(BUILDDIR)/latex." -cheatsheet: translations +cheatsheet: @echo "Running cheatsheet/salt.tex file through xelatex..." cd cheatsheet && xelatex salt.tex && cp salt.pdf ../salt-cheatsheet.pdf @echo "./salt-cheatsheet.pdf created." -text: check_sphinx-build translations +text: check_sphinx-build $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." -man: check_sphinx-build translations +man: check_sphinx-build $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." -texinfo: check_sphinx-build translations +texinfo: check_sphinx-build $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." -info: check_sphinx-build translations +info: check_sphinx-build $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." -gettext: check_sphinx-build - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale" - -changes: check_sphinx-build translations +changes: check_sphinx-build $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." @@ -205,27 +184,12 @@ doctest: check_sphinx-build @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." -xml: check_sphinx-build translations +xml: check_sphinx-build $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." -pseudoxml: check_sphinx-build translations +pseudoxml: check_sphinx-build $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." - -translations: - @if [ "$(SPHINXLANG)" = "en" ] || [ "x$(SPHINXLANG)" = "x" ]; then \ - echo "No need to update translations. Skipping..."; \ - elif [ ! -d locale/$(SPHINXLANG) ]; then \ - echo "The locale directory for $(SPHINXLANG) does not exist"; \ - exit 1; \ - else \ - echo "Compiling exising message catalog for '$(SPHINXLANG)'"; \ - .scripts/compile-translation-catalogs; \ - fi - -download-translations: - @echo "Downloading $(SPHINXLANG) translations" - .scripts/download-translation-catalog $(SPHINXLANG) diff --git a/doc/_ext/saltrepo.py b/doc/_ext/saltrepo.py new file mode 100644 index 000000000000..524e0757e330 --- /dev/null +++ b/doc/_ext/saltrepo.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +''' + saltrepo + ~~~~~~~~ + + SaltStack Repository Sphinx directives +''' + +def source_read_handler(app, docname, source): + if '|repo_primary_branch|' in source[0]: + source[0] = source[0].replace( + '|repo_primary_branch|', + app.config.html_context['repo_primary_branch'] + ) + + +def setup(app): + app.connect('source-read', source_read_handler) + + return { + 'version': 'builtin', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } diff --git a/doc/_themes/saltstack/layout.html b/doc/_themes/saltstack/layout.html index 85e0a3cfa41a..f8ad959c5687 100644 --- a/doc/_themes/saltstack/layout.html +++ b/doc/_themes/saltstack/layout.html @@ -264,20 +264,6 @@

Community

- {% endif %} diff --git a/doc/_themes/saltstack2/layout.html b/doc/_themes/saltstack2/layout.html index b1006c5299f8..004e86f014b9 100644 --- a/doc/_themes/saltstack2/layout.html +++ b/doc/_themes/saltstack2/layout.html @@ -52,7 +52,8 @@ SEARCH_CX: '{{ search_cx }}', COLLAPSE_INDEX: false, FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}', - HAS_SOURCE: '{{ has_source|lower }}' + HAS_SOURCE: '{{ has_source|lower }}', + REPO_PRIMARY_BRANCH_TAB_NAME: '{{ repo_primary_branch | capitalize }}' }; {%- for scriptfile in script_files %} @@ -178,15 +179,18 @@
@@ -202,9 +206,9 @@ - {% elif build_type == "develop" and on_saltstack %} + {% elif build_type == repo_primary_branch and on_saltstack %} - + {% elif build_type == "inactive" and on_saltstack %} @@ -244,16 +248,16 @@

{{ today }}

{% if build_type == "latest" %} -

You are viewing docs for the latest stable release, {{ latest_release }}. Switch to docs for the previous stable release, {{ previous_release }}, or to a recent doc build from the develop branch.

+

You are viewing docs for the latest stable release, {{ latest_release }}. Switch to docs for the previous stable release, {{ previous_release }}, or to a recent doc build from the {{ repo_primary_branch }} branch.

{% elif build_type == "previous" %} -

You are viewing docs for the previous stable release, {{ previous_release }}. Switch to docs for the latest stable release, {{ latest_release }}, or to a recent doc build from the develop branch.

+

You are viewing docs for the previous stable release, {{ previous_release }}. Switch to docs for the latest stable release, {{ latest_release }}, or to a recent doc build from the {{ repo_primary_branch }} branch.

{% elif build_type == "inactive" %} -

You are viewing docs for an inactive release, {{ previous_release }}. Switch to docs for the latest stable release, {{ latest_release }}, or to a recent doc build from the develop branch.

+

You are viewing docs for an inactive release, {{ previous_release }}. Switch to docs for the latest stable release, {{ latest_release }}, or to a recent doc build from the {{ repo_primary_branch }} branch.

- {% elif build_type == "develop" %} -

You are viewing docs built from a recent snapshot of the develop branch. Switch to docs for the latest stable release, {{ latest_release }}.

+ {% elif build_type == repo_primary_branch %} +

You are viewing docs built from a recent snapshot of the {{ repo_primary_branch }} branch. Switch to docs for the latest stable release, {{ latest_release }}.

{% elif build_type == "next" %}

You are viewing preview docs for the next major release, {{ next_release }}. Switch to docs for the latest stable release, {{ latest_release }}.

@@ -285,12 +289,12 @@ + {% endif %} #}--> @@ -304,11 +308,11 @@ @@ -376,20 +380,6 @@ - {% endif %} diff --git a/doc/_themes/saltstack2/static/js/webhelp.min_v1.4.4.js b/doc/_themes/saltstack2/static/js/webhelp.min_v1.4.4.js index 92e5b1586f6f..b69a1d2358b7 100644 --- a/doc/_themes/saltstack2/static/js/webhelp.min_v1.4.4.js +++ b/doc/_themes/saltstack2/static/js/webhelp.min_v1.4.4.js @@ -125,7 +125,7 @@ $( document ).ready(function() { window.location.href = window.location.href.replace($currentVer.attr("href"), clickedVer); } else { - if ($currentVer.text().indexOf("Develop") == -1) { + if ($currentVer.text().indexOf(DOCUMENTATION_OPTIONS.REPO_PRIMARY_BRANCH_TAB_NAME) == -1) { window.location.href = clickedVer + "topics/releases/" + $currentVer.text().trim() + ".html"; } else window.location.href = clickedVer + "topics/releases/"; diff --git a/doc/conf.py b/doc/conf.py index c8c9cc577ab1..47840c308284 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -253,9 +253,9 @@ def inner(fn, *iargs, **ikwargs): # pylint: disable=unused-argument on_saltstack = 'SALT_ON_SALTSTACK' in os.environ project = 'Salt' - +repo_primary_branch = 'master' # This is the default branch on GitHub for the Salt project version = salt.version.__version__ -latest_release = '2019.2.1' # latest release +latest_release = '2019.2.2' # latest release previous_release = '2018.3.4' # latest release from previous branch previous_release_dir = '2018.3' # path on web server for previous branch next_release = '' # next release @@ -268,22 +268,31 @@ def inner(fn, *iargs, **ikwargs): # pylint: disable=unused-argument copyright = time.strftime("%Y") # < --- START do not merge these settings to other branches START ---> # -build_type = 'latest' # latest, previous, develop, next -release = latest_release +build_type = repo_primary_branch # latest, previous, master, next # < --- END do not merge these settings to other branches END ---> # # Set google custom search engine -if release == latest_release: +if build_type == repo_primary_branch: + release = latest_release + search_cx = '011515552685726825874:v1had6i279q' # master + #search_cx = '011515552685726825874:x17j5zl74g8' # develop +elif build_type == 'next': + release = next_release + search_cx = '011515552685726825874:ht0p8miksrm' # latest +elif build_type == 'previous': + release = previous_release + if release.startswith('2018.3'): + search_cx = '011515552685726825874:vadptdpvyyu' # 2018.3 + elif release.startswith('2017.7'): + search_cx = '011515552685726825874:w-hxmnbcpou' # 2017.7 + elif release.startswith('2016.11'): + search_cx = '011515552685726825874:dlsj745pvhq' # 2016.11 + else: + search_cx = '011515552685726825874:ht0p8miksrm' # latest +else: # latest or something else + release = latest_release search_cx = '011515552685726825874:ht0p8miksrm' # latest -elif release.startswith('2018.3'): - search_cx = '011515552685726825874:vadptdpvyyu' # 2018.3 -elif release.startswith('2017.7'): - search_cx = '011515552685726825874:w-hxmnbcpou' # 2017.7 -elif release.startswith('2016.11'): - search_cx = '011515552685726825874:dlsj745pvhq' # 2016.11 -else: - search_cx = '011515552685726825874:x17j5zl74g8' # develop needs_sphinx = '1.3' @@ -306,6 +315,7 @@ def inner(fn, *iargs, **ikwargs): # pylint: disable=unused-argument 'sphinx.ext.intersphinx', 'httpdomain', 'youtube', + 'saltrepo' #'saltautodoc', # Must be AFTER autodoc #'shorturls', ] @@ -364,7 +374,7 @@ def inner(fn, *iargs, **ikwargs): # pylint: disable=unused-argument # A shortcut for linking to tickets on the GitHub issue tracker extlinks = { - 'blob': ('https://github.com/saltstack/salt/blob/%s/%%s' % 'develop', None), + 'blob': ('https://github.com/saltstack/salt/blob/%s/%%s' % repo_primary_branch, None), 'issue': ('https://github.com/saltstack/salt/issues/%s', 'issue #'), 'pull': ('https://github.com/saltstack/salt/pull/%s', 'PR #'), 'formula_url': ('https://github.com/saltstack-formulas/%s', ''), @@ -436,6 +446,7 @@ def inner(fn, *iargs, **ikwargs): # pylint: disable=unused-argument 'build_type': build_type, 'today': today, 'copyright': copyright, + 'repo_primary_branch': repo_primary_branch } html_use_index = True diff --git a/doc/ref/configuration/master.rst b/doc/ref/configuration/master.rst index d0d2cf6f9d22..26627b2a6adc 100644 --- a/doc/ref/configuration/master.rst +++ b/doc/ref/configuration/master.rst @@ -1072,6 +1072,21 @@ Should be greater than overall download time. http_request_timeout: 3600 +``use_yamlloader_old`` +------------------------ + +.. versionadded:: 2019.2.1 + +Default: ``False`` + +Use the pre-2019.2 YAML renderer. +Uses legacy YAML rendering to support some legacy inline data structures. +See the :ref:`2019.2.1 release notes ` for more details. + +.. code-block:: yaml + + use_yamlloader_old: False + .. _salt-ssh-configuration: Salt-SSH Configuration @@ -5373,7 +5388,7 @@ out for 2015.8.0 and later minions. .. note:: 2015.8.0 and later minions do not use this setting since the cachefile - is now located on the minion. + is now generated by the minion. Default: ``winrepo.p`` diff --git a/doc/ref/configuration/minion.rst b/doc/ref/configuration/minion.rst index 6f5acaffde47..a5d77145a1d3 100644 --- a/doc/ref/configuration/minion.rst +++ b/doc/ref/configuration/minion.rst @@ -652,6 +652,35 @@ FQDN (for instance, Solaris). append_domain: foo.org +.. conf_minion:: minion_id_remove_domain + +``minion_id_remove_domain`` +--------------------------- + +.. versionadded:: Neon + +Default: ``False`` + +Remove a domain when the minion id is generated as a fully qualified domain +name (either by the user provided ``id_function``, or by Salt). This is useful +when the minions shall be named like hostnames. Can be a single domain (to +prevent name clashes), or True, to remove all domains. + +Examples: + - minion_id_remove_domain = foo.org + - FQDN = king_bob.foo.org --> minion_id = king_bob + - FQDN = king_bob.bar.org --> minion_id = king_bob.bar.org + - minion_id_remove_domain = True + - FQDN = king_bob.foo.org --> minion_id = king_bob + - FQDN = king_bob.bar.org --> minion_id = king_bob + + +For more information, please see :issue:`49212` and :pull:`49378`. + +.. code-block:: yaml + + minion_id_remove_domain: foo.org + .. conf_minion:: minion_id_lowercase ``minion_id_lowercase`` @@ -769,6 +798,30 @@ Statically assigns grains to the minion. cabinet: 13 cab_u: 14-15 +.. conf_minion:: grains_blacklist + +``grains_blacklist`` +-------------------- + +Default: ``[]`` + +Each grains key will be compared against each of the expressions in this list. +Any keys which match will be filtered from the grains. Exact matches, glob +matches, and regular expressions are supported. + +.. note:: + Some states and execution modules depend on grains. Filtering may cause + them to be unavailable or run unreliably. + +.. versionadded:: Neon + +.. code-block:: yaml + + grains_blacklist: + - cpu_flags + - zmq* + - ipv[46] + .. conf_minion:: grains_cache ``grains_cache`` @@ -893,6 +946,20 @@ minion. Since this grain is expensive, it is disabled by default. iscsi_grains: True +.. conf_minion:: nvme_grains + +``nvme_grains`` +------------------------ + +Default: ``False`` + +The ``nvme_grains`` setting will enable the ``nvme_nqn`` grain on the +minion. Since this grain is expensive, it is disabled by default. + +.. code-block:: yaml + + nvme_grains: True + .. conf_minion:: mine_enabled ``mine_enabled`` @@ -1326,7 +1393,7 @@ creates a new connection for every return to the master. Default: ``ipc`` Windows platforms lack POSIX IPC and must rely on slower TCP based inter- -process communications. Set ipc_mode to ``tcp`` on such systems. +process communications. ``ipc_mode`` is set to ``tcp`` on such systems. .. code-block:: yaml @@ -1493,6 +1560,21 @@ List of hosts to bypass HTTP proxy no_proxy: [ '127.0.0.1', 'foo.tld' ] +``use_yamlloader_old`` +------------------------ + +.. versionadded:: 2019.2.1 + +Default: ``False`` + +Use the pre-2019.2 YAML renderer. +Uses legacy YAML rendering to support some legacy inline data structures. +See the :ref:`2019.2.1 release notes ` for more details. + +.. code-block:: yaml + + use_yamlloader_old: False + Docker Configuration ==================== diff --git a/doc/ref/engines/all/index.rst b/doc/ref/engines/all/index.rst index 15f6d8a667e0..d9b19e016666 100644 --- a/doc/ref/engines/all/index.rst +++ b/doc/ref/engines/all/index.rst @@ -11,7 +11,6 @@ engine modules :template: autosummary.rst.tmpl docker_events - hipchat http_logstash ircbot junos_syslog diff --git a/doc/ref/engines/all/salt.engines.hipchat.rst b/doc/ref/engines/all/salt.engines.hipchat.rst deleted file mode 100644 index 4b12b40f99b2..000000000000 --- a/doc/ref/engines/all/salt.engines.hipchat.rst +++ /dev/null @@ -1,6 +0,0 @@ -salt.engines.hipchat module -=========================== - -.. automodule:: salt.engines.hipchat - :members: - :undoc-members: diff --git a/doc/ref/index.rst b/doc/ref/index.rst index 204cca6f7d71..aeebd5736002 100644 --- a/doc/ref/index.rst +++ b/doc/ref/index.rst @@ -30,5 +30,6 @@ This section contains a list of the Python modules that are used to extend the v ../ref/serializers/all/index ../ref/states/all/index ../ref/thorium/all/index + ../ref/tokens/all/index ../ref/tops/all/index ../ref/wheel/all/index diff --git a/doc/ref/internals/fileserver-and-client.rst b/doc/ref/internals/fileserver-and-client.rst index da6e3e3bd2c4..66c7681b990a 100644 --- a/doc/ref/internals/fileserver-and-client.rst +++ b/doc/ref/internals/fileserver-and-client.rst @@ -44,8 +44,8 @@ will be skipped if the ``update`` function is run on the fileserver. Backends for the fileserver are located in `salt/fileserver/`_ (the files not named ``__init__.py``). -.. _`salt/fileserver/__init__.py`: https://github.com/saltstack/salt/tree/develop/salt/fileserver/__init__.py -.. _`salt/fileserver/`: https://github.com/saltstack/salt/tree/develop/salt/fileserver +.. _`salt/fileserver/__init__.py`: https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/fileserver/__init__.py +.. _`salt/fileserver/`: https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/fileserver Fileclient ---------- diff --git a/doc/ref/modules/all/index.rst b/doc/ref/modules/all/index.rst index 94a42d8dad80..339b3ebe4d76 100644 --- a/doc/ref/modules/all/index.rst +++ b/doc/ref/modules/all/index.rst @@ -171,7 +171,6 @@ execution modules hashutil heat hg - hipchat hosts http ifttt @@ -200,6 +199,7 @@ execution modules jboss7 jboss7_cli jenkinsmod + jinja jira_mod junos k8s @@ -211,6 +211,7 @@ execution modules keyboard keystone keystoneng + keystore kmod kubernetesmod launchctl_service @@ -232,6 +233,7 @@ execution modules logrotate lvs lxc + lxd mac_assistive mac_brew_pkg macdefaults diff --git a/doc/ref/modules/all/salt.modules.hipchat.rst b/doc/ref/modules/all/salt.modules.hipchat.rst deleted file mode 100644 index 09b519a3a93a..000000000000 --- a/doc/ref/modules/all/salt.modules.hipchat.rst +++ /dev/null @@ -1,6 +0,0 @@ -==================== -salt.modules.hipchat -==================== - -.. automodule:: salt.modules.hipchat - :members: diff --git a/doc/ref/modules/all/salt.modules.jinja.rst b/doc/ref/modules/all/salt.modules.jinja.rst new file mode 100644 index 000000000000..7ab7f5ca39a4 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.jinja.rst @@ -0,0 +1,6 @@ +================== +salt.modules.jinja +================== + +.. automodule:: salt.modules.jinja + :members: diff --git a/doc/ref/modules/all/salt.modules.keystore.rst b/doc/ref/modules/all/salt.modules.keystore.rst new file mode 100644 index 000000000000..b2defc9982ee --- /dev/null +++ b/doc/ref/modules/all/salt.modules.keystore.rst @@ -0,0 +1,6 @@ +===================== +salt.modules.keystore +===================== + +.. automodule:: salt.modules.keystore + :members: \ No newline at end of file diff --git a/doc/ref/modules/all/salt.modules.lxd.rst b/doc/ref/modules/all/salt.modules.lxd.rst new file mode 100644 index 000000000000..afb09abcb334 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.lxd.rst @@ -0,0 +1,6 @@ +================ +salt.modules.lxd +================ + +.. automodule:: salt.modules.lxd + :members: \ No newline at end of file diff --git a/doc/ref/modules/index.rst b/doc/ref/modules/index.rst index 73c47f168055..b19d91e1e8f8 100644 --- a/doc/ref/modules/index.rst +++ b/doc/ref/modules/index.rst @@ -298,7 +298,7 @@ prevent loading if dependencies are not met. Since ``__virtual__`` is called before the module is loaded, ``__salt__`` will be unreliable as not all modules will be available at this point in time. The -``__pillar`` and ``__grains__`` :ref:`"dunder" dictionaries ` +``__pillar__`` and ``__grains__`` :ref:`"dunder" dictionaries ` are available however. .. note:: diff --git a/doc/ref/renderers/all/salt.renderers.yaml.rst b/doc/ref/renderers/all/salt.renderers.yaml.rst index a01bd0b28a7c..2f4e3ca3b275 100644 --- a/doc/ref/renderers/all/salt.renderers.yaml.rst +++ b/doc/ref/renderers/all/salt.renderers.yaml.rst @@ -53,7 +53,7 @@ And in Python: .. code-block:: python - {'first_level_dict_key': {'second_level_dict_key': 'value_in_second_level_dict' } + {'first_level_dict_key': {'second_level_dict_key': 'value_in_second_level_dict' } } Rule Three: Dashes ------------------ diff --git a/doc/ref/returners/all/index.rst b/doc/ref/returners/all/index.rst index 9ee7a3b0b3d0..68831349e34c 100644 --- a/doc/ref/returners/all/index.rst +++ b/doc/ref/returners/all/index.rst @@ -19,7 +19,6 @@ returner modules elasticsearch_return etcd_return highstate_return - hipchat_return influxdb_return kafka_return librato_return diff --git a/doc/ref/returners/all/salt.returners.hipchat_return.rst b/doc/ref/returners/all/salt.returners.hipchat_return.rst deleted file mode 100644 index 37e5d6d4be11..000000000000 --- a/doc/ref/returners/all/salt.returners.hipchat_return.rst +++ /dev/null @@ -1,6 +0,0 @@ -============================= -salt.returners.hipchat_return -============================= - -.. automodule:: salt.returners.hipchat_return - :members: diff --git a/doc/ref/returners/index.rst b/doc/ref/returners/index.rst index 742c53a0ca7f..10469df24622 100644 --- a/doc/ref/returners/index.rst +++ b/doc/ref/returners/index.rst @@ -383,4 +383,4 @@ Full List of Returners .. toctree:: all/index -.. _`redis`: https://github.com/saltstack/salt/tree/develop/salt/returners/redis_return.py +.. _`redis`: https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/returners/redis_return.py diff --git a/doc/ref/runners/all/salt.runners.mattermost.rst b/doc/ref/runners/all/salt.runners.mattermost.rst index c33a9f459b52..869745ffae2e 100644 --- a/doc/ref/runners/all/salt.runners.mattermost.rst +++ b/doc/ref/runners/all/salt.runners.mattermost.rst @@ -3,7 +3,7 @@ salt.runners.mattermost **Note for 2017.7 releases!** -Due to the `salt.runners.config `_ module not being available in this release series, importing the `salt.runners.config `_ module from the develop branch is required to make this module work. +Due to the `salt.runners.config `_ module not being available in this release series, importing the `salt.runners.config `_ module from the |repo_primary_branch| branch is required to make this module work. Ref: `Mattermost runner failing to retrieve config values due to unavailable config runner #43479 `_ diff --git a/doc/ref/serializers/all/index.rst b/doc/ref/serializers/all/index.rst index 261ccb01f695..b8d03368cbbc 100644 --- a/doc/ref/serializers/all/index.rst +++ b/doc/ref/serializers/all/index.rst @@ -14,5 +14,6 @@ serializer modules json msgpack python + toml yaml yamlex diff --git a/doc/ref/serializers/all/salt.serializers.toml.rst b/doc/ref/serializers/all/salt.serializers.toml.rst new file mode 100644 index 000000000000..c5672ab3cdf7 --- /dev/null +++ b/doc/ref/serializers/all/salt.serializers.toml.rst @@ -0,0 +1,6 @@ +===================== +salt.serializers.toml +===================== + +.. automodule:: salt.serializers.toml + :members: diff --git a/doc/ref/states/all/index.rst b/doc/ref/states/all/index.rst index fb9ac25953a0..699895d8a226 100644 --- a/doc/ref/states/all/index.rst +++ b/doc/ref/states/all/index.rst @@ -115,7 +115,6 @@ state modules group heat hg - hipchat host http icinga2 @@ -150,6 +149,7 @@ state modules keystone_role_grant keystone_service keystone_user + keystore kmod kubernetes layman @@ -255,6 +255,7 @@ state modules rvm salt_proxy saltmod + saltutil schedule selinux serverdensity_device diff --git a/doc/ref/states/all/salt.states.hipchat.rst b/doc/ref/states/all/salt.states.hipchat.rst deleted file mode 100644 index 921bd1bbe89a..000000000000 --- a/doc/ref/states/all/salt.states.hipchat.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================== -salt.states.hipchat -=================== - -.. automodule:: salt.states.hipchat - :members: diff --git a/doc/ref/states/all/salt.states.keystore.rst b/doc/ref/states/all/salt.states.keystore.rst new file mode 100644 index 000000000000..2db8b6914335 --- /dev/null +++ b/doc/ref/states/all/salt.states.keystore.rst @@ -0,0 +1,6 @@ +==================== +salt.states.keystore +==================== + +.. automodule:: salt.states.keystore + :members: \ No newline at end of file diff --git a/doc/ref/states/all/salt.states.saltutil.rst b/doc/ref/states/all/salt.states.saltutil.rst new file mode 100644 index 000000000000..f9e0360eb832 --- /dev/null +++ b/doc/ref/states/all/salt.states.saltutil.rst @@ -0,0 +1,6 @@ +==================== +salt.states.saltutil +==================== + +.. automodule:: salt.states.saltutil + :members: \ No newline at end of file diff --git a/doc/ref/states/ordering.rst b/doc/ref/states/ordering.rst index b23d37a56d3e..4463e6c38af4 100644 --- a/doc/ref/states/ordering.rst +++ b/doc/ref/states/ordering.rst @@ -5,10 +5,10 @@ Ordering States =============== The way in which configuration management systems are executed is a hotly -debated topic in the configuration management world. Two -major philosophies exist on the subject, to either execute in an imperative -fashion where things are executed in the order in which they are defined, or -in a declarative fashion where dependencies need to be mapped between objects. +debated topic in the configuration management world. Two major philosophies +exist on the subject, to either execute in an imperative fashion where things +are executed in the order in which they are defined, or in a declarative +fashion where dependencies need to be mapped between objects. Imperative ordering is finite and generally considered easier to write, but declarative ordering is much more powerful and flexible but generally considered @@ -27,20 +27,17 @@ State Auto Ordering .. versionadded: 0.17.0 Salt always executes states in a finite manner, meaning that they will always -execute in the same order regardless of the system that is executing them. -But in Salt 0.17.0, the ``state_auto_order`` option was added. This option -makes states get evaluated in the order in which they are defined in sls -files, including the top.sls file. +execute in the same order regardless of the system that is executing them. This +evaluation order makes it easy to know what order the states will be executed in, +but it is important to note that the requisite system will override the ordering +defined in the files, and the ``order`` option, described below, will also +override the order in which states are executed. -The evaluation order makes it easy to know what order the states will be -executed in, but it is important to note that the requisite system will -override the ordering defined in the files, and the ``order`` option described -below will also override the order in which states are defined in sls files. - -If the classic ordering is preferred (lexicographic), then set -``state_auto_order`` to ``False`` in the master configuration file. Otherwise, -``state_auto_order`` defaults to ``True``. +This ordering system can be disabled in preference of lexicographic (classic) +ordering by setting the ``state_auto_order`` option to ``False`` in the master +configuration file. Otherwise, ``state_auto_order`` defaults to ``True``. +How compiler ordering is managed is described further in :ref:`compiler-ordering`. .. _ordering_requisites: diff --git a/doc/ref/states/requisites.rst b/doc/ref/states/requisites.rst index d8b06c3c0f9f..fbbf44387ba6 100644 --- a/doc/ref/states/requisites.rst +++ b/doc/ref/states/requisites.rst @@ -68,6 +68,22 @@ first line in the stanza) or the ``- name`` parameter. - require: - pkg: vim +Glog matching in requisites +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 0.9.8 + +Glob matching is supported in requisites. This is mostly useful for file +changes. In the example below, a change in ``/etc/apache2/httpd.conf`` or +``/etc/apache2/sites-available/default.conf`` will reload/restart the service: + +.. code-block:: yaml + + apache2: + service.running: + - watch: + - file: /etc/apache2/* + Omitting state module in requisites ----------------------------------- @@ -917,7 +933,7 @@ For example: - names: - first_deploy_cmd - second_deploy_cmd - - unless: ls /usr/bin/vim + - unless: some_check In the above case, ``some_check`` will be run prior to _each_ name -- once for ``first_deploy_cmd`` and a second time for ``second_deploy_cmd``. diff --git a/doc/ref/states/writing.rst b/doc/ref/states/writing.rst index dce5853eaf62..effdb42ca238 100644 --- a/doc/ref/states/writing.rst +++ b/doc/ref/states/writing.rst @@ -21,7 +21,7 @@ illustrate: .. code-block:: yaml /etc/salt/master: # maps to "name", unless a "name" argument is specified below - file.managed: # maps to . - e.g. "managed" in https://github.com/saltstack/salt/tree/develop/salt/states/file.py + file.managed: # maps to . - e.g. "managed" in https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/states/file.py - user: root # one of many options passed to the manage function - group: root - mode: 644 @@ -59,7 +59,7 @@ A well-written state function will follow these steps: This is an extremely simplified example. Feel free to browse the `source code`_ for Salt's state modules to see other examples. - .. _`source code`: https://github.com/saltstack/salt/tree/develop/salt/states + .. _`source code`: https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/states 1. Set up the return dictionary and perform any necessary input validation (type checking, looking for use of mutually-exclusive arguments, etc.). diff --git a/doc/ref/tokens/all/index.rst b/doc/ref/tokens/all/index.rst new file mode 100644 index 000000000000..5b46333fa256 --- /dev/null +++ b/doc/ref/tokens/all/index.rst @@ -0,0 +1,14 @@ +.. _all-salt.tokens: + +============ +auth modules +============ + +.. currentmodule:: salt.tokens + +.. autosummary:: + :toctree: + :template: autosummary.rst.tmpl + + localfs + rediscluster diff --git a/doc/ref/tokens/all/salt.tokens.localfs.rst b/doc/ref/tokens/all/salt.tokens.localfs.rst new file mode 100644 index 000000000000..9ba95ca7d1a6 --- /dev/null +++ b/doc/ref/tokens/all/salt.tokens.localfs.rst @@ -0,0 +1,6 @@ +=================== +salt.tokens.localfs +=================== + +.. automodule:: salt.tokens.localfs + :members: diff --git a/doc/ref/tokens/all/salt.tokens.rediscluster.rst b/doc/ref/tokens/all/salt.tokens.rediscluster.rst new file mode 100644 index 000000000000..b00c290da86d --- /dev/null +++ b/doc/ref/tokens/all/salt.tokens.rediscluster.rst @@ -0,0 +1,6 @@ +======================== +salt.tokens.rediscluster +======================== + +.. automodule:: salt.tokens.rediscluster + :members: diff --git a/doc/topics/cloud/cloud.rst b/doc/topics/cloud/cloud.rst index 96cf765b53e3..30cd17963f43 100644 --- a/doc/topics/cloud/cloud.rst +++ b/doc/topics/cloud/cloud.rst @@ -31,7 +31,7 @@ imports, to decide whether to load the module. In most cases, it will return a filename, then that name should be returned instead of ``True``. An example of this may be seen in the Azure module: -https://github.com/saltstack/salt/tree/develop/salt/cloud/clouds/msazure.py +https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/cloud/clouds/msazure.py The get_configured_provider() Function -------------------------------------- @@ -57,7 +57,7 @@ created by the cloud host, wait for it to become available, and then A good example to follow for writing a cloud driver module based on libcloud is the module provided for Linode: -https://github.com/saltstack/salt/tree/develop/salt/cloud/clouds/linode.py +https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/cloud/clouds/linode.py The basic flow of a ``create()`` function is as follows: @@ -183,7 +183,7 @@ imports should be absent from the Salt Cloud module. A good example of a non-libcloud driver is the DigitalOcean driver: -https://github.com/saltstack/salt/tree/develop/salt/cloud/clouds/digitalocean.py +https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/cloud/clouds/digitalocean.py The ``create()`` Function ------------------------- diff --git a/doc/topics/cloud/deploy.rst b/doc/topics/cloud/deploy.rst index c84e2a449d0d..f26a7db77f3c 100644 --- a/doc/topics/cloud/deploy.rst +++ b/doc/topics/cloud/deploy.rst @@ -89,7 +89,7 @@ functions include: A good, well commented example of this process is the Fedora deployment script: -https://github.com/saltstack/salt/blob/develop/salt/cloud/deploy/Fedora.sh +https://github.com/saltstack/salt/blob/|repo_primary_branch|/salt/cloud/deploy/Fedora.sh A number of legacy deploy scripts are included with the release tarball. None of them are as functional or complete as Salt Bootstrap, and are still included diff --git a/doc/topics/cloud/index.rst b/doc/topics/cloud/index.rst index 3a6ba09900fe..bf2758c941f5 100644 --- a/doc/topics/cloud/index.rst +++ b/doc/topics/cloud/index.rst @@ -35,7 +35,7 @@ is done using the following YAML configuration files: which the VM is created (the provider settings are in the provider configuration explained above). Based on your needs, you might define different profiles for web servers, database servers, and so on. See :ref:`VM - Profiles `. + Profiles `. Configuration Inheritance ========================= diff --git a/doc/topics/development/contributing.rst b/doc/topics/development/contributing.rst index cb1b01c61f4f..7493f8ec023a 100644 --- a/doc/topics/development/contributing.rst +++ b/doc/topics/development/contributing.rst @@ -78,12 +78,12 @@ Fork a Repo Guide_>`_ and is well worth reading. git fetch upstream git checkout -b fix-broken-thing upstream/2016.11 - If you're working on a feature, create your branch from the develop branch. + If you're working on a feature, create your branch from the |repo_primary_branch| branch. .. code-block:: bash git fetch upstream - git checkout -b add-cool-feature upstream/develop + git checkout -b add-cool-feature upstream/|repo_primary_branch| #. Edit and commit changes to your branch. @@ -149,7 +149,7 @@ Fork a Repo Guide_>`_ and is well worth reading. .. code-block:: bash git fetch upstream - git rebase upstream/develop add-cool-feature + git rebase upstream/|repo_primary_branch| add-cool-feature git push -u origin add-cool-feature If you do rebase, and the push is rejected with a @@ -185,9 +185,9 @@ Fork a Repo Guide_>`_ and is well worth reading. https://github.com/my-account/salt/compare/saltstack:2016.11...fix-broken-thing - If your branch is a feature, choose ``develop`` as the base branch, + If your branch is a feature, choose ``|repo_primary_branch|`` as the base branch, - https://github.com/my-account/salt/compare/saltstack:develop...add-cool-feature + https://github.com/my-account/salt/compare/saltstack:|repo_primary_branch|...add-cool-feature #. Review that the proposed changes are what you expect. #. Write a descriptive comment. Include links to related issues (e.g. @@ -219,10 +219,10 @@ Fork a Repo Guide_>`_ and is well worth reading. Salt's Branch Topology ---------------------- -There are three different kinds of branches in use: develop, main release +There are three different kinds of branches in use: |repo_primary_branch|, main release branches, and dot release branches. -- All feature work should go into the ``develop`` branch. +- All feature work should go into the ``|repo_primary_branch|`` branch. - Bug fixes and documentation changes should go into the oldest **supported main** release branch affected by the the bug or documentation change (you can use the blame button in github to figure out when the bug was introduced). @@ -236,22 +236,22 @@ branches, and dot release branches. .. note:: - GitHub will open pull requests against Salt's main branch, ``develop``, + GitHub will open pull requests against Salt's main branch, ``|repo_primary_branch|``, by default. Be sure to check which branch is selected when creating the pull request. -The Develop Branch -================== +The |repo_primary_branch| Branch +================================ -The ``develop`` branch is unstable and bleeding-edge. Pull requests containing -feature additions or non-bug-fix changes should be made against the ``develop`` +The ``|repo_primary_branch|`` branch is unstable and bleeding-edge. Pull requests containing +feature additions or non-bug-fix changes should be made against the ``|repo_primary_branch|`` branch. .. note:: If you have a bug fix or documentation change and have already forked your - working branch from ``develop`` and do not know how to rebase your commits - against another branch, then submit it to ``develop`` anyway. SaltStack's + working branch from ``|repo_primary_branch|`` and do not know how to rebase your commits + against another branch, then submit it to ``|repo_primary_branch|`` anyway. SaltStack's development team will be happy to back-port it to the correct branch. **Please make sure you let the maintainers know that the pull request needs @@ -298,7 +298,7 @@ automatically be "merged-forward" into the newer branches. For example, a pull request is merged into ``2017.7``. Then, the entire ``2017.7`` branch is merged-forward into the ``2018.3`` branch, and the -``2018.3`` branch is merged-forward into the ``develop`` branch. +``2018.3`` branch is merged-forward into the ``|repo_primary_branch|`` branch. This process makes is easy for contributors to make only one pull-request against an older branch, but allows the change to propagate to all **main** @@ -338,7 +338,7 @@ message text directly. Only including the text in a pull request will not close the issue. GitHub will close the referenced issue once the *commit* containing the -magic text is merged into the default branch (``develop``). Any magic text +magic text is merged into the default branch (``|repo_primary_branch|``). Any magic text input only into the pull request description will not be seen at the Git-level when those commits are merged-forward. In other words, only the commits are merged-forward and not the pull request text. @@ -348,7 +348,7 @@ commits are merged-forward and not the pull request text. Backporting Pull Requests ========================= -If a bug is fixed on ``develop`` and the bug is also present on a +If a bug is fixed on ``|repo_primary_branch|`` and the bug is also present on a currently-supported release branch, it will need to be back-ported to an applicable branch. @@ -363,11 +363,11 @@ applicable branch. changes themselves. It is often easiest to fix a bug on the oldest supported release branch and -then merge that branch forward into ``develop`` (as described earlier in this +then merge that branch forward into ``|repo_primary_branch|`` (as described earlier in this document). When that is not possible the fix must be back-ported, or copied, into any other affected branches. -These steps assume a pull request ``#1234`` has been merged into ``develop``. +These steps assume a pull request ``#1234`` has been merged into ``|repo_primary_branch|``. And ``upstream`` is the name of the remote pointing to the main Salt repo. #. Identify the oldest supported release branch that is affected by the bug. @@ -450,20 +450,20 @@ the name of the main `saltstack/salt`_ repository. git fetch upstream -#. Update your copy of the ``develop`` branch. +#. Update your copy of the ``|repo_primary_branch|`` branch. .. code-block:: bash - git checkout develop - git merge --ff-only upstream/develop + git checkout |repo_primary_branch| + git merge --ff-only upstream/|repo_primary_branch| If Git complains that a fast-forward merge is not possible, you have local commits. - * Run ``git pull --rebase origin develop`` to rebase your changes on top of + * Run ``git pull --rebase origin |repo_primary_branch|`` to rebase your changes on top of the upstream changes. * Or, run ``git branch `` to create a new branch with your - commits. You will then need to reset your ``develop`` branch before + commits. You will then need to reset your ``|repo_primary_branch|`` branch before updating it with the changes from upstream. If Git complains that local files will be overwritten, you have changes to @@ -474,7 +474,7 @@ the name of the main `saltstack/salt`_ repository. .. code-block:: bash - git push origin develop + git push origin |repo_primary_branch| #. Repeat the previous two steps for any other branches you work with, such as the current release branch. @@ -551,6 +551,6 @@ Script, see the Bootstrap Script's `Contributing Guidelines`_. .. _GPG Probot: https://probot.github.io/apps/gpg/ .. _help articles: https://help.github.com/articles/signing-commits-with-gpg/ .. _GPG Signature Verification feature announcement: https://github.com/blog/2144-gpg-signature-verification -.. _bootstrap-salt.sh: https://github.com/saltstack/salt/blob/develop/salt/cloud/deploy/bootstrap-salt.sh +.. _bootstrap-salt.sh: https://github.com/saltstack/salt/blob/|repo_primary_branch|/salt/cloud/deploy/bootstrap-salt.sh .. _salt-bootstrap repo: https://github.com/saltstack/salt-bootstrap .. _Contributing Guidelines: https://github.com/saltstack/salt-bootstrap/blob/develop/CONTRIBUTING.md diff --git a/doc/topics/development/modules/developing.rst b/doc/topics/development/modules/developing.rst index b55adc4b6728..127a8cf7504f 100644 --- a/doc/topics/development/modules/developing.rst +++ b/doc/topics/development/modules/developing.rst @@ -170,12 +170,19 @@ While ``__grains__`` is defined for every module, it's only filled in for some. __pillar__ ----------- -Filled in for: Execution, Returner, SSH Wrapper, State +Filled in for: Execution, Renderer, Returner, SSH Wrapper, State The ``__pillar__`` dictionary contains the pillar for the respective minion. While ``__pillar__`` is defined for every module, it's only filled in for some. +__ext_pillar__ +-------------- + +Filled in for: Pillar + +The ``__ext_pillar__`` dictionary contains the external pillar modules. + .. _dunder-context: __context__ @@ -208,19 +215,26 @@ each file. Here is an example from salt/modules/cp.py: __utils__ --------- -Defined in: Cloud, Engine, Execution, File Server, Pillar, Proxy, Runner, SDB. +Defined in: Cloud, Engine, Execution, File Server, Grain, Pillar, Proxy, Roster, Runner, SDB, State __proxy__ --------- Defined in: Beacon, Engine, Execution, Executor, Proxy, Renderer, Returner, State, Util -__runners__ +__runner__ ----------- Defined in: Engine, Roster, Thorium +.. note:: When used in engines, it should be called __runners__ (plural) + +__executors__ +------------- + +Defined in: Executor + __ret__ ------- -Defined in: Proxy, Search +Defined in: Proxy __thorium__ ----------- diff --git a/doc/topics/development/modules/external_pillars.rst b/doc/topics/development/modules/external_pillars.rst index d9aeb1ce9c06..c34ae5abe54c 100644 --- a/doc/topics/development/modules/external_pillars.rst +++ b/doc/topics/development/modules/external_pillars.rst @@ -182,7 +182,7 @@ From above example, 'external_pillar' is the top dictionary name. Therefore: .. code-block:: bash - salt-call '*' pillar.get external_pillar + salt '*' pillar.get external_pillar You shouldn't just add items to ``pillar`` and return that, since that will diff --git a/doc/topics/development/modules/index.rst b/doc/topics/development/modules/index.rst index e95a485759c3..45ba66c51a13 100644 --- a/doc/topics/development/modules/index.rst +++ b/doc/topics/development/modules/index.rst @@ -55,7 +55,7 @@ prepended by underscore, such as: Modules must be synced before they can be used. This can happen a few ways, discussed below. -.. note: +.. note:: Using saltenvs besides ``base`` may not work in all contexts. Sync Via States @@ -69,11 +69,21 @@ dynamic modules when states are run. To disable this behavior set When dynamic modules are autoloaded via states, only the modules defined in the same saltenvs as the states currently being run. +Also it is possible to use the explicit ``saltutil.sync_*`` :py:mod:`state functions ` +to sync the modules (previously it was necessary to use the ``module.run`` state): + +.. code-block::yaml + + synchronize_modules: + saltutil.sync_modules: + - refresh: True + + Sync Via the saltutil Module ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The saltutil module has a number of functions that can be used to sync all -or specific dynamic modules. The ``saltutil.sync_*`` +or specific dynamic modules. The ``saltutil.sync_*`` :py:mod:`execution functions ` and :py:mod:`runner functions ` can be used to sync modules to minions and the master, respectively. @@ -110,7 +120,7 @@ This is done via setuptools entry points: ) Note that these are not synced from the Salt Master to the Minions. They must be -installed indepdendently on each Minion. +installed independently on each Minion. Module Types ============ @@ -129,10 +139,12 @@ Cache ``salt.cache`` (:ref:`index `) `` Cloud ``salt.cloud.clouds`` (:ref:`index `) ``clouds`` ``cloud_dirs`` Engine ``salt.engines`` (:ref:`index `) ``engines`` ``engines_dirs`` Execution ``salt.modules`` (:ref:`index `) ``modules`` ``module_dirs`` -Executor ``salt.executors`` (:ref:`index `) ``executors`` [#no-fs]_ ``executor_dirs`` +Executor ``salt.executors`` (:ref:`index `) ``executors`` ``executor_dirs`` File Server ``salt.fileserver`` (:ref:`index `) ``fileserver`` ``fileserver_dirs`` Grain ``salt.grains`` (:ref:`index `) ``grains`` ``grains_dirs`` Log Handler ``salt.log.handlers`` (:ref:`index `) ``log_handlers`` ``log_handlers_dirs`` +Matcher ``salt.matchers`` ``matchers`` ``matchers_dirs`` +Metaproxy ``salt.metaproxy`` ``metaproxy`` [#no-fs]_ ``metaproxy_dirs`` Net API ``salt.netapi`` (:ref:`index `) ``netapi`` [#no-fs]_ ``netapi_dirs`` Outputter ``salt.output`` (:ref:`index `) ``output`` ``outputter_dirs`` Pillar ``salt.pillar`` (:ref:`index `) ``pillar`` ``pillar_dirs`` @@ -157,7 +169,7 @@ Wheel ``salt.wheels`` (:ref:`index `) `` .. [#no-fs] These modules cannot be loaded from the Salt File Server. -.. note: +.. note:: While it is possible to import modules directly with the import statement, it is strongly recommended that the appropriate :ref:`dunder dictionary ` is used to access them @@ -179,7 +191,7 @@ Beacon * :ref:`Writing Beacons ` -Beacons are polled by the Salt event loop to monitor non-salt processes. See +Beacons are polled by the Salt event loop to monitor non-salt processes. See :ref:`Beacons ` for more information about the beacon system. Cache @@ -258,6 +270,19 @@ Log Handler Log handlers allows the logs from salt (master or minion) to be sent to log aggregation systems. +Matcher +------- + +Matcher modules are used to define the :ref:`minion targeting expressions `. +For now, it is only possible to override the :ref:`existing matchers ` +(the required CLI plumbing for custom matchers is not implemented yet). + +Metaproxy +--------- + +Metaproxy is an abstraction layer above the existing proxy minion. It enables +adding different types of proxy minions that can still load existing proxymodules. + Net API ------- @@ -382,7 +407,7 @@ Tokens Token stores for :ref:`External Authentication `. See the :py:mod:`salt.tokens` docstring for details. -.. note: +.. note:: The runner to load tokens modules is :py:func:`saltutil.sync_eauth_tokens `. diff --git a/doc/topics/development/tests/index.rst b/doc/topics/development/tests/index.rst index 63b5478bbb79..c0d36acbfc01 100644 --- a/doc/topics/development/tests/index.rst +++ b/doc/topics/development/tests/index.rst @@ -69,7 +69,7 @@ test files for Salt execution modules. in the ``test_module_name_source_match`` function. This unit test ensures that we maintain the naming convention for test files. - .. __: https://github.com/saltstack/salt/blob/develop/tests/filename_map.yml + .. __: https://github.com/saltstack/salt/blob/|repo_primary_branch|/tests/filename_map.yml Integration Tests @@ -416,13 +416,13 @@ Tests for New Features If you are adding new functionality to Salt, please write the tests for this new feature in the same pull request as the new feature. New features should always be -submitted to the ``develop`` branch. +submitted to the ``|repo_primary_branch|`` branch. If you have already submitted the new feature, but did not write tests in the original pull request that has already been merged, please feel free to submit a new pull -request containing tests. If the feature was recently added to Salt's ``develop`` +request containing tests. If the feature was recently added to Salt's ``|repo_primary_branch|`` branch, then the tests should be added there as well. However, if the feature was -added to ``develop`` some time ago and is already present in one or more release +added to ``|repo_primary_branch|`` some time ago and is already present in one or more release branches, please refer to the `Tests for Entire Files or Functions`_ section below for more details about where to submit tests for functions or files that do not already have tests. @@ -448,7 +448,7 @@ earliest supported release branch that contains the file or function you're test Once your tests are submitted in a pull request and is merged into the branch in question, the tests you wrote will be merged-forward by SaltStack core engineers and the new tests will propagate to the newer release branches. That way the tests you -wrote will apply to all current and relevant release branches, and not just the ``develop`` +wrote will apply to all current and relevant release branches, and not just the ``|repo_primary_branch|`` branch, for example. This methodology will help protect against regressions on older files in Salt's codebase. diff --git a/doc/topics/development/tests/unit.rst b/doc/topics/development/tests/unit.rst index d129ad478ef5..54951e7ccda1 100644 --- a/doc/topics/development/tests/unit.rst +++ b/doc/topics/development/tests/unit.rst @@ -99,8 +99,8 @@ Salt loader modules use a series of globally available dunder variables, ``__salt__``, ``__opts__``, ``__pillar__``, etc. To facilitate testing these modules a mixin class was created, ``LoaderModuleMockMixin`` which can be found in ``tests/support/mixins.py``. The reason for the existence of this class is -because historiclly and because it was easier, one would add these dunder -variables directly on the imported module. This however, introduces unexpected +because historically one would add these dunder +variables directly on the imported module. This, however, introduces unexpected behavior when running the full test suite since those attributes would not be removed once we were done testing the module and would therefore leak to other modules being tested with unpredictable results. This is the kind of work that @@ -132,12 +132,10 @@ Consider this more extensive example from # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin - from tests.support.unit import TestCase, skipIf + from tests.support.unit import TestCase from tests.support.mock import ( patch, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import salt.modules.libcloud_dns as libcloud_dns @@ -151,7 +149,6 @@ Consider this more extensive example from return MockDNSDriver() - @skipIf(NO_MOCK, NO_MOCK_REASON) @patch('salt.modules.libcloud_dns._get_driver', MagicMock(return_value=MockDNSDriver())) class LibcloudDnsModuleTestCase(TestCase, LoaderModuleMockMixin): @@ -196,17 +193,14 @@ a separate implementation which has additional functionality. .. code-block:: python - from tests.support.unit import TestCase, skipIf + from tests.support.unit import TestCase from tests.support.mock import ( patch mock_open, - NO_MOCK, - NO_MOCK_REASON, ) import salt.modules.mymod as mymod - @skipIf(NO_MOCK, NO_MOCK_REASON) class MyAwesomeTestCase(TestCase): def test_something(self): @@ -251,17 +245,14 @@ those cases, you can pass ``read_data`` as a dictionary: import textwrap - from tests.support.unit import TestCase, skipIf + from tests.support.unit import TestCase from tests.support.mock import ( patch mock_open, - NO_MOCK, - NO_MOCK_REASON, ) import salt.modules.mymod as mymod - @skipIf(NO_MOCK, NO_MOCK_REASON) class MyAwesomeTestCase(TestCase): def test_something(self): @@ -324,17 +315,14 @@ Instead of a string, an exception can also be used as the ``read_data``: import errno - from tests.support.unit import TestCase, skipIf + from tests.support.unit import TestCase from tests.support.mock import ( patch mock_open, - NO_MOCK, - NO_MOCK_REASON, ) import salt.modules.mymod as mymod - @skipIf(NO_MOCK, NO_MOCK_REASON) class MyAwesomeTestCase(TestCase): def test_something(self): @@ -362,17 +350,14 @@ and produce a mocked filehandle with the specified contents. For example: import errno import textwrap - from tests.support.unit import TestCase, skipIf + from tests.support.unit import TestCase from tests.support.mock import ( patch mock_open, - NO_MOCK, - NO_MOCK_REASON, ) import salt.modules.mymod as mymod - @skipIf(NO_MOCK, NO_MOCK_REASON) class MyAwesomeTestCase(TestCase): def test_something(self): @@ -463,18 +448,15 @@ several useful attributes: .. code-block:: python - from tests.support.unit import TestCase, skipIf + from tests.support.unit import TestCase from tests.support.mock import ( patch mock_open, MockCall, - NO_MOCK, - NO_MOCK_REASON, ) import salt.modules.mymod as mymod - @skipIf(NO_MOCK, NO_MOCK_REASON) class MyAwesomeTestCase(TestCase): def test_something(self): @@ -607,13 +589,13 @@ Most commonly, the following imports are necessary to create a unit test: .. code-block:: python - from tests.support.unit import TestCase, skipIf + from tests.support.unit import TestCase If you need mock support to your tests, please also import: .. code-block:: python - from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch, call + from tests.support.mock import MagicMock, patch, call Evaluating Truth @@ -673,11 +655,10 @@ additional imports for MagicMock: from salt.modules import db # Import Mock libraries - from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch, call + from tests.support.mock import MagicMock, patch, call # Create test case class and inherit from Salt's customized TestCase # Skip this test case if we don't have access to mock! - @skipIf(NO_MOCK, NO_MOCK_REASON) class DbTestCase(TestCase): def test_create_user(self): # First, we replace 'execute_query' with our own mock function @@ -824,16 +805,13 @@ will also redefine the ``__salt__`` dictionary such that it only contains # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin - from tests.support.unit import skipIf, TestCase + from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) - @skipIf(NO_MOCK, NO_MOCK_REASON) class LinuxSysctlTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.linux_sysctl module @@ -926,16 +904,13 @@ with. # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin - from tests.support.unit import skipIf, TestCase + from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) - @skipIf(NO_MOCK, NO_MOCK_REASON) class LinuxSysctlTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.linux_sysctl module diff --git a/doc/topics/development/translating.rst b/doc/topics/development/translating.rst deleted file mode 100644 index dc650820074d..000000000000 --- a/doc/topics/development/translating.rst +++ /dev/null @@ -1,97 +0,0 @@ -Translating Documentation -========================= - -If you wish to help translate the Salt documentation to your language, please -head over to the `Transifex`_ website and `signup`__ for an account. - -Once registered, head over to the `Salt Translation Project`__, and either -click on **Request Language** if you can't find yours, or, select the language -for which you wish to contribute and click **Join Team**. - -`Transifex`_ provides some useful reading resources on their `support -domain`__, namely, some useful articles `directed to translators`__. - - -.. __: https://www.transifex.com/signup/ -.. __: https://www.transifex.com/projects/p/salt/ -.. __: http://support.transifex.com/ -.. __: http://support.transifex.com/customer/portal/topics/414107-translators/articles - - -Building A Localized Version of the Documentation -------------------------------------------------- - -While you're working on your translation on `Transifex`_, you might want to -have a look at how it's rendering. - - -Install The Transifex Client -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To interact with the `Transifex`_ web service you will need to install the -`transifex-client`__: - -.. code-block:: bash - - pip install transifex-client - - -.. __: https://github.com/transifex/transifex-client - - - -Configure The Transifex Client -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once installed, you will need to set it up on your computer. We created a -script to help you with that: - -.. code-block:: bash - - .scripts/setup-transifex-config - - - -Download Remote Translations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -There's a little script which simplifies the download process of the -translations(which isn't that complicated in the first place). -So, let's assume you're translating ``pt_PT``, Portuguese(Portugal). To -download the translations, execute from the ``doc/`` directory of your Salt -checkout: - -.. code-block:: bash - - - make download-translations SPHINXLANG=pt_PT - - -To download ``pt_PT``, Portuguese(Portugal), and ``nl``, Dutch, you can use the -helper script directly: - -.. code-block:: bash - - .scripts/download-translation-catalog pt_PT nl - - -Build Localized Documentation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -After the download process finishes, which might take a while, the next step is -to build a localized version of the documentation. -Following the ``pt_PT`` example above: - -.. code-block:: bash - - make html SPHINXLANG=pt_PT - - -View Localized Documentation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Open your browser, point it to the local documentation path and check the -localized output you've just build. - - -.. _`Transifex`: https://www.transifex.com diff --git a/doc/topics/development/tutorial.rst b/doc/topics/development/tutorial.rst index 77990b1e46db..df2fa1d288d0 100644 --- a/doc/topics/development/tutorial.rst +++ b/doc/topics/development/tutorial.rst @@ -48,21 +48,21 @@ completed, add SaltStack as a second remote and fetch any changes from $ git remote add upstream https://github.com/saltstack/salt.git $ git fetch upstream -For this tutorial, we will be working off from the ``develop`` branch, which is +For this tutorial, we will be working off from the ``|repo_primary_branch|`` branch, which is the default branch for the SaltStack GitHub project. This branch needs to -track ``upstream/develop`` so that we will get all upstream changes when they +track ``upstream/|repo_primary_branch|`` so that we will get all upstream changes when they happen. .. code-block:: bash - $ git checkout develop - $ git branch --set-upstream-to upstream/develop + $ git checkout |repo_primary_branch| + $ git branch --set-upstream-to upstream/|repo_primary_branch| ----- Fetch ----- -Fetch any ``upstream`` changes on the ``develop`` branch and sync them to your +Fetch any ``upstream`` changes on the ``|repo_primary_branch|`` branch and sync them to your local copy of the branch with a single command: .. code-block:: bash @@ -84,7 +84,7 @@ Now we are ready to get to work. Consult the `sprint beginner bug list and select an execution module whose ``__virtual__`` function needs to be updated. I'll select the ``alternatives`` module. -Create a new branch off from ``develop``. Be sure to name it something short +Create a new branch off from ``|repo_primary_branch|``. Be sure to name it something short and descriptive. .. code-block:: bash @@ -173,7 +173,7 @@ In your browser, navigate to the `new pull request `_ page on the ``saltstack/salt`` GitHub repository and click on ``compare across forks``. Select ```` from the list of head forks and the branch you are wanting to -merge into ``develop`` (``virt_ret`` in this case). +merge into ``|repo_primary_branch|`` (``virt_ret`` in this case). When you have finished reviewing the changes, click ``Create pull request``. @@ -194,7 +194,7 @@ request``. * I find it easiest to edit the following URL: - ``https://github.com/saltstack/salt/compare/develop...:virt_ret`` + ``https://github.com/saltstack/salt/compare/|repo_primary_branch|...:virt_ret`` --------- Resources diff --git a/doc/topics/grains/index.rst b/doc/topics/grains/index.rst index 106f0829cf92..8cfe7d1da978 100644 --- a/doc/topics/grains/index.rst +++ b/doc/topics/grains/index.rst @@ -63,8 +63,7 @@ Just add the option :conf_minion:`grains` and pass options to it: cab_u: 14-15 Then status data specific to your servers can be retrieved via Salt, or used -inside of the State system for matching. It also makes targeting, in the case -of the example above, simply based on specific data about your deployment. +inside of the State system for matching. It also makes it possible to target based on specific data about your deployment, as in the example above. Grains in /etc/salt/grains @@ -292,7 +291,7 @@ the Salt minion and provides the principal example of how to write grains: Syncing Grains ============== -Syncing grains can be done a number of ways, they are automatically synced when +Syncing grains can be done a number of ways. They are automatically synced when :mod:`state.highstate ` is called, or (as noted above) the grains can be manually synced and reloaded by calling the :mod:`saltutil.sync_grains ` or diff --git a/doc/topics/index.rst b/doc/topics/index.rst index 47656ebb4f87..8ffba2c26b1a 100644 --- a/doc/topics/index.rst +++ b/doc/topics/index.rst @@ -9,13 +9,13 @@ The 30 second summary Salt is: -* a configuration management system, capable of maintaining remote nodes - in defined states (for example, ensuring that specific packages are installed and - specific services are running) +* **A configuration management system.** Salt is capable of maintaining remote + nodes in defined states. For example, it can ensure that specific packages are + installed and that specific services are running. -* a distributed remote execution system used to execute commands and - query data on remote nodes, either individually or by arbitrary - selection criteria +* **A distributed remote execution system used to execute commands and + query data on remote nodes.** Salt can query and execute commands either on + individual nodes or by using an arbitrary selection criteria. It was developed in order to bring the best solutions found in the world of remote execution together and make them better, faster, and more @@ -173,7 +173,6 @@ Other community links - `Salt Stack Inc. `_ - `Subreddit `_ -- `Google+ `_ - `YouTube `_ - `Facebook `_ - `Twitter `_ @@ -188,4 +187,3 @@ documentation efforts, please review the :ref:`contributing documentation `! .. _`Apache 2.0 license`: http://www.apache.org/licenses/LICENSE-2.0.html - diff --git a/doc/topics/installation/freebsd.rst b/doc/topics/installation/freebsd.rst index e54e7fc67856..0c873f2616da 100644 --- a/doc/topics/installation/freebsd.rst +++ b/doc/topics/installation/freebsd.rst @@ -12,36 +12,45 @@ Salt is available in the FreeBSD ports tree at `sysutils/py-salt FreeBSD binary repo =================== + +For Python 2.7 use: + +Install Salt via the official package repository. Salt is packaged both as a Python 2.7 or 3.6 version. + + .. code-block:: bash pkg install py27-salt -FreeBSD ports -============= -By default salt is packaged using python 2.7, but if you build your own packages from FreeBSD ports either by hand or with poudriere you can instead package it with your choice of python. Add a line to /etc/make.conf to choose your python flavour: +For Python 3.6 use: -.. code-block:: text - echo "DEFAULT_VERSIONS+= python=3.6" >> /etc/make.conf +.. code-block:: bash + + pkg install py36-salt + + +FreeBSD ports +============= -Then build the port and install: +Installation from ports: .. code-block:: bash cd /usr/ports/sysutils/py-salt make install -Post-installation tasks -======================= +Python 3.6 can be used by setting default Python version to 3.6: + +.. code-block:: text -**Master** + echo "DEFAULT_VERSIONS+= python=3.6" >> /etc/make.conf -Copy the sample configuration file: -.. code-block:: bash +Post-installation tasks +======================= - cp /usr/local/etc/salt/master.sample /usr/local/etc/salt/master **rc.conf** @@ -59,14 +68,6 @@ Start the Salt Master as follows: service salt_master start -**Minion** - -Copy the sample configuration file: - -.. code-block:: bash - - cp /usr/local/etc/salt/minion.sample /usr/local/etc/salt/minion - **rc.conf** Activate the Salt Minion in ``/etc/rc.conf``: diff --git a/doc/topics/installation/suse.rst b/doc/topics/installation/suse.rst index 5c1fc3d9c4b7..75e97cbf0537 100644 --- a/doc/topics/installation/suse.rst +++ b/doc/topics/installation/suse.rst @@ -115,26 +115,20 @@ For openSUSE Tumbleweed run the following as root: .. code-block:: bash - zypper addrepo http://download.opensuse.org/repositories/systemsmanagement:/saltstack/openSUSE_Tumbleweed/systemsmanagement:saltstack.repo - zypper refresh zypper install salt salt-minion salt-master -For openSUSE 42.1 Leap run the following as root: +For openSUSE 15.0 Leap run the following as root: .. code-block:: bash - zypper addrepo http://download.opensuse.org/repositories/systemsmanagement:/saltstack/openSUSE_Leap_42.1/systemsmanagement:saltstack.repo - zypper refresh zypper install salt salt-minion salt-master -For openSUSE 13.2 run the following as root: +For openSUSE 15.1 run the following as root: .. code-block:: bash - zypper addrepo http://download.opensuse.org/repositories/systemsmanagement:/saltstack/openSUSE_13.2/systemsmanagement:saltstack.repo - zypper refresh zypper install salt salt-minion salt-master SUSE Linux Enterprise diff --git a/doc/topics/installation/windows.rst b/doc/topics/installation/windows.rst index 964f263af989..346551d45bb7 100644 --- a/doc/topics/installation/windows.rst +++ b/doc/topics/installation/windows.rst @@ -265,7 +265,7 @@ customize or fix bugs. It will also allow you to build your own installation. There are several scripts to automate creating a Windows installer as well as setting up an environment that facilitates developing and troubleshooting Salt code. They are located in the ``pkg\windows`` directory in the Salt repo -`(here) `_. +`(here) `_. Scripts: -------- diff --git a/doc/topics/jinja/index.rst b/doc/topics/jinja/index.rst index d8e12cf84d02..3783769e7cec 100644 --- a/doc/topics/jinja/index.rst +++ b/doc/topics/jinja/index.rst @@ -34,7 +34,7 @@ wrap conditional or redundant state elements: {% endif %} - source: salt://motd -In this example, the first if block will only be evaluated on minions that +In this example, the first **if** block will only be evaluated on minions that aren't running FreeBSD, and the second block changes the file name based on the *os* grain. @@ -656,7 +656,7 @@ Returns: .. versionadded:: 2017.7.0 -Return is an iterable object is already sorted. +Return ``True`` if an iterable object is already sorted. Example: @@ -690,7 +690,7 @@ Returns: .. code-block:: python - {'new': 4, 'old': 3} + {'new': [4], 'old': [3]} .. jinja_ref:: compare_dicts @@ -706,7 +706,7 @@ Example: .. code-block:: jinja - {{ {'a': 'b'} | compare_lists({'a': 'c'}) }} + {{ {'a': 'b'} | compare_dicts({'a': 'c'}) }} Returns: @@ -722,7 +722,7 @@ Returns: .. versionadded:: 2017.7.0 -Return True if the value is hexazecimal. +Return ``True`` if the value is hexadecimal. Example: @@ -746,7 +746,7 @@ Returns: .. versionadded:: 2017.7.0 -Return True if a text contains whitespaces. +Return ``True`` if a text contains whitespaces. Example: @@ -770,7 +770,7 @@ Returns: .. versionadded:: 2017.7.0 -Return is a substring is found in a list of string values. +Return ``True`` if a substring is found in a list of string values. Example: @@ -1019,6 +1019,137 @@ Returns: d94a45acd81f8e3107d237dbc0d5d195f6a52a0d188bc0284c0763ece1eac9f9496fb6a531a296074c87b3540398dace1222b42e150e67c9301383fde3d66ae5 +.. jinja_ref:: set_dict_key_value + +``set_dict_key_value`` +---------------------- + +..versionadded:: Neon + +Allows you to set a value in a nested dictionary without having to worry if all the nested keys actually exist. +Missing keys will be automatically created if they do not exist. +The default delimiter for the keys is ':', however, with the `delimiter`-parameter, a different delimiter can be specified. + +Examples: + +.. code-block:: jinja + +Example 1: + {%- set foo = {} %} + {{ foo | set_dict_key_value('bar:baz', 42) }} + +Example 2: + {{ {} | set_dict_key_value('bar.baz.qux', 42, delimiter='.') }} + +Returns: + +.. code-block:: text + +Example 1: + {'bar': {'baz': 42}} + +Example 2: + {'bar': {'baz': {'qux': 42}}} + + +.. jinja_ref:: append_dict_key_value + +``append_dict_key_value`` +------------------------- + +..versionadded:: Neon + +Allows you to append to a list nested (deep) in a dictionary without having to worry if all the nested keys (or the list itself) actually exist. +Missing keys will automatically be created if they do not exist. +The default delimiter for the keys is ':', however, with the `delimiter`-parameter, a different delimiter can be specified. + +Examples: + +.. code-block:: jinja + +Example 1: + {%- set foo = {'bar': {'baz': [1, 2]}} %} + {{ foo | append_dict_key_value('bar:baz', 42) }} + +Example 2: + {%- set foo = {} %} + {{ foo | append_dict_key_value('bar:baz:qux', 42) }} + +Returns: + +.. code-block:: text + +Example 1: + {'bar': {'baz': [1, 2, 42]}} + +Example 2: + {'bar': {'baz': {'qux': [42]}}} + + +.. jinja_ref:: extend_dict_key_value + +``extend_dict_key_value`` +------------------------- + +..versionadded:: Neon + +Allows you to extend a list nested (deep) in a dictionary without having to worry if all the nested keys (or the list itself) actually exist. +Missing keys will automatically be created if they do not exist. +The default delimiter for the keys is ':', however, with the `delimiter`-parameter, a different delimiter can be specified. + +Examples: + +.. code-block:: jinja + +Example 1: + {%- set foo = {'bar': {'baz': [1, 2]}} %} + {{ foo | extend_dict_key_value('bar:baz', [42, 42]) }} + +Example 2: + {{ {} | extend_dict_key_value('bar:baz:qux', [42]) }} + +Returns: + +.. code-block:: text + +Example 1: + {'bar': {'baz': [1, 2, 42, 42]}} + +Example 2: + {'bar': {'baz': {'qux': [42]}}} + + +.. jinja_ref:: update_dict_key_value + +``update_dict_key_value`` +------------------------- + +..versionadded:: Neon + +Allows you to update a dictionary nested (deep) in another dictionary without having to worry if all the nested keys actually exist. +Missing keys will automatically be created if they do not exist. +The default delimiter for the keys is ':', however, with the `delimiter`-parameter, a different delimiter can be specified. + +Examples: + +.. code-block:: jinja + +Example 1: + {%- set foo = {'bar': {'baz': {'qux': 1}}} %} + {{ foo | update_dict_key_value('bar:baz', {'quux': 3}) }} + +Example 2: + {{ {} | update_dict_key_value('bar:baz:qux', {'quux': 3}) }} + +.. code-block:: text + +Example 1: + {'bar': {'baz': {'qux': 1, 'quux': 3}}} + +Example 2: + {'bar': {'baz': {'qux': {'quux': 3}}}} + + .. jinja_ref:: md5 ``md5`` diff --git a/doc/topics/pillar/index.rst b/doc/topics/pillar/index.rst index db0dabd593cf..1a6aa511fbe4 100644 --- a/doc/topics/pillar/index.rst +++ b/doc/topics/pillar/index.rst @@ -741,11 +741,11 @@ done: option in the `master config template`_ should be updated to show the correct new default value. -.. _`salt/renderers/`: https://github.com/saltstack/salt/tree/develop/salt/renderers/ -.. _`salt/config/__init__.py`: https://github.com/saltstack/salt/tree/develop/salt/config/__init__.py -.. _`master config file`: https://github.com/saltstack/salt/tree/develop/doc/ref/configuration/master.rst -.. _`minion config file`: https://github.com/saltstack/salt/tree/develop/doc/ref/configuration/minion.rst -.. _`master config template`: https://github.com/saltstack/salt/tree/develop/conf/master +.. _`salt/renderers/`: https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/renderers/ +.. _`salt/config/__init__.py`: https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/config/__init__.py +.. _`master config file`: https://github.com/saltstack/salt/tree/|repo_primary_branch|/doc/ref/configuration/master.rst +.. _`minion config file`: https://github.com/saltstack/salt/tree/|repo_primary_branch|/doc/ref/configuration/minion.rst +.. _`master config template`: https://github.com/saltstack/salt/tree/|repo_primary_branch|/conf/master Binary Data in the Pillar ========================= diff --git a/doc/topics/proxyminion/beacon.rst b/doc/topics/proxyminion/beacon.rst index e02402e7aa63..d61d3bdaa74c 100644 --- a/doc/topics/proxyminion/beacon.rst +++ b/doc/topics/proxyminion/beacon.rst @@ -22,15 +22,13 @@ configuring and managing multiple ``salt-proxy`` processes. - p8000 This says that Salt's pillar should load some values for the proxy ``p8000`` -from the file /srv/pillar/p8000.sls (if you have not changed your default pillar_roots) +from the file ``/srv/pillar/p8000.sls`` (if you have not changed your default pillar_roots) -2. In the pillar root for your base environment, create this file: +2. In the pillar root for your base environment, create the ``p8000.sls`` file with the + following contents: .. code-block:: yaml - p8000.sls - --------- - proxy: # set proxytype for your proxymodule proxytype: ssh_sample diff --git a/doc/topics/proxyminion/demo.rst b/doc/topics/proxyminion/demo.rst index 87c585e484b2..b8ffa1c41d98 100644 --- a/doc/topics/proxyminion/demo.rst +++ b/doc/topics/proxyminion/demo.rst @@ -58,16 +58,14 @@ Now, configure your salt-proxy. This says that Salt's pillar should load some values for the proxy ``p8000`` -from the file /srv/pillar/p8000.sls (if you have not changed your default pillar_roots) +from the file ``/srv/pillar/p8000.sls`` (if you have not changed your default pillar_roots) -3. In the pillar root for your base environment, create this file: +3. In the pillar root for your base environment, create the ``p8000.sls`` file with the + following contents: .. code-block:: yaml - p8000.sls - --------- - proxy: proxytype: rest_sample url: http://:port diff --git a/doc/topics/proxyminion/index.rst b/doc/topics/proxyminion/index.rst index d28970237441..3bfd76c1906c 100644 --- a/doc/topics/proxyminion/index.rst +++ b/doc/topics/proxyminion/index.rst @@ -10,13 +10,13 @@ network gear that has an API but runs a proprietary OS, devices with limited CPU or memory, or devices that could run a minion, but for security reasons, will not. -*Proxy minions are not an "out of the box" feature*. Because there are an -infinite number of controllable devices, you will most likely have to write the -interface yourself. Fortunately, this is only as difficult as the actual -interface to the proxied device. Devices that have an existing Python module -(PyUSB for example) would be relatively simple to interface. Code to control a -device that has an HTML REST-based interface should be easy. Code to control -your typical housecat would be excellent source material for a PhD thesis. +There are some :ref:`proxy modules ` available, but if your device +interface is not currently supported you will most likely have to write the interface +yourself, because there are an infinite number of controllable devices. Fortunately, this +is only as difficult as the actual interface to the proxied device. Devices that have an +existing Python module (PyUSB for example) would be relatively simple to interface. +Code to control a device that has an HTML REST-based interface should be easy. Code to +control your typical housecat would be excellent source material for a PhD thesis. Salt proxy-minions provide the 'plumbing' that allows device enumeration and discovery, control, status, remote execution, and state management. diff --git a/doc/topics/proxyminion/ssh.rst b/doc/topics/proxyminion/ssh.rst index 445dcbf531be..f9d180b4709e 100644 --- a/doc/topics/proxyminion/ssh.rst +++ b/doc/topics/proxyminion/ssh.rst @@ -35,16 +35,14 @@ Now, configure your salt-proxy. This says that Salt's pillar should load some values for the proxy ``p8000`` -from the file /srv/pillar/p8000.sls (if you have not changed your default pillar_roots) +from the file ``/srv/pillar/p8000.sls`` (if you have not changed your default pillar_roots) -3. In the pillar root for your base environment, create this file: +3. In the pillar root for your base environment, create the ``p8000.sls`` file with the + following contents: .. code-block:: yaml - p8000.sls - --------- - proxy: proxytype: ssh_sample host: saltyVM diff --git a/doc/topics/proxyminion/state.rst b/doc/topics/proxyminion/state.rst index e49b1df86421..c5e6642918b2 100644 --- a/doc/topics/proxyminion/state.rst +++ b/doc/topics/proxyminion/state.rst @@ -23,15 +23,13 @@ on the remote end. - p8000 This says that Salt's pillar should load some values for the proxy ``p8000`` -from the file /srv/pillar/p8000.sls (if you have not changed your default pillar_roots) +from the file ``/srv/pillar/p8000.sls`` (if you have not changed your default pillar_roots) -2. In the pillar root for your base environment, create this file: +2. In the pillar root for your base environment, create the ``p8000.sls`` file with the + following contents: .. code-block:: yaml - p8000.sls - --------- - proxy: # set proxytype for your proxymodule proxytype: ssh_sample diff --git a/doc/topics/releases/2019.2.0.rst b/doc/topics/releases/2019.2.0.rst index fe0af4e7bcc8..d719cf92f122 100644 --- a/doc/topics/releases/2019.2.0.rst +++ b/doc/topics/releases/2019.2.0.rst @@ -929,11 +929,12 @@ you can now configure which type numbers indicate a login and logout. See the :py:mod:`wtmp beacon documentation ` for more information. -Deprecations -============ -API Deprecations ----------------- +Deprecated and Removed Options +============================== + +API Removed Arguments +--------------------- Support for :ref:`LocalClient `'s ``expr_form`` argument has been removed. Please use ``tgt_type`` instead. This change was made due to @@ -952,14 +953,14 @@ their code to use ``tgt_type``. >>> local.cmd('*', 'cmd.run', ['whoami'], tgt_type='glob') {'jerry': 'root'} -Minion Configuration Deprecations ---------------------------------- +Minion Configuration Deprecated Option +-------------------------------------- The :conf_minion:`master_shuffle` configuration option is deprecated as of the ``2019.2.0`` release. Please use the :conf_minion:`random_master` option instead. -Module Deprecations -------------------- +Module Removed Options +---------------------- - The :py:mod:`napalm_network ` module has been changed as follows: @@ -1011,8 +1012,8 @@ Module Deprecations functions have been removed. Please use :py:func:`win_wua.list ` instead. -Pillar Deprecations -------------------- +Pillar Removed Option +--------------------- - The :py:mod:`vault ` external pillar has been changed as follows: @@ -1020,8 +1021,8 @@ Pillar Deprecations - Support for the ``profile`` argument was removed. Any options passed up until and following the first ``path=`` are discarded. -Roster Deprecations -------------------- +Roster Removed Option +--------------------- - The :py:mod:`cache ` roster has been changed as follows: @@ -1032,8 +1033,8 @@ Roster Deprecations ``private``, ``public``, ``global`` or ``local`` settings. The syntax for these settings has changed to ``ipv4-*`` or ``ipv6-*``, respectively. -State Deprecations ------------------- +State Removed Modules and Options +--------------------------------- - The ``docker`` state module has been removed @@ -1126,8 +1127,8 @@ State Deprecations - Support for virtual packages has been removed from the py:mod:`pkg state `. -Utils Deprecations ------------------- +Utils Removed Options +--------------------- The ``cloud`` utils module had the following changes: @@ -1151,7 +1152,7 @@ been deprecated in favor of ``pypsexec``. Salt-Cloud has deprecated the use ``impacket`` in favor of ``smbprotocol``. This changes was made because ``impacket`` is not compatible with Python 3. -SaltSSH major updates +SaltSSH Major Updates ===================== SaltSSH now works across different major Python versions. Python 2.7 ~ Python 3.x diff --git a/doc/topics/releases/2019.2.1.rst b/doc/topics/releases/2019.2.1.rst index dc80598f8924..84ebf4579009 100644 --- a/doc/topics/releases/2019.2.1.rst +++ b/doc/topics/releases/2019.2.1.rst @@ -1,3 +1,5 @@ +.. _release-2019-2-1: + =========================== Salt 2019.2.1 Release Notes =========================== @@ -49,7 +51,7 @@ the YAML renderer that now require the new Jinja filter `tojson`. In 2019.2.1, we introduce a new configuration option for both the Salt master and Salt minion configurations to be able to support the older YAML renderer. Using the option -`use_yamlloader_old` will allow the YAML renderer to function as before. +``use_yamlloader_old`` will allow the YAML renderer to function as before. Statistics ========== diff --git a/doc/topics/releases/2019.2.2.rst b/doc/topics/releases/2019.2.2.rst index b3d1302a651a..0246aadb6ae1 100644 --- a/doc/topics/releases/2019.2.2.rst +++ b/doc/topics/releases/2019.2.2.rst @@ -1,3 +1,5 @@ +.. _release-2019-2-2: + =========================== Salt 2019.2.2 Release Notes =========================== @@ -7,16 +9,72 @@ Version 2019.2.2 is a bugfix release for :ref:`2019.2.0 `. Statistics ========== -- Total Merges: **20** -- Total Issue References: **11** -- Total PR References: **20** +- Total Merges: **26** +- Total Issue References: **12** +- Total PR References: **26** -- Contributors: **12** (`Akm0d`_, `Ch3LL`_, `Oloremo`_, `OrlandoArcapix`_, `bryceml`_, `dhiltonp`_, `dwoz`_, `frogunder`_, `garethgreenaway`_, `javierbertoli`_, `pizzapanther`_, `s0undt3ch`_) +- Contributors: **13** (`Akm0d`_, `Ch3LL`_, `Oloremo`_, `OrlandoArcapix`_, `bryceml`_, `dhiltonp`_, `dwoz`_, `frogunder`_, `garethgreenaway`_, `javierbertoli`_, `pizzapanther`_, `s0undt3ch`_, `twangboy`_) Changelog for v2019.2.1..v2019.2.2 ================================== -*Generated at: 2019-10-02 18:33:46 UTC* +*Generated at: 2019-10-11 20:54:15 UTC* + +* **PR** `#54919`_: (`twangboy`_) Add missing docs for win_wusa state and module (2019.2.1) + @ *2019-10-11 18:28:11 UTC* + + * 7d253bc Merge pull request `#54919`_ from twangboy/update_docs + + * 57ff199 Add docs for win_wusa + +* **ISSUE** `#54941`_: (`UtahDave`_) Pillar data is refreshed for EVERY salt command in 2019.2.1 and 2019.2.2 (refs: `#54942`_) + +* **PR** `#54942`_: (`dwoz`_) Fix for 54941 pillar_refresh regression + @ *2019-10-11 18:27:31 UTC* + + * 2f817bc Merge pull request `#54942`_ from dwoz/fix-54941 + + * cb5d326 Add a test for 54941 using test.ping + + * 348d1c4 Add regression tests for issue 54941 + + * 766f3ca Initial commit of a potential fix for 54941 + +* **PR** `#54897`_: (`bryceml`_) update version numbers to be correct + @ *2019-10-05 01:59:14 UTC* + + * f783108 Merge pull request `#54897`_ from bryceml/2019.2.1_fix_docs + + * e9a2a70 update version numbers to be correct + +* **PR** `#54894`_: (`bryceml`_) 2019.2.1 fix docs + @ *2019-10-04 22:31:26 UTC* + + * 3233663 Merge pull request `#54894`_ from bryceml/2019.2.1_fix_docs + + * 2456aaa Porting PR `#52948`_ to 2019.2.1 + + * 94a1e3b Porting PR `#52752`_ to 2019.2.1 + + * c7b7474 modifying saltconf ads + + * d48057b add new saltconf ads + +* **PR** `#54858`_: (`frogunder`_) remove in progress from releasenotes 2019.2.2 + @ *2019-10-02 20:42:59 UTC* + + * 4b06eca Merge pull request `#54858`_ from frogunder/releasenotes_remove2019.2.2 + + * a697abd remove in progress from releasenotes 2019.2.2 + +* **PR** `#54854`_: (`frogunder`_) releasenotes 2019.2.2 + @ *2019-10-02 18:58:21 UTC* + + * aaf2d1c Merge pull request `#54854`_ from frogunder/release_notes_2019.2.2 + + * a41dc59 Update 2019.2.2.rst + + * 9bea043 releasenotes 2019.2.2 * **PR** `#54852`_: (`frogunder`_) Update man pages for 2019.2.2 @ *2019-10-02 18:27:07 UTC* @@ -97,7 +155,7 @@ Changelog for v2019.2.1..v2019.2.2 * c585550 fix broken salt-cloud openstack query -* **ISSUE** `#54762`_: (`margau`_) 2019.2.1: Breaks Minion-Master Communication (refs: `#54807`_, `#54784`_, `#54823`_) +* **ISSUE** `#54762`_: (`margau`_) 2019.2.1: Breaks Minion-Master Communication (refs: `#54823`_, `#54784`_, `#54807`_) * **PR** `#54823`_: (`dhiltonp`_) ip_bracket can now accept ipv6 addresses with brackets @ *2019-10-01 01:13:34 UTC* @@ -106,7 +164,7 @@ Changelog for v2019.2.1..v2019.2.2 * faa1d98 ip_bracket can now accept ipv6 addresses with brackets -* **ISSUE** `#54762`_: (`margau`_) 2019.2.1: Breaks Minion-Master Communication (refs: `#54807`_, `#54784`_, `#54823`_) +* **ISSUE** `#54762`_: (`margau`_) 2019.2.1: Breaks Minion-Master Communication (refs: `#54823`_, `#54784`_, `#54807`_) * **PR** `#54807`_: (`dwoz`_) Fix pip state pip >=10.0 and <=18.0 @ *2019-09-30 09:20:14 UTC* @@ -193,7 +251,7 @@ Changelog for v2019.2.1..v2019.2.2 * a35e609 Adding __init__.py to metaproxy directory so that metaproxy is included when running setup.py. -* **ISSUE** `#54762`_: (`margau`_) 2019.2.1: Breaks Minion-Master Communication (refs: `#54807`_, `#54784`_, `#54823`_) +* **ISSUE** `#54762`_: (`margau`_) 2019.2.1: Breaks Minion-Master Communication (refs: `#54823`_, `#54784`_, `#54807`_) * **PR** `#54784`_: (`dhiltonp`_) fix dns_check to return uri-compatible ipv6 addresses, add tests @ *2019-09-28 08:36:51 UTC* @@ -242,6 +300,8 @@ Changelog for v2019.2.1..v2019.2.2 * 168a6c1 switch to ruby 2.6.3 .. _`#1`: https://github.com/saltstack/salt/issues/1 +.. _`#52752`: https://github.com/saltstack/salt/pull/52752 +.. _`#52948`: https://github.com/saltstack/salt/pull/52948 .. _`#54521`: https://github.com/saltstack/salt/issues/54521 .. _`#54706`: https://github.com/saltstack/salt/pull/54706 .. _`#54731`: https://github.com/saltstack/salt/pull/54731 @@ -271,12 +331,20 @@ Changelog for v2019.2.1..v2019.2.2 .. _`#54830`: https://github.com/saltstack/salt/pull/54830 .. _`#54845`: https://github.com/saltstack/salt/pull/54845 .. _`#54852`: https://github.com/saltstack/salt/pull/54852 +.. _`#54854`: https://github.com/saltstack/salt/pull/54854 +.. _`#54858`: https://github.com/saltstack/salt/pull/54858 +.. _`#54894`: https://github.com/saltstack/salt/pull/54894 +.. _`#54897`: https://github.com/saltstack/salt/pull/54897 +.. _`#54919`: https://github.com/saltstack/salt/pull/54919 +.. _`#54941`: https://github.com/saltstack/salt/issues/54941 +.. _`#54942`: https://github.com/saltstack/salt/pull/54942 .. _`Akm0d`: https://github.com/Akm0d .. _`Ch3LL`: https://github.com/Ch3LL .. _`Oloremo`: https://github.com/Oloremo .. _`OrangeDog`: https://github.com/OrangeDog .. _`OrlandoArcapix`: https://github.com/OrlandoArcapix .. _`Reiner030`: https://github.com/Reiner030 +.. _`UtahDave`: https://github.com/UtahDave .. _`awerner`: https://github.com/awerner .. _`bryceml`: https://github.com/bryceml .. _`dhiltonp`: https://github.com/dhiltonp @@ -290,3 +358,4 @@ Changelog for v2019.2.1..v2019.2.2 .. _`paul-palmer`: https://github.com/paul-palmer .. _`pizzapanther`: https://github.com/pizzapanther .. _`s0undt3ch`: https://github.com/s0undt3ch +.. _`twangboy`: https://github.com/twangboy diff --git a/doc/topics/releases/neon.rst b/doc/topics/releases/neon.rst index 2396068ff78c..07349827949f 100644 --- a/doc/topics/releases/neon.rst +++ b/doc/topics/releases/neon.rst @@ -4,8 +4,197 @@ Salt Release Notes - Codename Neon ================================== + +Keystore State and Module +========================= + +A new :py:func:`state ` and +:py:func:`execution module ` for manaing Java +Keystore files is now included. It allows for adding/removing/listing +as well as managing keystore files. + +.. code-block:: bash + + # salt-call keystore.list /path/to/keystore.jks changeit + local: + |_ + ---------- + alias: + hostname1 + expired: + True + sha1: + CB:5E:DE:50:57:99:51:87:8E:2E:67:13:C5:3B:E9:38:EB:23:7E:40 + type: + TrustedCertEntry + valid_start: + August 22 2012 + valid_until: + August 21 2017 + +.. code-block:: yaml + + define_keystore: + keystore.managed: + - name: /tmp/statestore.jks + - passphrase: changeit + - force_remove: True + - entries: + - alias: hostname1 + certificate: /tmp/testcert.crt + - alias: remotehost + certificate: /tmp/512.cert + private_key: /tmp/512.key + - alias: stringhost + certificate: | + -----BEGIN CERTIFICATE----- + MIICEjCCAX + Hn+GmxZA + -----END CERTIFICATE----- + + +Troubleshooting Jinja map files +=============================== + +A new :py:func:`execution module ` for ``map.jinja`` troubleshooting +has been added. + +Assuming the map is loaded in your formula SLS as follows: + +.. code-block:: jinja + + {% from "myformula/map.jinja" import myformula with context %} + +The following command can be used to load the map and check the results: + +.. code-block:: bash + + salt myminion jinja.load_map myformula/map.jinja myformula + +The module can be also used to test ``json`` and ``yaml`` maps: + +.. code-block:: bash + + salt myminion jinja.import_yaml myformula/defaults.yaml + + salt myminion jinja.import_json myformula/defaults.json + + +Slot Syntax Updates +=================== + +The slot syntax has been updated to support parsing dictionary responses and to append text. + +.. code-block:: yaml + + demo dict parsing and append: + test.configurable_test_state: + - name: slot example + - changes: False + - comment: __slot__:salt:test.arg(shell="/bin/bash").kwargs.shell ~ /appended + +.. code-block:: none + + local: + ---------- + ID: demo dict parsing and append + Function: test.configurable_test_state + Name: slot example + Result: True + Comment: /bin/bash/appended + Started: 09:59:58.623575 + Duration: 1.229 ms + Changes: + + +State Changes +============= + +- Added new :py:func:`ssh_auth.manage ` state to + ensure only the specified ssh keys are present for the specified user. + Module Changes ============== - Added new :py:func:`boto_ssm ` module to set and query secrets in AWS SSM parameters. + +Deprecations +============ + +Module Deprecations +------------------- + +- The hipchat module has been removed due to the service being retired. + :py:func:`Google Chat `, + :py:func:`MS Teams `, or + :py:func:`Slack ` may be suitable replacements. + +- The :py:mod:`dockermod ` module has been + changed as follows: + + - Support for the ``tags`` kwarg has been removed from the + :py:func:`dockermod.resolve_tag ` + function. + - Support for the ``network_id`` kwarg has been removed from the + :py:func:`dockermod.connect_container_to_network ` + function. Please use ``net_id`` instead. + - Support for the ``name`` kwarg has been removed from the + :py:func:`dockermod.sls_build ` + function. Please use ``repository`` and ``tag`` instead. + - Support for the ``image`` kwarg has been removed from the following + functions. In all cases, please use both the ``repository`` and ``tag`` + options instead: + + - :py:func:`dockermod.build ` + - :py:func:`dockermod.commit ` + - :py:func:`dockermod.import ` + - :py:func:`dockermod.load ` + - :py:func:`dockermod.tag ` + +State Deprecations +------------------ + +- The hipchat state has been removed due to the service being retired. + :py:func:`MS Teams ` or + :py:func:`Slack ` may be suitable replacements. + +Fileserver Deprecations +----------------------- + +- The hgfs fileserver had the following config options removed: + + - The ``hgfs_env_whitelist`` config option has been removed in favor of ``hgfs_saltenv_whitelist``. + - The ``hgfs_env_blacklist`` config option has been removed in favor of ``hgfs_saltenv_blacklist``. + +- The svnfs fileserver had the following config options removed: + + - The ``svnfs_env_whitelist`` config option has been removed in favor of ``svnfs_saltenv_whitelist``. + - The ``svnfs_env_blacklist`` config option has been removed in favor of ``svnfs_saltenv_blacklist``. + +Engine Removal +-------------- + +- The hipchat engine has been removed due to the service being retired. For users migrating + to Slack, the :py:func:`slack ` engine may be a suitable replacement. + +Returner Removal +---------------- + +- The hipchat returner has been removed due to the service being retired. For users migrating + to Slack, the :py:func:`slack ` returner may be a suitable + replacement. + +Grain Deprecations +------------------ + +For ``smartos`` some grains have been deprecated. These grains have been removed. + + - The ``hypervisor_uuid`` has been replaced with ``mdata:sdc:server_uuid`` grain. + - The ``datacenter`` has been replaced with ``mdata:sdc:datacenter_name`` grain. + +salt.auth.Authorize Class Removal +--------------------------------- +- The salt.auth.Authorize Class inside of the `salt/auth/__init__.py` file has been removed and + the `any_auth` method inside of the file `salt/utils/minions.py`. These method and classes were + not being used inside of the salt code base. diff --git a/doc/topics/releases/releasecandidate.rst b/doc/topics/releases/releasecandidate.rst index db14777026e8..7306a8e27dd1 100644 --- a/doc/topics/releases/releasecandidate.rst +++ b/doc/topics/releases/releasecandidate.rst @@ -8,7 +8,7 @@ Installing/Testing a Salt Release Candidate It's time for a new feature release of Salt! Follow the instructions below to install the latest release candidate of Salt, and try :ref:`all the shiny new -features `! Be sure to report any bugs you find on `Github +features `! Be sure to report any bugs you find on `Github `_. Installing Using Packages @@ -18,22 +18,52 @@ Builds for a few platforms are available as part of the RC at https://repo.salts .. note:: - For RHEL and Ubuntu, Follow the instructions on - https://repo.saltstack.com/, but insert ``salt_rc/`` into the URL between - the hostname and the remainder of the path. For example: + Follow the instructions on https://repo.saltstack.com/, + but insert ``salt_rc/`` into the URL between the hostname + and the remainder of the path. + + For Redhat Python 2 .. code-block:: bash baseurl=https://repo.saltstack.com/salt_rc/yum/redhat/$releasever/$basearch/ + For Redhat Python 3 + + .. code-block:: bash + + baseurl=https://repo.saltstack.com/salt_rc/py3/redhat/$releasever/$basearch/ + + For Ubuntu Python 2 + + .. code-block:: none + + deb http://repo.saltstack.com/salt_rc/apt/ubuntu/18.04/amd64 bionic main + + For Ubuntu Python 3 + .. code-block:: none - deb http://repo.saltstack.com/salt_rc/apt/ubuntu/14.04/amd64 jessie main + deb http://repo.saltstack.com/salt_rc/py3/ubuntu/18.04/amd64 bionic main + + For Debian Python 2 + + .. code-block:: none + + deb http://repo.saltstack.com/salt_rc/apt/debian/9/amd64 stretch main + + For Debian Python 3 + + .. code-block:: none + + deb http://repo.saltstack.com/salt_rc/py3/debian/9/amd64 stretch main + Available builds: -- Ubuntu16 -- Redhat7 +- Ubuntu 18 +- Debian 9 +- Redhat 7 - Windows .. FreeBSD @@ -47,14 +77,14 @@ You can install a release candidate of Salt using `Salt Bootstrap .. code-block:: bash curl -o install_salt.sh -L https://bootstrap.saltstack.com - sudo sh install_salt.sh -P git v2018.3.0rc1 + sudo sh install_salt.sh -P git v2019.2.0rc1 If you want to also install a master using Salt Bootstrap, use the ``-M`` flag: .. code-block:: bash curl -o install_salt.sh -L https://bootstrap.saltstack.com - sudo sh install_salt.sh -P -M git v2018.3.0rc1 + sudo sh install_salt.sh -P -M git v2019.2.0rc1 If you want to install only a master and not a minion using Salt Bootstrap, use the ``-M`` and ``-N`` flags: @@ -62,13 +92,13 @@ the ``-M`` and ``-N`` flags: .. code-block:: bash curl -o install_salt.sh -L https://bootstrap.saltstack.com - sudo sh install_salt.sh -P -M -N git v2018.3.0rc1 + sudo sh install_salt.sh -P -M -N git v2019.2.0rc1 Installing Using PyPI ===================== Installing from the `source archive -`_ on +`_ on `PyPI `_ is fairly straightforward. .. note:: @@ -106,4 +136,4 @@ Then install salt using the following command: .. code-block:: bash - sudo pip install salt==2018.3.0rc1 + sudo pip install salt==2019.2.0rc1 diff --git a/doc/topics/releases/version_numbers.rst b/doc/topics/releases/version_numbers.rst index dda93a4f47b2..70a2e5c04a1f 100644 --- a/doc/topics/releases/version_numbers.rst +++ b/doc/topics/releases/version_numbers.rst @@ -23,7 +23,7 @@ Code Names To distinguish future releases from the current release, code names are used. The periodic table is used to derive the next codename. The first release in the date based system was code named ``Hydrogen``, each subsequent release will -go to the next `atomic number `. +go to the next `atomic number `_. Assigned codenames: diff --git a/doc/topics/slots/index.rst b/doc/topics/slots/index.rst index 30f796ea11e6..3b71f04c1b73 100644 --- a/doc/topics/slots/index.rst +++ b/doc/topics/slots/index.rst @@ -5,6 +5,7 @@ Slots ===== .. versionadded:: 2018.3.0 +.. versionchanged:: Neon .. note:: This functionality is under development and could be changed in the future releases @@ -33,7 +34,14 @@ Slot syntax looks close to the simple python function call. __slot__:salt:.(, ..., , ...) -Also there are some specifics in the syntax coming from the execution functions +For the Neon release, this syntax has been updated to support parsing functions +which return dictionaries and for appending text to the slot result. + +.. code-block:: text + + __slot__:salt:.(..., , ...).dictionary ~ append + +There are some specifics in the syntax coming from the execution functions nature and a desire to simplify the user experience. First one is that you don't need to quote the strings passed to the slots functions. The second one is that all arguments handled as strings. @@ -51,3 +59,12 @@ This will execute the :py:func:`test.echo ` execution functions right before calling the state. The functions in the example will return `/tmp/some_file` and `/etc/hosts` strings that will be used as a target and source arguments in the state function `file.copy`. + +Here is an example of result parsing and appending: + +.. code-block:: yaml + + file-in-user-home: + file.copy: + - name: __slot__:salt:user.info(someuser).home ~ /subdirectory + - source: salt://somefile diff --git a/doc/topics/states/index.rst b/doc/topics/states/index.rst index b212925c8fed..28c1b6c92440 100644 --- a/doc/topics/states/index.rst +++ b/doc/topics/states/index.rst @@ -48,6 +48,7 @@ resources to learn more about state and renderers. rename_moe: module.run: + - name: user.rename - m_name: moe - new_name: larry - onlyif: id moe diff --git a/doc/topics/thorium/index.rst b/doc/topics/thorium/index.rst index 6b421a74d9db..4a452b90f0bc 100644 --- a/doc/topics/thorium/index.rst +++ b/doc/topics/thorium/index.rst @@ -4,18 +4,6 @@ Thorium Complex Reactor ======================= -.. note:: - - Thorium is a provisional feature of Salt and is subject to change - and removal if the feature proves to not be a viable solution. - -.. note:: - - Thorium was added to Salt as an experimental feature in the 2016.3.0 - release, as of 2016.3.0 this feature is considered experimental, no - guarantees are made for support of any kind yet. - - The original Salt Reactor is based on the idea of listening for a specific event and then reacting to it. This model comes with many logical limitations, for instance it is very difficult (and hacky) to fire a reaction based on diff --git a/doc/topics/tutorials/http.rst b/doc/topics/tutorials/http.rst index b8e46b839625..9616a7ee72f9 100644 --- a/doc/topics/tutorials/http.rst +++ b/doc/topics/tutorials/http.rst @@ -543,7 +543,7 @@ checked. This is done using the ``status`` argument: http://example.com/: http.query: - - status: '200' + - status: 200 If both are specified, both will be checked, but if only one is ``True`` and the other is ``False``, then ``False`` will be returned. In this case, the comments diff --git a/doc/topics/tutorials/jinja_to_execution_module.rst b/doc/topics/tutorials/jinja_to_execution_module.rst index 25493635926a..91500f4f82dc 100644 --- a/doc/topics/tutorials/jinja_to_execution_module.rst +++ b/doc/topics/tutorials/jinja_to_execution_module.rst @@ -6,8 +6,7 @@ How to Convert Jinja Logic to an Execution Module .. versionadded: 2016.??? -.. note: - +.. note:: This tutorial assumes a basic knowledge of Salt states and specifically experience using the `maps.jinja` idiom. diff --git a/doc/topics/windows/windows-package-manager.rst b/doc/topics/windows/windows-package-manager.rst index 0c05d438e087..fdda4c90be3d 100644 --- a/doc/topics/windows/windows-package-manager.rst +++ b/doc/topics/windows/windows-package-manager.rst @@ -631,20 +631,17 @@ into right location. Configuration options for Minions 2015.8.0 and later ==================================================== -The :conf_minion:`winrepo_source_dir` config parameter (default: -``salt://win/repo-ng/``) controls where :mod:`pkg.refresh_db -` fetches the software package definitions. -:mod:`pkg.refresh_db ` generates meta database -file called :conf_minion:`winrepo_cachefile` on the minion. +On newer minions (2015.8.0 and later), the :conf_minion:`winrepo_source_dir` +config parameter (default: ``salt://win/repo-ng``) controls where +:mod:`pkg.refresh_db ` looks for the software +definition files that will be downloaded to the minion and used to generate the +local database file (``winrepo.p``). -Cache configuration options for Minions 2016.11.0 and later -=========================================================== - -Software package definitions are automatically refresh if stale after +Software package definitions are automatically refreshed if stale after :conf_minion:`winrepo_cache_expire_max`. Running a highstate normal forces the -refresh of the package definition and generation of meta database, unless the -meta database is younger than :conf_minion:`winrepo_cache_expire_max`. -Refreshing the package definition can take some time, these options were +refresh of the package definition and generation of the meta database, unless +the meta database is younger than :conf_minion:`winrepo_cache_expire_max`. +Refreshing the package definitions can take some time, these options were introduced to allow more control of when it occurs. It's important use :py:func:`pkg.refresh_db ` @@ -655,6 +652,14 @@ your testing new definitions on. Configuration options for Minions before 2015.8.0 ================================================= +On older minions (before 2015.8.0), the :conf_minion:`winrepo_source_dir` +config parameter (default: ``salt://win/repo``) controls where +:mod:`pkg.refresh_db ` looks for the cachefile +(default: ``winrepo.p``). This means that the default location for the winrepo +cachefile would be ``salt://win/repo/winrepo.p``. Both :conf_minion: +winrepo_source_dir` and :conf_minion:`winrepo_cachefile` can be adjusted to +match the actual location of this file on the Salt fileserver. + If connected to a master, the minion will by default look for the winrepo cachefile (the file generated by the :mod:`winrepo.genrepo runner `) at ``salt://win/repo/winrepo.p``. If the @@ -662,6 +667,7 @@ cachefile is in a different path on the salt fileserver, then :conf_minion:`win_repo_cachefile` will need to be updated to reflect the proper location. + .. _2015-8-0-winrepo-changes: Changes in Version 2015.8.0 diff --git a/noxfile.py b/noxfile.py index 4cae78cf7069..3749b369995e 100644 --- a/noxfile.py +++ b/noxfile.py @@ -5,6 +5,7 @@ Nox configuration script ''' +# pylint: disable=resource-leakage # Import Python libs from __future__ import absolute_import, unicode_literals, print_function @@ -15,6 +16,7 @@ import pprint import shutil import tempfile +import datetime if __name__ == '__main__': sys.stderr.write('Do not execute this file directly. Use nox instead, it will know how to handle this file\n') @@ -34,8 +36,8 @@ # Global Path Definitions REPO_ROOT = os.path.abspath(os.path.dirname(__file__)) SITECUSTOMIZE_DIR = os.path.join(REPO_ROOT, 'tests', 'support', 'coverage') +IS_DARWIN = sys.platform.lower().startswith('darwin') IS_WINDOWS = sys.platform.lower().startswith('win') - # Python versions to run against _PYTHON_VERSIONS = ('2', '2.7', '3', '3.4', '3.5', '3.6', '3.7') @@ -45,10 +47,21 @@ # Don't fail on missing interpreters nox.options.error_on_missing_interpreters = False +# Change current directory to REPO_ROOT +os.chdir(REPO_ROOT) + +RUNTESTS_LOGFILE = os.path.join( + 'artifacts', 'logs', + 'runtests-{}.log'.format(datetime.datetime.now().strftime('%Y%m%d%H%M%S.%f')) +) + +# Prevent Python from writing bytecode +os.environ[str('PYTHONDONTWRITEBYTECODE')] = str('1') + def _create_ci_directories(): for dirname in ('logs', 'coverage', 'xml-unittests-output'): - path = os.path.join(REPO_ROOT, 'artifacts', dirname) + path = os.path.join('artifacts', dirname) if not os.path.exists(path): os.makedirs(path) @@ -138,10 +151,16 @@ def _install_system_packages(session): '/usr/lib/python{py_version}/dist-packages/*apt*' ] } - for key in ('ubuntu-14.04', 'ubuntu-16.04', 'ubuntu-18.04', 'debian-8', 'debian-9'): - system_python_packages[key] = system_python_packages['__debian_based_distros__'] distro = _get_distro_info(session) + if not distro['id'].startswith(('debian', 'ubuntu')): + # This only applies to debian based distributions + return + + system_python_packages['{id}-{version}'.format(**distro)] = \ + system_python_packages['{id}-{version_parts[major]}'.format(**distro)] = \ + system_python_packages['__debian_based_distros__'][:] + distro_keys = [ '{id}'.format(**distro), '{id}-{version}'.format(**distro), @@ -175,9 +194,9 @@ def _install_system_packages(session): shutil.copyfile(src, dst) -def _install_requirements(session, transport, *extra_requirements): +def _get_distro_pip_constraints(session, transport): # Install requirements - distro_requirements = None + distro_constraints = [] if transport == 'tcp': # The TCP requirements are the exact same requirements as the ZeroMQ ones @@ -186,51 +205,105 @@ def _install_requirements(session, transport, *extra_requirements): pydir = _get_pydir(session) if IS_WINDOWS: - _distro_requirements = os.path.join(REPO_ROOT, - 'requirements', - 'static', - pydir, - '{}-windows.txt'.format(transport)) - if os.path.exists(_distro_requirements): - distro_requirements = _distro_requirements + _distro_constraints = os.path.join('requirements', + 'static', + pydir, + '{}-windows.txt'.format(transport)) + if os.path.exists(_distro_constraints): + distro_constraints.append(_distro_constraints) + _distro_constraints = os.path.join('requirements', + 'static', + pydir, + 'windows.txt') + if os.path.exists(_distro_constraints): + distro_constraints.append(_distro_constraints) + _distro_constraints = os.path.join('requirements', + 'static', + pydir, + 'windows-crypto.txt') + if os.path.exists(_distro_constraints): + distro_constraints.append(_distro_constraints) + elif IS_DARWIN: + _distro_constraints = os.path.join('requirements', + 'static', + pydir, + '{}-darwin.txt'.format(transport)) + if os.path.exists(_distro_constraints): + distro_constraints.append(_distro_constraints) + _distro_constraints = os.path.join('requirements', + 'static', + pydir, + 'darwin.txt') + if os.path.exists(_distro_constraints): + distro_constraints.append(_distro_constraints) + _distro_constraints = os.path.join('requirements', + 'static', + pydir, + 'darwin-crypto.txt') + if os.path.exists(_distro_constraints): + distro_constraints.append(_distro_constraints) else: _install_system_packages(session) distro = _get_distro_info(session) distro_keys = [ + 'linux', '{id}'.format(**distro), '{id}-{version}'.format(**distro), '{id}-{version_parts[major]}'.format(**distro) ] for distro_key in distro_keys: - _distro_requirements = os.path.join(REPO_ROOT, - 'requirements', - 'static', - pydir, - '{}-{}.txt'.format(transport, distro_key)) - if os.path.exists(_distro_requirements): - distro_requirements = _distro_requirements - break + _distro_constraints = os.path.join('requirements', + 'static', + pydir, + '{}.txt'.format(distro_key)) + if os.path.exists(_distro_constraints): + distro_constraints.append(_distro_constraints) + _distro_constraints = os.path.join('requirements', + 'static', + pydir, + '{}-crypto.txt'.format(distro_key)) + if os.path.exists(_distro_constraints): + distro_constraints.append(_distro_constraints) + _distro_constraints = os.path.join('requirements', + 'static', + pydir, + '{}-{}.txt'.format(transport, distro_key)) + if os.path.exists(_distro_constraints): + distro_constraints.append(_distro_constraints) + distro_constraints.append(_distro_constraints) + _distro_constraints = os.path.join('requirements', + 'static', + pydir, + '{}-{}-crypto.txt'.format(transport, distro_key)) + if os.path.exists(_distro_constraints): + distro_constraints.append(_distro_constraints) + return distro_constraints - if distro_requirements is not None: - _requirements_files = [distro_requirements] - requirements_files = [] - else: - _requirements_files = [ - os.path.join(REPO_ROOT, 'requirements', 'pytest.txt') + +def _install_requirements(session, transport, *extra_requirements): + # Install requirements + distro_constraints = _get_distro_pip_constraints(session, transport) + + _requirements_files = [ + os.path.join('requirements', 'base.txt'), + os.path.join('requirements', 'zeromq.txt'), + os.path.join('requirements', 'pytest.txt') + ] + if sys.platform.startswith('linux'): + requirements_files = [ + os.path.join('requirements', 'static', 'linux.in') + ] + elif sys.platform.startswith('win'): + requirements_files = [ + os.path.join('pkg', 'windows', 'req.txt'), + os.path.join('requirements', 'static', 'windows.in') + ] + elif sys.platform.startswith('darwin'): + requirements_files = [ + os.path.join('pkg', 'osx', 'req.txt'), + os.path.join('pkg', 'osx', 'req_ext.txt'), + os.path.join('requirements', 'static', 'darwin.in') ] - if sys.platform.startswith('linux'): - requirements_files = [ - os.path.join(REPO_ROOT, 'requirements', 'tests.txt') - ] - elif sys.platform.startswith('win'): - requirements_files = [ - os.path.join(REPO_ROOT, 'pkg', 'windows', 'req.txt'), - ] - elif sys.platform.startswith('darwin'): - requirements_files = [ - os.path.join(REPO_ROOT, 'pkg', 'osx', 'req.txt'), - os.path.join(REPO_ROOT, 'pkg', 'osx', 'req_ext.txt'), - ] while True: if not requirements_files: @@ -254,10 +327,25 @@ def _install_requirements(session, transport, *extra_requirements): continue for requirements_file in _requirements_files: - session.install('--progress-bar=off', '-r', requirements_file, silent=PIP_INSTALL_SILENT) + install_command = [ + '--progress-bar=off', '-r', requirements_file + ] + for distro_constraint in distro_constraints: + install_command.extend([ + '--constraint', distro_constraint + ]) + session.install(*install_command, silent=PIP_INSTALL_SILENT) if extra_requirements: - session.install('--progress-bar=off', *extra_requirements, silent=PIP_INSTALL_SILENT) + install_command = [ + '--progress-bar=off', + ] + for distro_constraint in distro_constraints: + install_command.extend([ + '--constraint', distro_constraint + ]) + install_command += list(extra_requirements) + session.install(*install_command, silent=PIP_INSTALL_SILENT) def _run_with_coverage(session, *test_cmd): @@ -272,19 +360,23 @@ def _run_with_coverage(session, *test_cmd): python_path_entries.remove(SITECUSTOMIZE_DIR) python_path_entries.insert(0, SITECUSTOMIZE_DIR) python_path_env_var = os.pathsep.join(python_path_entries) + + env = { + # The updated python path so that sitecustomize is importable + 'PYTHONPATH': python_path_env_var, + # The full path to the .coverage data file. Makes sure we always write + # them to the same directory + 'COVERAGE_FILE': os.path.abspath(os.path.join(REPO_ROOT, '.coverage')), + # Instruct sub processes to also run under coverage + 'COVERAGE_PROCESS_START': os.path.join(REPO_ROOT, '.coveragerc') + } + if IS_DARWIN: + # Don't nuke our multiprocessing efforts objc! + # https://stackoverflow.com/questions/50168647/multiprocessing-causes-python-to-crash-and-gives-an-error-may-have-been-in-progr + env['OBJC_DISABLE_INITIALIZE_FORK_SAFETY'] = 'YES' + try: - session.run( - *test_cmd, - env={ - # The updated python path so that sitecustomize is importable - 'PYTHONPATH': python_path_env_var, - # The full path to the .coverage data file. Makes sure we always write - # them to the same directory - 'COVERAGE_FILE': os.path.abspath(os.path.join(REPO_ROOT, '.coverage')), - # Instruct sub processes to also run under coverage - 'COVERAGE_PROCESS_START': os.path.join(REPO_ROOT, '.coveragerc') - } - ) + session.run(*test_cmd, env=env) finally: # Always combine and generate the XML coverage report try: @@ -293,7 +385,20 @@ def _run_with_coverage(session, *test_cmd): # Sometimes some of the coverage files are corrupt which would trigger a CommandFailed # exception pass - session.run('coverage', 'xml', '-o', os.path.join(REPO_ROOT, 'artifacts', 'coverage', 'coverage.xml')) + # Generate report for salt code coverage + session.run( + 'coverage', 'xml', + '-o', os.path.join('artifacts', 'coverage', 'salt.xml'), + '--omit=tests/*', + '--include=salt/*' + ) + # Generate report for tests code coverage + session.run( + 'coverage', 'xml', + '-o', os.path.join('artifacts', 'coverage', 'tests.xml'), + '--omit=salt/*', + '--include=tests/*' + ) def _runtests(session, coverage, cmd_args): @@ -303,7 +408,13 @@ def _runtests(session, coverage, cmd_args): if coverage is True: _run_with_coverage(session, 'coverage', 'run', os.path.join('tests', 'runtests.py'), *cmd_args) else: - session.run('python', os.path.join('tests', 'runtests.py'), *cmd_args) + cmd_args = ['python', os.path.join('tests', 'runtests.py')] + list(cmd_args) + env = None + if IS_DARWIN: + # Don't nuke our multiprocessing efforts objc! + # https://stackoverflow.com/questions/50168647/multiprocessing-causes-python-to-crash-and-gives-an-error-may-have-been-in-progr + env = {'OBJC_DISABLE_INITIALIZE_FORK_SAFETY': 'YES'} + session.run(*cmd_args, env=env) except CommandFailed: # Disabling re-running failed tests for the time being raise @@ -371,12 +482,19 @@ def runtests_parametrized(session, coverage, transport, crypto): session.run('pip', 'uninstall', '-y', 'pycrypto', 'pycryptodome', 'pycryptodomex', silent=True) else: session.run('pip', 'uninstall', '-y', 'm2crypto', silent=True) - session.install('--progress-bar=off', crypto, silent=PIP_INSTALL_SILENT) + distro_constraints = _get_distro_pip_constraints(session, transport) + install_command = [ + '--progress-bar=off', + ] + for distro_constraint in distro_constraints: + install_command.extend([ + '--constraint', distro_constraint + ]) + install_command.append(crypto) + session.install(*install_command, silent=PIP_INSTALL_SILENT) cmd_args = [ - '--tests-logfile={}'.format( - os.path.join(REPO_ROOT, 'artifacts', 'logs', 'runtests.log') - ), + '--tests-logfile={}'.format(RUNTESTS_LOGFILE), '--transport={}'.format(transport) ] + session.posargs _runtests(session, coverage, cmd_args) @@ -515,14 +633,12 @@ def runtests_cloud(session, coverage): _install_requirements(session, 'zeromq', 'unittest-xml-reporting==2.2.1') pydir = _get_pydir(session) - cloud_requirements = os.path.join(REPO_ROOT, 'requirements', 'static', pydir, 'cloud.txt') + cloud_requirements = os.path.join('requirements', 'static', pydir, 'cloud.txt') session.install('--progress-bar=off', '-r', cloud_requirements, silent=PIP_INSTALL_SILENT) cmd_args = [ - '--tests-logfile={}'.format( - os.path.join(REPO_ROOT, 'artifacts', 'logs', 'runtests.log') - ), + '--tests-logfile={}'.format(RUNTESTS_LOGFILE), '--cloud-provider-tests' ] + session.posargs _runtests(session, coverage, cmd_args) @@ -537,9 +653,7 @@ def runtests_tornado(session, coverage): session.install('--progress-bar=off', 'pyzmq==17.0.0', silent=PIP_INSTALL_SILENT) cmd_args = [ - '--tests-logfile={}'.format( - os.path.join(REPO_ROOT, 'artifacts', 'logs', 'runtests.log') - ), + '--tests-logfile={}'.format(RUNTESTS_LOGFILE) ] + session.posargs _runtests(session, coverage, cmd_args) @@ -557,13 +671,21 @@ def pytest_parametrized(session, coverage, transport, crypto): session.run('pip', 'uninstall', '-y', 'pycrypto', 'pycryptodome', 'pycryptodomex', silent=True) else: session.run('pip', 'uninstall', '-y', 'm2crypto', silent=True) - session.install('--progress-bar=off', crypto, silent=PIP_INSTALL_SILENT) + distro_constraints = _get_distro_pip_constraints(session, transport) + install_command = [ + '--progress-bar=off', + ] + for distro_constraint in distro_constraints: + install_command.extend([ + '--constraint', distro_constraint + ]) + install_command.append(crypto) + session.install(*install_command, silent=PIP_INSTALL_SILENT) cmd_args = [ '--rootdir', REPO_ROOT, - '--log-file={}'.format( - os.path.join(REPO_ROOT, 'artifacts', 'logs', 'runtests.log') - ), + '--log-file={}'.format(RUNTESTS_LOGFILE), + '--log-file-level=debug', '--no-print-logs', '-ra', '-s', @@ -704,19 +826,18 @@ def pytest_cloud(session, coverage): # Install requirements _install_requirements(session, 'zeromq') pydir = _get_pydir(session) - cloud_requirements = os.path.join(REPO_ROOT, 'requirements', 'static', pydir, 'cloud.txt') + cloud_requirements = os.path.join('requirements', 'static', pydir, 'cloud.txt') session.install('--progress-bar=off', '-r', cloud_requirements, silent=PIP_INSTALL_SILENT) cmd_args = [ '--rootdir', REPO_ROOT, - '--log-file={}'.format( - os.path.join(REPO_ROOT, 'artifacts', 'logs', 'runtests.log') - ), + '--log-file={}'.format(RUNTESTS_LOGFILE), + '--log-file-level=debug', '--no-print-logs', '-ra', '-s', - os.path.join(REPO_ROOT, 'tests', 'integration', 'cloud', 'providers') + os.path.join('tests', 'integration', 'cloud', 'providers') ] + session.posargs _pytest(session, coverage, cmd_args) @@ -731,9 +852,8 @@ def pytest_tornado(session, coverage): cmd_args = [ '--rootdir', REPO_ROOT, - '--log-file={}'.format( - os.path.join(REPO_ROOT, 'artifacts', 'logs', 'runtests.log') - ), + '--log-file={}'.format(RUNTESTS_LOGFILE), + '--log-file-level=debug', '--no-print-logs', '-ra', '-s', @@ -745,24 +865,50 @@ def _pytest(session, coverage, cmd_args): # Create required artifacts directories _create_ci_directories() + env = None + if IS_DARWIN: + # Don't nuke our multiprocessing efforts objc! + # https://stackoverflow.com/questions/50168647/multiprocessing-causes-python-to-crash-and-gives-an-error-may-have-been-in-progr + env = {'OBJC_DISABLE_INITIALIZE_FORK_SAFETY': 'YES'} + try: if coverage is True: _run_with_coverage(session, 'coverage', 'run', '-m', 'py.test', *cmd_args) else: - session.run('py.test', *cmd_args) + session.run('py.test', *cmd_args, env=env) except CommandFailed: + # Not rerunning failed tests for now + raise + + # pylint: disable=unreachable # Re-run failed tests session.log('Re-running failed tests') + + for idx, parg in enumerate(cmd_args): + if parg.startswith('--junitxml='): + cmd_args[idx] = parg.replace('.xml', '-rerun-failed.xml') cmd_args.append('--lf') if coverage is True: _run_with_coverage(session, 'coverage', 'run', '-m', 'py.test', *cmd_args) else: - session.run('py.test', *cmd_args) + session.run('py.test', *cmd_args, env=env) + # pylint: enable=unreachable def _lint(session, rcfile, flags, paths): _install_requirements(session, 'zeromq') - session.install('--progress-bar=off', '-r', 'requirements/static/{}/lint.txt'.format(_get_pydir(session)), silent=PIP_INSTALL_SILENT) + requirements_file = 'requirements/static/lint.in' + distro_constraints = [ + 'requirements/static/{}/lint.txt'.format(_get_pydir(session)) + ] + install_command = [ + '--progress-bar=off', '-r', requirements_file + ] + for distro_constraint in distro_constraints: + install_command.extend([ + '--constraint', distro_constraint + ]) + session.install(*install_command, silent=PIP_INSTALL_SILENT) session.run('pylint', '--version') pylint_report_path = os.environ.get('PYLINT_REPORT') @@ -816,7 +962,7 @@ def lint_salt(session): if session.posargs: paths = session.posargs else: - paths = ['setup.py', 'salt/'] + paths = ['setup.py', 'noxfile.py', 'salt/'] _lint(session, '.testing.pylintrc', flags, paths) @@ -836,19 +982,73 @@ def lint_tests(session): @nox.session(python='3') -def docs(session): +@nox.parametrize('update', [False, True]) +@nox.parametrize('compress', [False, True]) +def docs(session, compress, update): ''' Build Salt's Documentation ''' + session.notify('docs-html(compress={})'.format(compress)) + session.notify('docs-man(compress={}, update={})'.format(compress, update)) + + +@nox.session(name='docs-html', python='3') +@nox.parametrize('compress', [False, True]) +def docs_html(session, compress): + ''' + Build Salt's HTML Documentation + ''' pydir = _get_pydir(session) if pydir == 'py3.4': session.error('Sphinx only runs on Python >= 3.5') - session.install( - '--progress-bar=off', - '-r', 'requirements/static/{}/docs.txt'.format(pydir), - silent=PIP_INSTALL_SILENT) + requirements_file = 'requirements/static/docs.in' + distro_constraints = [ + 'requirements/static/{}/docs.txt'.format(_get_pydir(session)) + ] + install_command = [ + '--progress-bar=off', '-r', requirements_file + ] + for distro_constraint in distro_constraints: + install_command.extend([ + '--constraint', distro_constraint + ]) + session.install(*install_command, silent=PIP_INSTALL_SILENT) os.chdir('doc/') session.run('make', 'clean', external=True) session.run('make', 'html', 'SPHINXOPTS=-W', external=True) - session.run('tar', '-czvf', 'doc-archive.tar.gz', '_build/html') + if compress: + session.run('tar', '-cJvf', 'html-archive.tar.xz', '_build/html', external=True) + os.chdir('..') + + +@nox.session(name='docs-man', python='3') +@nox.parametrize('update', [False, True]) +@nox.parametrize('compress', [False, True]) +def docs_man(session, compress, update): + ''' + Build Salt's Manpages Documentation + ''' + pydir = _get_pydir(session) + if pydir == 'py3.4': + session.error('Sphinx only runs on Python >= 3.5') + requirements_file = 'requirements/static/docs.in' + distro_constraints = [ + 'requirements/static/{}/docs.txt'.format(_get_pydir(session)) + ] + install_command = [ + '--progress-bar=off', '-r', requirements_file + ] + for distro_constraint in distro_constraints: + install_command.extend([ + '--constraint', distro_constraint + ]) + session.install(*install_command, silent=PIP_INSTALL_SILENT) + os.chdir('doc/') + session.run('make', 'clean', external=True) + session.run('make', 'man', 'SPHINXOPTS=-W', external=True) + if update: + session.run('rm', '-rf', 'man/', external=True) + session.run('cp', '-Rp', '_build/man', 'man/', external=True) + if compress: + session.run('tar', '-cJvf', 'man-archive.tar.xz', '_build/man', external=True) os.chdir('..') diff --git a/pkg/osx/req.txt b/pkg/osx/req.txt index 926601a6b142..7868a68c7495 100644 --- a/pkg/osx/req.txt +++ b/pkg/osx/req.txt @@ -24,7 +24,7 @@ pyobjc==5.1.2 pyopenssl python-dateutil==2.8.0 python-gnupg==0.4.4 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 requests==2.21.0 setproctitle diff --git a/pkg/windows/req.txt b/pkg/windows/req.txt index ef87695d6e37..b226b8011065 100644 --- a/pkg/windows/req.txt +++ b/pkg/windows/req.txt @@ -28,7 +28,7 @@ pyopenssl==19.0.0 python-dateutil==2.8.0 python-gnupg==0.4.4 pythonnet==2.3.0 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 requests==2.21.0 setproctitle diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 000000000000..7a8b91b2ab3c --- /dev/null +++ b/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +log_print=false +log_date_format=%H:%M:%S +log_cli_format=%(asctime)s,%(msecs)03.0f [%(name)-5s:%(lineno)-4d][%(levelname)-8s][%(processName)s(%(process)s)] %(message)s +log_file_format=%(asctime)s,%(msecs)03d [%(name)-17s:%(lineno)-4d][%(levelname)-8s][%(processName)s(%(process)d)] %(message)s diff --git a/requirements/base.txt b/requirements/base.txt index b313eb7afe87..ec8dc3b33a28 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -2,7 +2,7 @@ Jinja2 # This should be changed to msgpack-python for Packages # msgpack-python>0.3,!=0.5.5 msgpack>=0.5,!=0.5.5 -PyYAML<5.1 +PyYAML MarkupSafe requests>=1.0.0 tornado>=4.2.1,<6.0; python_version < '3' diff --git a/requirements/crypto.txt b/requirements/crypto.txt new file mode 100644 index 000000000000..c98b7f77656d --- /dev/null +++ b/requirements/crypto.txt @@ -0,0 +1,2 @@ +pycryptodome; sys.platform != 'win32' +pycryptodomex; sys.platform == 'win32' diff --git a/requirements/dev.txt b/requirements/dev.txt deleted file mode 100644 index cd666765c53d..000000000000 --- a/requirements/dev.txt +++ /dev/null @@ -1,12 +0,0 @@ --r base.txt - -mock>=2.0.0 -SaltPyLint>=v2017.3.6 -testinfra>=1.7.0,!=1.17.0 - -# httpretty Needs to be here for now even though it's a dependency of boto. -# A pip install on a fresh system will decide to target httpretty 0.8.10 to -# 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 diff --git a/requirements/dev_python27.txt b/requirements/dev_python27.txt deleted file mode 100644 index d602a71ed3ef..000000000000 --- a/requirements/dev_python27.txt +++ /dev/null @@ -1,2 +0,0 @@ -# This is a legacy file, use dev.txt --r dev.txt diff --git a/requirements/dev_python34.txt b/requirements/dev_python34.txt deleted file mode 100644 index d602a71ed3ef..000000000000 --- a/requirements/dev_python34.txt +++ /dev/null @@ -1,2 +0,0 @@ -# This is a legacy file, use dev.txt --r dev.txt diff --git a/requirements/opt.txt b/requirements/opt.txt deleted file mode 100644 index 057ace1437a1..000000000000 --- a/requirements/opt.txt +++ /dev/null @@ -1,9 +0,0 @@ -mysql-python -timelib -yappi>=0.8.2 ---allow-unverified python-novaclient>2.17.0 ---allow-unverified python-neutronclient>2.3.6 -python-gnupg -cherrypy>=3.2.2,<18.0.0; python_version < '3.5' -cherrypy>=3.2.2; python_version >= '3.5' -libnacl diff --git a/requirements/pytest.txt b/requirements/pytest.txt index 1159d1112959..7c2679989c25 100644 --- a/requirements/pytest.txt +++ b/requirements/pytest.txt @@ -1,8 +1,6 @@ # PyTest -pytest >= 4.0.1 -pytest-cov -pytest-salt == 2018.12.8 -pytest-timeout >= 1.3.3 -pytest-tempdir >= 2018.8.11 -pytest-helpers-namespace >= 2017.11.11 -pytest-salt-runtests-bridge >= 2019.1.30 +pytest >=4.6.6,<4.7 # PyTest 4.6.x are the last Py2 and Py3 releases +pytest-salt >= 2019.12.18 +pytest-tempdir >= 2019.10.12 +pytest-helpers-namespace >= 2019.1.8 +pytest-salt-runtests-bridge >= 2019.7.10 diff --git a/requirements/static/amzn-2.in b/requirements/static/amzn-2.in deleted file mode 100644 index cb87b7dadf0e..000000000000 --- a/requirements/static/amzn-2.in +++ /dev/null @@ -1,42 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -croniter>=0.3.0,!=0.3.22 -dnspython -docker -futures>=2.0; python_version < '3.0' -GitPython -jsonschema<=2.6.0 -junos-eznc -jxmlease -kazoo -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -paramiko==2.1.2; python_version < '3.7' -paramiko>=2.2.3; python_version >= '3.7' -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pyinotify -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -requests -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -watchdog diff --git a/requirements/static/amzn-2018.03.in b/requirements/static/amzn-2018.03.in deleted file mode 100644 index 3297e92acbe6..000000000000 --- a/requirements/static/amzn-2018.03.in +++ /dev/null @@ -1,39 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -croniter>=0.3.0,!=0.3.22 -dnspython -docker -futures>=2.0; python_version < '3.0' -GitPython<2.0.9 -jsonschema<=2.6.0 -junos-eznc -jxmlease -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pyinotify -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -requests -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -watchdog diff --git a/requirements/static/arch.in b/requirements/static/arch.in deleted file mode 100644 index 079320e387c6..000000000000 --- a/requirements/static/arch.in +++ /dev/null @@ -1,38 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -croniter>=0.3.0,!=0.3.22 -dnspython -docker -futures>=2.0; python_version < '3.0' -GitPython -jsonschema<=2.6.0 -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pyinotify -pygit2 -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -requests -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -watchdog diff --git a/requirements/static/centos-6.in b/requirements/static/centos-6.in deleted file mode 100644 index 09d172bc0182..000000000000 --- a/requirements/static/centos-6.in +++ /dev/null @@ -1,40 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -croniter>=0.3.0,!=0.3.22 -dnspython -docker -futures>=2.0; python_version < '3.0' -GitPython<2.0.9 -jsonschema<=2.6.0 -junos-eznc -jxmlease -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pyinotify -pygit2 -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -requests -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -watchdog diff --git a/requirements/static/centos-7.in b/requirements/static/centos-7.in deleted file mode 100644 index ca38a327d434..000000000000 --- a/requirements/static/centos-7.in +++ /dev/null @@ -1,43 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -croniter>=0.3.0,!=0.3.22 -dnspython -docker -futures>=2.0; python_version < '3.0' -GitPython -jsonschema<=2.6.0 -junos-eznc -jxmlease -kazoo -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -paramiko==2.1.2; python_version < '3.7' -paramiko>=2.2.3; python_version >= '3.7' -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pyinotify -pygit2 -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -requests -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -watchdog diff --git a/requirements/static/crypto.in b/requirements/static/crypto.in new file mode 100644 index 000000000000..cbd603104b61 --- /dev/null +++ b/requirements/static/crypto.in @@ -0,0 +1,2 @@ +m2crypto +pycryptodomex diff --git a/requirements/static/osx.in b/requirements/static/darwin.in similarity index 58% rename from requirements/static/osx.in rename to requirements/static/darwin.in index 1f191ee32c3b..d3f777e8c6c8 100644 --- a/requirements/static/osx.in +++ b/requirements/static/darwin.in @@ -6,15 +6,17 @@ croniter>=0.3.0,!=0.3.22 dnspython docker futures>=2.0; python_version < '3.0' -jsonschema<=2.6.0 +jsonschema junos-eznc jxmlease keyring==5.7.1 kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' +mock>=3.0.5; python_version < '3.6' more-itertools==5.0.0 moto -pylxd>=2.2.5 +# XXX: Temporarily do not install pylxd. +# pylxd(or likely ws4py) will cause the test suite to hang at the finish line under runtests.py +# pylxd>=2.2.5 pyopenssl python-etcd>0.4.2 pyvmomi @@ -25,3 +27,9 @@ supervisor==3.3.5; python_version < '3' virtualenv watchdog yamlordereddictloader + +# Available template libraries that can be used +Genshi +Cheetah3==3.1.0 +Mako +wempy; python_version <'3' diff --git a/requirements/static/debian-10.in b/requirements/static/debian-10.in deleted file mode 100644 index 4ee2f3c697b3..000000000000 --- a/requirements/static/debian-10.in +++ /dev/null @@ -1,44 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -ansible; python_version < '3.0' -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -clustershell -croniter>=0.3.0,!=0.3.22 -dnspython -docker<4.0 -futures>=2.0; python_version < '3.0' -GitPython -jsonschema<=2.6.0 -junos-eznc -jxmlease -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -paramiko==2.2.3 -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pygit2 -pyinotify -pylxd>=2.2.5 -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -requests -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -yamlordereddictloader diff --git a/requirements/static/debian-8.in b/requirements/static/debian-8.in deleted file mode 100644 index f6b488d9417f..000000000000 --- a/requirements/static/debian-8.in +++ /dev/null @@ -1,41 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -croniter>=0.3.0,!=0.3.22 -dnspython -docker -futures>=2.0; python_version < '3.0' -GitPython -jsonschema<=2.6.0 -junos-eznc -jxmlease -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -paramiko==2.1.2; python_version < '3.7' -paramiko>=2.2.3; python_version >= '3.7' -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pyinotify -pygit2 -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -watchdog diff --git a/requirements/static/debian-9.in b/requirements/static/debian-9.in deleted file mode 100644 index bce0ebf4793d..000000000000 --- a/requirements/static/debian-9.in +++ /dev/null @@ -1,42 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -croniter>=0.3.0,!=0.3.22 -dnspython -docker -futures>=2.0; python_version < '3.0' -GitPython -jsonschema<=2.6.0 -junos-eznc -jxmlease -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -paramiko==2.1.2; python_version < '3.7' -paramiko>=2.2.3; python_version >= '3.7' -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pyinotify -pygit2 -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -requests -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -watchdog diff --git a/requirements/static/fedora-29.in b/requirements/static/fedora-29.in deleted file mode 100644 index 995781f04aec..000000000000 --- a/requirements/static/fedora-29.in +++ /dev/null @@ -1,40 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -croniter>=0.3.0,!=0.3.22 -dnspython -docker -futures>=2.0; python_version < '3.0' -GitPython -jsonschema<=2.6.0 -junos-eznc -jxmlease -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pyinotify -pygit2 -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -requests -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -watchdog diff --git a/requirements/static/fedora-30.in b/requirements/static/fedora-30.in deleted file mode 100644 index 995781f04aec..000000000000 --- a/requirements/static/fedora-30.in +++ /dev/null @@ -1,40 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -croniter>=0.3.0,!=0.3.22 -dnspython -docker -futures>=2.0; python_version < '3.0' -GitPython -jsonschema<=2.6.0 -junos-eznc -jxmlease -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pyinotify -pygit2 -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -requests -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -watchdog diff --git a/requirements/static/lint.in b/requirements/static/lint.in index a3e9e972e1ba..988a71dd494a 100644 --- a/requirements/static/lint.in +++ b/requirements/static/lint.in @@ -1,3 +1,3 @@ # Lint requirements pylint==1.6.5 -SaltPyLint>=v2017.3.6 +SaltPyLint>=v2019.11.14 diff --git a/requirements/static/opensuse-leap-15.in b/requirements/static/linux.in similarity index 65% rename from requirements/static/opensuse-leap-15.in rename to requirements/static/linux.in index d4edf89b8508..f72ea4dccab1 100644 --- a/requirements/static/opensuse-leap-15.in +++ b/requirements/static/linux.in @@ -1,4 +1,3 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run apache-libcloud==2.0.0 boto3 boto>=2.46.0 @@ -11,19 +10,22 @@ docker futures>=2.0; python_version < '3.0' GitPython hgtools -jsonschema<=2.6.0 +jsonschema +junos-eznc +jxmlease +kazoo keyring==5.7.1 kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' +mock>=3.0.5; python_version < '3.6' more-itertools==5.0.0 moto -msgpack-python >= 0.4.2, != 0.5.5 +paramiko>=2.1.6 psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues +# Let's install pycryptodome instead of pycrypto because of pycrypto's outstanding security issues # PyCrypto, if pulled, will be removed from the generated static requirements pycryptodome -pyinotify pygit2 +pyinotify pyopenssl python-etcd>0.4.2 python-gnupg @@ -39,3 +41,9 @@ timelib tornado<5.0 virtualenv watchdog + +# Available template libraries that can be used +Genshi +Cheetah3==3.1.0 +Mako +wempy; python_version <'3' diff --git a/requirements/static/py2.7/cloud.txt b/requirements/static/py2.7/cloud.txt index 2a41a73b9afb..988fd29f2ee7 100644 --- a/requirements/static/py2.7/cloud.txt +++ b/requirements/static/py2.7/cloud.txt @@ -131,5 +131,5 @@ six==1.12.0 # via cryptography, impacket, isodate, pathlib2, profi smbprotocol==0.1.1 # via pypsexec typing==3.6.6 # via msrest urllib3==1.24.2 # via requests -werkzeug==0.15.2 # via flask +werkzeug==0.15.6 # via flask xmltodict==0.12.0 # via pywinrm diff --git a/requirements/static/py2.7/darwin-crypto.txt b/requirements/static/py2.7/darwin-crypto.txt new file mode 100644 index 000000000000..28c30874aa4c --- /dev/null +++ b/requirements/static/py2.7/darwin-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py2.7/darwin-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.0 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py2.7/zeromq-osx.txt b/requirements/static/py2.7/darwin.txt similarity index 75% rename from requirements/static/py2.7/zeromq-osx.txt rename to requirements/static/py2.7/darwin.txt index 85e739a768ee..0498bb670021 100644 --- a/requirements/static/py2.7/zeromq-osx.txt +++ b/requirements/static/py2.7/darwin.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py2.7/zeromq-osx.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/osx.in +# pip-compile -o requirements/static/py2.7/darwin.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/darwin.in # apache-libcloud==2.4.0 argh==0.26.2 # via watchdog @@ -27,16 +27,16 @@ cheroot==6.5.5 # via cherrypy cherrypy==17.4.1 click==7.0 clustershell==1.8.1 -contextlib2==0.5.5 # via cherrypy +configparser==4.0.2 # via importlib-metadata +contextlib2==0.5.5 # via cherrypy, importlib-metadata cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov croniter==0.3.29 cryptography==2.6.1 dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==3.7.2 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose enum34==1.1.6 funcsigs==1.0.2 # via mock, pytest functools32==3.2.3.post2 # via jsonschema @@ -47,6 +47,7 @@ gitdb==0.6.4 gitpython==2.1.11 google-auth==1.6.3 # via kubernetes idna==2.8 +importlib-metadata==0.23 # via pluggy, pytest ipaddress==1.0.22 jaraco.functools==2.0 # via tempora jinja2==2.10.1 @@ -67,14 +68,14 @@ mock==3.0.5 ; python_version < "3.6" more-itertools==5.0.0 moto==1.3.7 msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc +packaging==19.2 # via pytest paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathlib2==2.3.3 # via pytest +pathlib2==2.3.3 # via importlib-metadata, pytest pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock, pylxd -pluggy==0.9.0 # via pytest +pluggy==0.13.1 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -82,30 +83,24 @@ pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth pyasn1==0.4.5 pycparser==2.19 -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pylxd==2.2.9 +pycryptodome==3.8.1 ; sys_platform != "win32" pynacl==1.3.0 # via paramiko pyopenssl==19.0.0 +pyparsing==2.4.5 # via packaging pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 python-etcd==0.4.5 python-gnupg==0.4.4 python-jose==2.0.2 # via moto pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" -requests-toolbelt==0.9.1 # via pylxd -requests-unixsocket==0.1.5 # via pylxd requests==2.21.0 responses==0.10.6 # via moto rfc3987==1.3.8 @@ -117,7 +112,7 @@ scp==0.13.2 # via junos-eznc selectors2==2.0.1 # via ncclient setproctitle==1.1.10 singledispatch==3.4.0.3 -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pylxd, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, packaging, pathlib2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client smmap2==2.0.5 # via gitdb2 smmap==0.9.0 strict-rfc3339==0.7 @@ -125,16 +120,19 @@ supervisor==3.3.5 ; python_version < "3" tempora==1.14.1 # via portend timelib==0.2.4 tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests, requests-unixsocket +urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 vultr==1.0.1 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wrapt==1.11.1 # via aws-xray-sdk -ws4py==0.5.1 # via pylxd xmltodict==0.12.0 # via moto yamlordereddictloader==0.4.0 zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata +# Passthrough dependencies from pkg/osx/req.txt +pyobjc==5.1.2 # Passthrough dependencies from pkg/osx/req.txt pyobjc==5.1.2 diff --git a/requirements/static/py2.7/lint.txt b/requirements/static/py2.7/lint.txt index 078a544add4a..b831d2cb0803 100644 --- a/requirements/static/py2.7/lint.txt +++ b/requirements/static/py2.7/lint.txt @@ -6,7 +6,7 @@ # astroid==1.4.9 # via pylint backports.functools-lru-cache==1.5 # via isort, pylint -configparser==3.7.4 # via pylint +configparser==4.0.2 # via pylint futures==3.2.0 # via isort isort==4.3.17 # via pylint lazy-object-proxy==1.3.1 # via astroid @@ -14,6 +14,6 @@ mccabe==0.6.1 # via pylint modernize==0.5 # via saltpylint pycodestyle==2.5.0 # via saltpylint pylint==1.6.5 -saltpylint==2019.1.11 +saltpylint==2019.11.14 six==1.12.0 # via astroid, pylint wrapt==1.11.1 # via astroid diff --git a/requirements/static/py2.7/linux-crypto.txt b/requirements/static/py2.7/linux-crypto.txt new file mode 100644 index 000000000000..5f46f1c9e774 --- /dev/null +++ b/requirements/static/py2.7/linux-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py2.7/linux-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.0 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py2.7/zeromq-fedora-29.txt b/requirements/static/py2.7/linux.txt similarity index 75% rename from requirements/static/py2.7/zeromq-fedora-29.txt rename to requirements/static/py2.7/linux.txt index de9fb20ec82a..f09e17e33ea1 100644 --- a/requirements/static/py2.7/zeromq-fedora-29.txt +++ b/requirements/static/py2.7/linux.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py2.7/zeromq-fedora-29.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/fedora-29.in +# pip-compile -o requirements/static/py2.7/linux.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/linux.in # apache-libcloud==2.0.0 argh==0.26.2 # via watchdog @@ -20,21 +20,21 @@ boto3==1.9.132 boto==2.49.0 botocore==1.12.132 # via boto3, moto, s3transfer cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado +certifi==2019.3.9 cffi==1.12.2 chardet==3.0.4 # via requests cheroot==6.5.4 # via cherrypy cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy +configparser==4.0.2 # via importlib-metadata +contextlib2==0.5.5 # via cherrypy, importlib-metadata cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov croniter==0.3.29 cryptography==2.6.1 # via moto, paramiko, pyopenssl dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==3.7.2 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose enum34==1.1.6 # via cryptography funcsigs==1.0.2 # via mock, pytest functools32==3.2.3.post2 # via jsonschema @@ -43,7 +43,9 @@ futures==3.2.0 ; python_version < "3.0" gitdb2==2.0.5 # via gitpython gitpython==2.1.11 google-auth==1.6.3 # via kubernetes +hgtools==8.1.1 idna==2.8 # via requests +importlib-metadata==0.23 # via pluggy, pytest ipaddress==1.0.22 # via cryptography, docker, kubernetes jaraco.functools==2.0 # via tempora jinja2==2.10.1 @@ -53,6 +55,7 @@ jsonpickle==1.1 # via aws-xray-sdk jsonschema==2.6.0 junos-eznc==2.2.0 jxmlease==1.0.1 +kazoo==2.6.1 keyring==5.7.1 kubernetes==3.0.0 lxml==4.3.3 # via junos-eznc, ncclient @@ -61,15 +64,14 @@ meld3==1.0.2 # via supervisor mock==3.0.5 ; python_version < "3.6" more-itertools==5.0.0 moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathlib2==2.3.3 # via pytest +packaging==19.2 # via pytest +paramiko==2.4.2 +pathlib2==2.3.3 # via importlib-metadata, pytest pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest +pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -77,28 +79,25 @@ pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 +pycryptodome==3.8.1 ; sys_platform != "win32" pygit2==0.28.2 pyinotify==0.9.6 pynacl==1.3.0 # via paramiko pyopenssl==19.0.0 +pyparsing==2.4.5 # via packaging pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto python-etcd==0.4.5 python-gnupg==0.4.4 python-jose==2.0.2 # via moto pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" requests==2.21.0 responses==0.10.6 # via moto @@ -110,8 +109,9 @@ scandir==1.10.0 # via pathlib2 scp==0.13.2 # via junos-eznc selectors2==2.0.1 # via ncclient setproctitle==1.1.10 +setuptools-scm==3.2.0 singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, packaging, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client smmap2==2.0.5 # via gitdb2 strict-rfc3339==0.7 supervisor==3.3.5 ; python_version < "3" @@ -121,8 +121,10 @@ tornado==4.5.3 ; python_version < "3" urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wrapt==1.11.1 # via aws-xray-sdk xmltodict==0.12.0 # via moto zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py2.7/windows-crypto.txt b/requirements/static/py2.7/windows-crypto.txt new file mode 100644 index 000000000000..4fc8e9899d10 --- /dev/null +++ b/requirements/static/py2.7/windows-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py2.7/windows-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.0 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py2.7/zeromq-windows.txt b/requirements/static/py2.7/windows.txt similarity index 76% rename from requirements/static/py2.7/zeromq-windows.txt rename to requirements/static/py2.7/windows.txt index 514834743a75..0fe8f9e243c2 100644 --- a/requirements/static/py2.7/zeromq-windows.txt +++ b/requirements/static/py2.7/windows.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py2.7/zeromq-windows.txt -v pkg/windows/req.txt pkg/windows/req_win.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/windows.in +# pip-compile -o requirements/static/py2.7/windows.txt -v pkg/windows/req.txt pkg/windows/req_win.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/windows.in # argh==0.26.2 # via watchdog asn1crypto==0.24.0 # via cryptography @@ -24,16 +24,16 @@ chardet==3.0.4 # via requests cheroot==6.5.5 # via cherrypy cherrypy==17.4.1 colorama==0.4.1 # via pytest -contextlib2==0.5.5 # via cherrypy +configparser==4.0.2 # via importlib-metadata +contextlib2==0.5.5 # via cherrypy, importlib-metadata cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov cryptography==2.6.1 dmidecode==0.9.0 dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==2.7.0 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose enum34==1.1.6 funcsigs==1.0.2 # via mock, pytest functools32==3.2.3.post2 # via jsonschema @@ -44,6 +44,7 @@ gitdb==0.6.4 gitpython==2.1.10 google-auth==1.6.3 # via kubernetes idna==2.8 +importlib-metadata==0.23 # via pluggy, pytest ioloop==0.1a0 ipaddress==1.0.22 jaraco.functools==2.0 # via tempora @@ -63,12 +64,12 @@ mock==3.0.5 ; python_version < "3.6" more-itertools==5.0.0 moto==1.3.7 msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 +packaging==19.2 # via pytest patch==1.16 -pathlib2==2.3.3 # via pytest +pathlib2==2.3.3 # via importlib-metadata, pytest pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest +pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -82,13 +83,12 @@ pycurl==7.43.0.2 pygit2==0.28.2 pymysql==0.9.3 pyopenssl==19.0.0 -pytest-cov==2.6.1 +pyparsing==2.4.5 # via packaging pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 python-etcd==0.4.5 python-gnupg==0.4.4 @@ -97,7 +97,7 @@ pythonnet==2.3.0 pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 pywin32==224 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" requests==2.21.0 responses==0.10.6 # via moto @@ -109,7 +109,7 @@ scandir==1.10.0 # via pathlib2 sed==0.3.1 setproctitle==1.1.10 singledispatch==3.4.0.3 -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, packaging, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client smmap2==2.0.5 # via gitdb2 smmap==0.9.0 strict-rfc3339==0.7 @@ -120,10 +120,12 @@ tornado==4.5.3 ; python_version < "3" urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wheel==0.33.4 wmi==1.4.9 wrapt==1.11.1 # via aws-xray-sdk xmltodict==0.12.0 # via moto zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py2.7/zeromq-amzn-2.txt b/requirements/static/py2.7/zeromq-amzn-2.txt deleted file mode 100644 index ba67e6d4f78a..000000000000 --- a/requirements/static/py2.7/zeromq-amzn-2.txt +++ /dev/null @@ -1,126 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/zeromq-amzn-2.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/amzn-2.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -enum34==1.1.6 # via cryptography -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via cryptography, docker, kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -kazoo==2.6.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py2.7/zeromq-amzn-2018.03.txt b/requirements/static/py2.7/zeromq-amzn-2018.03.txt deleted file mode 100644 index d3776c741abc..000000000000 --- a/requirements/static/py2.7/zeromq-amzn-2018.03.txt +++ /dev/null @@ -1,127 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/zeromq-amzn-2018.03.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/amzn-2018.03.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -enum34==1.1.6 # via cryptography -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -gitdb==0.6.4 # via gitpython -gitpython==2.0.8 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via cryptography, docker, kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap==0.9.0 # via gitdb -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py2.7/zeromq-arch.txt b/requirements/static/py2.7/zeromq-arch.txt deleted file mode 100644 index 31e7467a9363..000000000000 --- a/requirements/static/py2.7/zeromq-arch.txt +++ /dev/null @@ -1,117 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/zeromq-arch.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/arch.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -enum34==1.1.6 # via cryptography -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via cryptography, docker, kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -keyring==5.7.1 -kubernetes==3.0.0 -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py2.7/zeromq-centos-6.txt b/requirements/static/py2.7/zeromq-centos-6.txt deleted file mode 100644 index ea957f8b62b2..000000000000 --- a/requirements/static/py2.7/zeromq-centos-6.txt +++ /dev/null @@ -1,128 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/zeromq-centos-6.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/centos-6.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -enum34==1.1.6 # via cryptography -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -gitdb==0.6.4 # via gitpython -gitpython==2.0.8 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via cryptography, docker, kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap==0.9.0 # via gitdb -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py2.7/zeromq-centos-7.txt b/requirements/static/py2.7/zeromq-centos-7.txt deleted file mode 100644 index 9638a2c5e77d..000000000000 --- a/requirements/static/py2.7/zeromq-centos-7.txt +++ /dev/null @@ -1,127 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/zeromq-centos-7.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/centos-7.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -enum34==1.1.6 # via cryptography -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via cryptography, docker, kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -kazoo==2.6.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py2.7/zeromq-debian-8.txt b/requirements/static/py2.7/zeromq-debian-8.txt deleted file mode 100644 index 1d894827c521..000000000000 --- a/requirements/static/py2.7/zeromq-debian-8.txt +++ /dev/null @@ -1,126 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/zeromq-debian-8.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-8.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -enum34==1.1.6 # via cryptography -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via cryptography, docker, kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py2.7/zeromq-debian-9.txt b/requirements/static/py2.7/zeromq-debian-9.txt deleted file mode 100644 index 2b023543102b..000000000000 --- a/requirements/static/py2.7/zeromq-debian-9.txt +++ /dev/null @@ -1,126 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/zeromq-debian-9.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-9.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -enum34==1.1.6 # via cryptography -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via cryptography, docker, kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py2.7/zeromq-fedora-30.txt b/requirements/static/py2.7/zeromq-fedora-30.txt deleted file mode 100644 index fee4b1ad5d04..000000000000 --- a/requirements/static/py2.7/zeromq-fedora-30.txt +++ /dev/null @@ -1,128 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/zeromq-fedora-30.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/fedora-30.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -enum34==1.1.6 # via cryptography -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via cryptography, docker, kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py2.7/zeromq-opensuse-leap-15.txt b/requirements/static/py2.7/zeromq-opensuse-leap-15.txt deleted file mode 100644 index 0d5b830a5318..000000000000 --- a/requirements/static/py2.7/zeromq-opensuse-leap-15.txt +++ /dev/null @@ -1,119 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/zeromq-opensuse-leap-15.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/opensuse-leap-15.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -enum34==1.1.6 # via cryptography -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -hgtools==8.1.1 -idna==2.8 # via requests -ipaddress==1.0.22 # via cryptography, docker, kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -keyring==5.7.1 -kubernetes==3.0.0 -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -setproctitle==1.1.10 -setuptools-scm==3.2.0 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py2.7/zeromq-ubuntu-16.04.txt b/requirements/static/py2.7/zeromq-ubuntu-16.04.txt deleted file mode 100644 index 6cac17e43ffa..000000000000 --- a/requirements/static/py2.7/zeromq-ubuntu-16.04.txt +++ /dev/null @@ -1,126 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/zeromq-ubuntu-16.04.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/ubuntu-16.04.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -enum34==1.1.6 # via cryptography -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via cryptography, docker, kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py2.7/zeromq-ubuntu-18.04.txt b/requirements/static/py2.7/zeromq-ubuntu-18.04.txt deleted file mode 100644 index c2140d4107f8..000000000000 --- a/requirements/static/py2.7/zeromq-ubuntu-18.04.txt +++ /dev/null @@ -1,126 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/zeromq-ubuntu-18.04.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/ubuntu-18.04.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -cookies==2.2.1 # via responses -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -enum34==1.1.6 # via cryptography -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via cryptography, docker, kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version < "3" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.4/linux-crypto.txt b/requirements/static/py3.4/linux-crypto.txt new file mode 100644 index 000000000000..a83659e76786 --- /dev/null +++ b/requirements/static/py3.4/linux-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.4/linux-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.3 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.4/zeromq-fedora-29.txt b/requirements/static/py3.4/linux.txt similarity index 69% rename from requirements/static/py3.4/zeromq-fedora-29.txt rename to requirements/static/py3.4/linux.txt index 427be8ad5f4c..7f4db5a2a839 100644 --- a/requirements/static/py3.4/zeromq-fedora-29.txt +++ b/requirements/static/py3.4/linux.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.4/zeromq-fedora-29.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/fedora-29.in +# pip-compile -o requirements/static/py3.4/linux.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/linux.in # apache-libcloud==2.0.0 argh==0.26.2 # via watchdog @@ -10,33 +10,35 @@ asn1crypto==0.24.0 # via cryptography atomicwrites==1.3.0 # via pytest attrs==19.1.0 # via pytest aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client +backports.ssl-match-hostname==3.7.0.1 # via docker bcrypt==3.1.6 # via paramiko boto3==1.9.132 boto==2.49.0 botocore==1.12.132 # via boto3, moto, s3transfer cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado +certifi==2019.3.9 cffi==1.12.2 chardet==3.0.4 # via requests +cheetah3==3.1.0 cheroot==6.5.4 # via cherrypy cherrypy==17.3.0 contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov croniter==0.3.29 cryptography==2.6.1 # via moto, paramiko, pyopenssl dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==3.7.2 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose future==0.17.1 # via python-jose +genshi==0.7.3 gitdb2==2.0.5 # via gitpython gitpython==2.1.11 google-auth==1.6.3 # via kubernetes +hgtools==8.1.1 idna==2.8 # via requests +importlib-metadata==0.23 # via pluggy, pytest ipaddress==1.0.22 # via kubernetes jaraco.functools==2.0 # via tempora jinja2==2.10.1 @@ -46,22 +48,23 @@ jsonpickle==1.1 # via aws-xray-sdk jsonschema==2.6.0 junos-eznc==2.2.0 jxmlease==1.0.1 +kazoo==2.6.1 keyring==5.7.1 kubernetes==3.0.0 lxml==4.3.3 # via junos-eznc, ncclient +mako==1.1.0 markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" +mock==3.0.5 ; python_version < "3.6" more-itertools==5.0.0 moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathlib2==2.3.3 # via pytest +packaging==19.2 # via pytest +paramiko==2.4.2 +pathlib2==2.3.3 # via importlib-metadata, pytest pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest +pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -69,28 +72,25 @@ pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 +pycryptodome==3.8.1 ; sys_platform != "win32" pygit2==0.28.2 pyinotify==0.9.6 pynacl==1.3.0 # via paramiko pyopenssl==19.0.0 +pyparsing==2.4.5 # via packaging pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto python-etcd==0.4.5 python-gnupg==0.4.4 python-jose==2.0.2 # via moto pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==17.0.0 ; python_version == "3.4" requests==2.21.0 responses==0.10.6 # via moto @@ -102,8 +102,8 @@ scandir==1.10.0 # via pathlib2 scp==0.13.2 # via junos-eznc selectors2==2.0.1 # via ncclient setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +setuptools-scm==3.2.0 +six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, packaging, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, websocket-client smmap2==2.0.5 # via gitdb2 strict-rfc3339==0.7 tempora==1.14.1 # via portend @@ -112,8 +112,10 @@ tornado==4.5.3 ; python_version >= "3.4" urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wrapt==1.11.1 # via aws-xray-sdk xmltodict==0.12.0 # via moto zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py3.4/zeromq-amzn-2.txt b/requirements/static/py3.4/zeromq-amzn-2.txt deleted file mode 100644 index a6f3556a54f9..000000000000 --- a/requirements/static/py3.4/zeromq-amzn-2.txt +++ /dev/null @@ -1,117 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/zeromq-amzn-2.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/amzn-2.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -kazoo==2.6.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==17.0.0 ; python_version == "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.4/zeromq-arch.txt b/requirements/static/py3.4/zeromq-arch.txt deleted file mode 100644 index fb37b5f4bca5..000000000000 --- a/requirements/static/py3.4/zeromq-arch.txt +++ /dev/null @@ -1,108 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/zeromq-arch.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/arch.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -keyring==5.7.1 -kubernetes==3.0.0 -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==17.0.0 ; python_version == "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.4/zeromq-centos-7.txt b/requirements/static/py3.4/zeromq-centos-7.txt deleted file mode 100644 index 948de772a576..000000000000 --- a/requirements/static/py3.4/zeromq-centos-7.txt +++ /dev/null @@ -1,118 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/zeromq-centos-7.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/centos-7.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -kazoo==2.6.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==17.0.0 ; python_version == "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.4/zeromq-debian-10.txt b/requirements/static/py3.4/zeromq-debian-10.txt deleted file mode 100644 index bbdd6e51bfe6..000000000000 --- a/requirements/static/py3.4/zeromq-debian-10.txt +++ /dev/null @@ -1,120 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/zeromq-debian-10.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-10.in -# -apache-libcloud==2.0.0 -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker -bcrypt==3.1.7 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -clustershell==1.8.1 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.4 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pylxd, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.2.3 -pathlib2==2.3.3 # via pytest -pbr==5.1.3 # via mock, pylxd -pluggy==0.11.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.1 -pyinotify==0.9.6 -pylxd==2.2.9 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.7.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.2 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto, pylxd -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==17.0.0 ; python_version == "3.4" -requests-toolbelt==0.9.1 # via pylxd -requests-unixsocket==0.1.5 # via pylxd -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pylxd, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.3 # via botocore, kubernetes, python-etcd, requests, requests-unixsocket -virtualenv==16.4.3 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -ws4py==0.5.1 # via pylxd -xmltodict==0.12.0 # via moto -yamlordereddictloader==0.4.0 -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.4/zeromq-debian-8.txt b/requirements/static/py3.4/zeromq-debian-8.txt deleted file mode 100644 index 5728a8cfbdb7..000000000000 --- a/requirements/static/py3.4/zeromq-debian-8.txt +++ /dev/null @@ -1,117 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/zeromq-debian-8.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-8.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==17.0.0 ; python_version == "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.4/zeromq-debian-9.txt b/requirements/static/py3.4/zeromq-debian-9.txt deleted file mode 100644 index 53354b094b6e..000000000000 --- a/requirements/static/py3.4/zeromq-debian-9.txt +++ /dev/null @@ -1,117 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/zeromq-debian-9.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-9.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==17.0.0 ; python_version == "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.4/zeromq-fedora-30.txt b/requirements/static/py3.4/zeromq-fedora-30.txt deleted file mode 100644 index 8b6830f681b1..000000000000 --- a/requirements/static/py3.4/zeromq-fedora-30.txt +++ /dev/null @@ -1,119 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/zeromq-fedora-30.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/fedora-30.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==17.0.0 ; python_version == "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.4/zeromq-opensuse-leap-15.txt b/requirements/static/py3.4/zeromq-opensuse-leap-15.txt deleted file mode 100644 index 8d7e7f0a1dca..000000000000 --- a/requirements/static/py3.4/zeromq-opensuse-leap-15.txt +++ /dev/null @@ -1,110 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/zeromq-opensuse-leap-15.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/opensuse-leap-15.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -hgtools==8.1.1 -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -keyring==5.7.1 -kubernetes==3.0.0 -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==17.0.0 ; python_version == "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -setproctitle==1.1.10 -setuptools-scm==3.2.0 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.4/zeromq-ubuntu-16.04.txt b/requirements/static/py3.4/zeromq-ubuntu-16.04.txt deleted file mode 100644 index a66820c77e11..000000000000 --- a/requirements/static/py3.4/zeromq-ubuntu-16.04.txt +++ /dev/null @@ -1,117 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/zeromq-ubuntu-16.04.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/ubuntu-16.04.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==17.0.0 ; python_version == "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.4/zeromq-ubuntu-18.04.txt b/requirements/static/py3.4/zeromq-ubuntu-18.04.txt deleted file mode 100644 index fe3029ebc97a..000000000000 --- a/requirements/static/py3.4/zeromq-ubuntu-18.04.txt +++ /dev/null @@ -1,117 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/zeromq-ubuntu-18.04.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/ubuntu-18.04.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==17.0.0 ; python_version == "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.5/darwin-crypto.txt b/requirements/static/py3.5/darwin-crypto.txt new file mode 100644 index 000000000000..a6a86036f2e7 --- /dev/null +++ b/requirements/static/py3.5/darwin-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.5/darwin-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.0 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.5/zeromq-osx.txt b/requirements/static/py3.5/darwin.txt similarity index 73% rename from requirements/static/py3.5/zeromq-osx.txt rename to requirements/static/py3.5/darwin.txt index b34829fdb771..0b60fe3bb031 100644 --- a/requirements/static/py3.5/zeromq-osx.txt +++ b/requirements/static/py3.5/darwin.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.5/zeromq-osx.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/osx.in +# pip-compile -o requirements/static/py3.5/darwin.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/darwin.in # apache-libcloud==2.4.0 argh==0.26.2 # via watchdog @@ -10,9 +10,9 @@ asn1crypto==0.24.0 # via cryptography atomicwrites==1.3.0 # via pytest attrs==19.1.0 # via pytest aws-xray-sdk==0.95 # via moto -backports-abc==0.5 backports.functools-lru-cache==1.5 # via cheroot backports.ssl_match_hostname==3.7.0.1 +backports_abc==0.5 bcrypt==3.1.6 # via paramiko boto3==1.9.132 boto==2.49.0 @@ -21,26 +21,28 @@ cachetools==3.1.0 # via google-auth certifi==2019.3.9 cffi==1.12.2 chardet==3.0.4 # via requests +cheetah3==3.1.0 cheroot==6.5.5 # via cherrypy cherrypy==17.4.1 click==7.0 clustershell==1.8.1 contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov croniter==0.3.29 cryptography==2.6.1 dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==3.7.2 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose enum34==1.1.6 future==0.17.1 # via python-jose +genshi==0.7.3 gitdb2==2.0.5 # via gitpython gitdb==0.6.4 gitpython==2.1.11 google-auth==1.6.3 # via kubernetes idna==2.8 +importlib-metadata==0.23 # via pluggy, pytest ipaddress==1.0.22 jaraco.functools==2.0 # via tempora jinja2==2.10.1 @@ -56,18 +58,18 @@ linode-python==1.1.1 lxml==4.3.3 # via junos-eznc, ncclient mako==1.0.7 markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" +mock==3.0.5 ; python_version < "3.6" more-itertools==5.0.0 moto==1.3.7 msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc +packaging==19.2 # via pytest paramiko==2.4.2 # via junos-eznc, ncclient, scp pathlib2==2.3.3 # via pytest pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock, pylxd -pluggy==0.9.0 # via pytest +pluggy==0.13.1 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -75,30 +77,24 @@ pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth pyasn1==0.4.5 pycparser==2.19 -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pylxd==2.2.9 +pycryptodome==3.8.1 ; sys_platform != "win32" pynacl==1.3.0 # via paramiko pyopenssl==19.0.0 +pyparsing==2.4.5 # via packaging pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 python-etcd==0.4.5 python-gnupg==0.4.4 python-jose==2.0.2 # via moto pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" -requests-toolbelt==0.9.1 # via pylxd -requests-unixsocket==0.1.5 # via pylxd requests==2.21.0 responses==0.10.6 # via moto rfc3987==1.3.8 @@ -108,23 +104,24 @@ salttesting==2017.6.1 scp==0.13.2 # via junos-eznc setproctitle==1.1.10 singledispatch==3.4.0.3 -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pylxd, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, packaging, pathlib2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client smmap2==2.0.5 # via gitdb2 smmap==0.9.0 strict-rfc3339==0.7 tempora==1.14.1 # via portend timelib==0.2.4 tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests, requests-unixsocket +urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 vultr==1.0.1 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wrapt==1.11.1 # via aws-xray-sdk -ws4py==0.5.1 # via pylxd xmltodict==0.12.0 # via moto yamlordereddictloader==0.4.0 zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata # Passthrough dependencies from pkg/osx/req.txt pyobjc==5.1.2 diff --git a/requirements/static/py3.5/linux-crypto.txt b/requirements/static/py3.5/linux-crypto.txt new file mode 100644 index 000000000000..59c45171e55c --- /dev/null +++ b/requirements/static/py3.5/linux-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.5/linux-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.3 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.7/zeromq-debian-8.txt b/requirements/static/py3.5/linux.txt similarity index 70% rename from requirements/static/py3.7/zeromq-debian-8.txt rename to requirements/static/py3.5/linux.txt index 9818c1d44ed1..a7a7831027e0 100644 --- a/requirements/static/py3.7/zeromq-debian-8.txt +++ b/requirements/static/py3.5/linux.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.7/zeromq-debian-8.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-8.in +# pip-compile -o requirements/static/py3.5/linux.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/linux.in # apache-libcloud==2.0.0 argh==0.26.2 # via watchdog @@ -10,33 +10,34 @@ asn1crypto==0.24.0 # via cryptography atomicwrites==1.3.0 # via pytest attrs==19.1.0 # via pytest aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client bcrypt==3.1.6 # via paramiko boto3==1.9.132 boto==2.49.0 botocore==1.12.132 # via boto3, moto, s3transfer cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado +certifi==2019.3.9 cffi==1.12.2 chardet==3.0.4 # via requests +cheetah3==3.1.0 cheroot==6.5.4 # via cherrypy cherrypy==17.3.0 contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov croniter==0.3.29 cryptography==2.6.1 # via moto, paramiko, pyopenssl dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==3.7.2 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose future==0.17.1 # via python-jose +genshi==0.7.3 gitdb2==2.0.5 # via gitpython gitpython==2.1.11 google-auth==1.6.3 # via kubernetes +hgtools==8.1.1 idna==2.8 # via requests +importlib-metadata==0.23 # via pluggy, pytest ipaddress==1.0.22 # via kubernetes jaraco.functools==2.0 # via tempora jinja2==2.10.1 @@ -46,21 +47,23 @@ jsonpickle==1.1 # via aws-xray-sdk jsonschema==2.6.0 junos-eznc==2.2.0 jxmlease==1.0.1 +kazoo==2.6.1 keyring==5.7.1 kubernetes==3.0.0 lxml==4.3.3 # via junos-eznc, ncclient +mako==1.1.0 markupsafe==1.1.1 -mock==2.0.0 # via moto +mock==3.0.5 ; python_version < "3.6" more-itertools==5.0.0 moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc -paramiko==2.2.3 ; python_version >= "3.7" +packaging==19.2 # via pytest +paramiko==2.4.2 +pathlib2==2.3.3 # via pytest pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest +pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -68,28 +71,25 @@ pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 +pycryptodome==3.8.1 ; sys_platform != "win32" pygit2==0.28.2 pyinotify==0.9.6 pynacl==1.3.0 # via paramiko pyopenssl==19.0.0 +pyparsing==2.4.5 # via packaging pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto python-etcd==0.4.5 python-gnupg==0.4.4 python-jose==2.0.2 # via moto pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" requests==2.21.0 responses==0.10.6 # via moto @@ -99,8 +99,8 @@ s3transfer==0.2.0 # via boto3 salttesting==2017.6.1 scp==0.13.2 # via junos-eznc setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +setuptools-scm==3.2.0 +six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, packaging, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, websocket-client smmap2==2.0.5 # via gitdb2 strict-rfc3339==0.7 tempora==1.14.1 # via portend @@ -109,8 +109,10 @@ tornado==4.5.3 ; python_version >= "3.4" urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wrapt==1.11.1 # via aws-xray-sdk xmltodict==0.12.0 # via moto zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py3.5/windows-crypto.txt b/requirements/static/py3.5/windows-crypto.txt new file mode 100644 index 000000000000..1e209d42238b --- /dev/null +++ b/requirements/static/py3.5/windows-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.5/windows-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.0 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.5/zeromq-windows.txt b/requirements/static/py3.5/windows.txt similarity index 76% rename from requirements/static/py3.5/zeromq-windows.txt rename to requirements/static/py3.5/windows.txt index e6fd677e62aa..ac4ad28a7c05 100644 --- a/requirements/static/py3.5/zeromq-windows.txt +++ b/requirements/static/py3.5/windows.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.5/zeromq-windows.txt -v pkg/windows/req.txt pkg/windows/req_win.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/windows.in +# pip-compile -o requirements/static/py3.5/windows.txt -v pkg/windows/req.txt pkg/windows/req_win.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/windows.in # argh==0.26.2 # via watchdog asn1crypto==0.24.0 # via cryptography @@ -19,25 +19,27 @@ cachetools==3.1.0 # via google-auth certifi==2019.3.9 cffi==1.12.2 chardet==3.0.4 # via requests +cheetah3==3.1.0 cheroot==6.5.5 # via cherrypy cherrypy==17.4.1 colorama==0.4.1 # via pytest contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov cryptography==2.6.1 dmidecode==0.9.0 dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==2.7.0 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose enum34==1.1.6 future==0.17.1 # via python-jose +genshi==0.7.3 gitdb2==2.0.5 # via gitpython gitdb==0.6.4 gitpython==2.1.10 google-auth==1.6.3 # via kubernetes idna==2.8 +importlib-metadata==0.23 # via pluggy, pytest ioloop==0.1a0 ipaddress==1.0.22 jaraco.functools==2.0 # via tempora @@ -52,16 +54,16 @@ libnacl==1.6.1 lxml==4.3.0 mako==1.0.7 markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" +mock==3.0.5 ; python_version < "3.6" more-itertools==5.0.0 moto==1.3.7 msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 +packaging==19.2 # via pytest patch==1.16 pathlib2==2.3.3 # via pytest pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest +pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -75,13 +77,12 @@ pycurl==7.43.0.2 pygit2==0.28.2 pymysql==0.9.3 pyopenssl==19.0.0 -pytest-cov==2.6.1 +pyparsing==2.4.5 # via packaging pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 python-etcd==0.4.5 python-gnupg==0.4.4 @@ -90,7 +91,7 @@ pythonnet==2.3.0 pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 pywin32==224 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" requests==2.21.0 responses==0.10.6 # via moto @@ -101,7 +102,7 @@ salttesting==2017.6.1 sed==0.3.1 setproctitle==1.1.10 singledispatch==3.4.0.3 -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, packaging, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client smmap2==2.0.5 # via gitdb2 smmap==0.9.0 strict-rfc3339==0.7 @@ -111,10 +112,12 @@ tornado==4.5.3 ; python_version >= "3.4" urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wheel==0.33.4 wmi==1.4.9 wrapt==1.11.1 # via aws-xray-sdk xmltodict==0.12.0 # via moto zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py3.5/zeromq-amzn-2.txt b/requirements/static/py3.5/zeromq-amzn-2.txt deleted file mode 100644 index 096f6488338f..000000000000 --- a/requirements/static/py3.5/zeromq-amzn-2.txt +++ /dev/null @@ -1,115 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.5/zeromq-amzn-2.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/amzn-2.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -kazoo==2.6.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.5/zeromq-arch.txt b/requirements/static/py3.5/zeromq-arch.txt deleted file mode 100644 index 9dce579b4b0c..000000000000 --- a/requirements/static/py3.5/zeromq-arch.txt +++ /dev/null @@ -1,107 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.5/zeromq-arch.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/arch.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -keyring==5.7.1 -kubernetes==3.0.0 -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.5/zeromq-centos-7.txt b/requirements/static/py3.5/zeromq-centos-7.txt deleted file mode 100644 index b40b7ebbc4ff..000000000000 --- a/requirements/static/py3.5/zeromq-centos-7.txt +++ /dev/null @@ -1,116 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.5/zeromq-centos-7.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/centos-7.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -kazoo==2.6.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.5/zeromq-debian-10.txt b/requirements/static/py3.5/zeromq-debian-10.txt deleted file mode 100644 index 73f64e481fa4..000000000000 --- a/requirements/static/py3.5/zeromq-debian-10.txt +++ /dev/null @@ -1,117 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.5/zeromq-debian-10.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-10.in -# -apache-libcloud==2.0.0 -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports.functools-lru-cache==1.5 # via cheroot -bcrypt==3.1.7 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -clustershell==1.8.1 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.4 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pylxd, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.2.3 -pathlib2==2.3.3 # via pytest -pbr==5.1.3 # via mock, pylxd -pluggy==0.11.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.1 -pyinotify==0.9.6 -pylxd==2.2.9 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.7.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.2 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto, pylxd -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests-toolbelt==0.9.1 # via pylxd -requests-unixsocket==0.1.5 # via pylxd -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pylxd, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.3 # via botocore, kubernetes, python-etcd, requests, requests-unixsocket -virtualenv==16.4.3 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -ws4py==0.5.1 # via pylxd -xmltodict==0.12.0 # via moto -yamlordereddictloader==0.4.0 -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.5/zeromq-debian-8.txt b/requirements/static/py3.5/zeromq-debian-8.txt deleted file mode 100644 index 8c1123e6065f..000000000000 --- a/requirements/static/py3.5/zeromq-debian-8.txt +++ /dev/null @@ -1,115 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.5/zeromq-debian-8.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-8.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.5/zeromq-debian-9.txt b/requirements/static/py3.5/zeromq-debian-9.txt deleted file mode 100644 index 84a767f25fe7..000000000000 --- a/requirements/static/py3.5/zeromq-debian-9.txt +++ /dev/null @@ -1,115 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.5/zeromq-debian-9.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-9.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.5/zeromq-fedora-29.txt b/requirements/static/py3.5/zeromq-fedora-29.txt deleted file mode 100644 index e4e4ebb6cfc1..000000000000 --- a/requirements/static/py3.5/zeromq-fedora-29.txt +++ /dev/null @@ -1,117 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.5/zeromq-fedora-29.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/fedora-29.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.5/zeromq-fedora-30.txt b/requirements/static/py3.5/zeromq-fedora-30.txt deleted file mode 100644 index 361d5b50237e..000000000000 --- a/requirements/static/py3.5/zeromq-fedora-30.txt +++ /dev/null @@ -1,117 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.5/zeromq-fedora-30.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/fedora-30.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.5/zeromq-opensuse-leap-15.txt b/requirements/static/py3.5/zeromq-opensuse-leap-15.txt deleted file mode 100644 index 0d01ae60a928..000000000000 --- a/requirements/static/py3.5/zeromq-opensuse-leap-15.txt +++ /dev/null @@ -1,109 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.5/zeromq-opensuse-leap-15.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/opensuse-leap-15.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -hgtools==8.1.1 -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -keyring==5.7.1 -kubernetes==3.0.0 -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -setproctitle==1.1.10 -setuptools-scm==3.2.0 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.5/zeromq-ubuntu-16.04.txt b/requirements/static/py3.5/zeromq-ubuntu-16.04.txt deleted file mode 100644 index 1b839b8236de..000000000000 --- a/requirements/static/py3.5/zeromq-ubuntu-16.04.txt +++ /dev/null @@ -1,115 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.5/zeromq-ubuntu-16.04.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/ubuntu-16.04.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.5/zeromq-ubuntu-18.04.txt b/requirements/static/py3.5/zeromq-ubuntu-18.04.txt deleted file mode 100644 index 5629353f765a..000000000000 --- a/requirements/static/py3.5/zeromq-ubuntu-18.04.txt +++ /dev/null @@ -1,115 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.5/zeromq-ubuntu-18.04.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/ubuntu-18.04.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathlib2==2.3.3 # via pytest -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.6/darwin-crypto.txt b/requirements/static/py3.6/darwin-crypto.txt new file mode 100644 index 000000000000..9c11d937f084 --- /dev/null +++ b/requirements/static/py3.6/darwin-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.6/darwin-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.0 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.7/zeromq-osx.txt b/requirements/static/py3.6/darwin.txt similarity index 74% rename from requirements/static/py3.7/zeromq-osx.txt rename to requirements/static/py3.6/darwin.txt index e7a78b6df202..d944e0e6301d 100644 --- a/requirements/static/py3.7/zeromq-osx.txt +++ b/requirements/static/py3.6/darwin.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.7/zeromq-osx.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/osx.in +# pip-compile -o requirements/static/py3.6/darwin.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/darwin.in # apache-libcloud==2.4.0 argh==0.26.2 # via watchdog @@ -10,9 +10,9 @@ asn1crypto==0.24.0 # via cryptography atomicwrites==1.3.0 # via pytest attrs==19.1.0 # via pytest aws-xray-sdk==0.95 # via moto -backports-abc==0.5 backports.functools-lru-cache==1.5 # via cheroot backports.ssl_match_hostname==3.7.0.1 +backports_abc==0.5 bcrypt==3.1.6 # via paramiko boto3==1.9.132 boto==2.49.0 @@ -21,26 +21,28 @@ cachetools==3.1.0 # via google-auth certifi==2019.3.9 cffi==1.12.2 chardet==3.0.4 # via requests +cheetah3==3.1.0 cheroot==6.5.5 # via cherrypy cherrypy==17.4.1 click==7.0 clustershell==1.8.1 contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov croniter==0.3.29 cryptography==2.6.1 dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==3.7.2 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose enum34==1.1.6 future==0.17.1 # via python-jose +genshi==0.7.3 gitdb2==2.0.5 # via gitpython gitdb==0.6.4 gitpython==2.1.11 google-auth==1.6.3 # via kubernetes idna==2.8 +importlib-metadata==0.23 # via pluggy, pytest ipaddress==1.0.22 jaraco.functools==2.0 # via tempora jinja2==2.10.1 @@ -60,13 +62,14 @@ mock==2.0.0 # via moto more-itertools==5.0.0 moto==1.3.7 msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc +packaging==19.2 # via pytest paramiko==2.4.2 # via junos-eznc, ncclient, scp pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock, pylxd -pluggy==0.9.0 # via pytest +pbr==5.1.3 # via mock +pluggy==0.13.1 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -74,30 +77,24 @@ pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth pyasn1==0.4.5 pycparser==2.19 -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pylxd==2.2.9 +pycryptodome==3.8.1 ; sys_platform != "win32" pynacl==1.3.0 # via paramiko pyopenssl==19.0.0 +pyparsing==2.4.5 # via packaging pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 python-etcd==0.4.5 python-gnupg==0.4.4 python-jose==2.0.2 # via moto pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" -requests-toolbelt==0.9.1 # via pylxd -requests-unixsocket==0.1.5 # via pylxd requests==2.21.0 responses==0.10.6 # via moto rfc3987==1.3.8 @@ -107,23 +104,24 @@ salttesting==2017.6.1 scp==0.13.2 # via junos-eznc setproctitle==1.1.10 singledispatch==3.4.0.3 -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pylxd, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, packaging, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client smmap2==2.0.5 # via gitdb2 smmap==0.9.0 strict-rfc3339==0.7 tempora==1.14.1 # via portend timelib==0.2.4 tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests, requests-unixsocket +urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 vultr==1.0.1 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wrapt==1.11.1 # via aws-xray-sdk -ws4py==0.5.1 # via pylxd xmltodict==0.12.0 # via moto yamlordereddictloader==0.4.0 zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata # Passthrough dependencies from pkg/osx/req.txt pyobjc==5.1.2 diff --git a/requirements/static/py3.6/linux-crypto.txt b/requirements/static/py3.6/linux-crypto.txt new file mode 100644 index 000000000000..3b92d88ffb20 --- /dev/null +++ b/requirements/static/py3.6/linux-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.6/linux-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.3 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.7/zeromq-amzn-2.txt b/requirements/static/py3.6/linux.txt similarity index 73% rename from requirements/static/py3.7/zeromq-amzn-2.txt rename to requirements/static/py3.6/linux.txt index 6fc843711563..db65981db59c 100644 --- a/requirements/static/py3.7/zeromq-amzn-2.txt +++ b/requirements/static/py3.6/linux.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.7/zeromq-amzn-2.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/amzn-2.in +# pip-compile -o requirements/static/py3.6/linux.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/linux.in # apache-libcloud==2.0.0 argh==0.26.2 # via watchdog @@ -10,33 +10,34 @@ asn1crypto==0.24.0 # via cryptography atomicwrites==1.3.0 # via pytest attrs==19.1.0 # via pytest aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client bcrypt==3.1.6 # via paramiko boto3==1.9.132 boto==2.49.0 botocore==1.12.132 # via boto3, moto, s3transfer cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado +certifi==2019.3.9 cffi==1.12.2 chardet==3.0.4 # via requests +cheetah3==3.1.0 cheroot==6.5.4 # via cherrypy cherrypy==17.3.0 contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov croniter==0.3.29 cryptography==2.6.1 # via moto, paramiko, pyopenssl dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==3.7.2 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose future==0.17.1 # via python-jose +genshi==0.7.3 gitdb2==2.0.5 # via gitpython gitpython==2.1.11 google-auth==1.6.3 # via kubernetes +hgtools==8.1.1 idna==2.8 # via requests +importlib-metadata==0.23 # via pluggy, pytest ipaddress==1.0.22 # via kubernetes jaraco.functools==2.0 # via tempora jinja2==2.10.1 @@ -50,18 +51,19 @@ kazoo==2.6.1 keyring==5.7.1 kubernetes==3.0.0 lxml==4.3.3 # via junos-eznc, ncclient +mako==1.1.0 markupsafe==1.1.1 mock==2.0.0 # via moto more-itertools==5.0.0 moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc -paramiko==2.2.3 ; python_version >= "3.7" +packaging==19.2 # via pytest +paramiko==2.4.2 pathtools==0.1.2 # via watchdog pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest +pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -69,27 +71,25 @@ pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 +pycryptodome==3.8.1 ; sys_platform != "win32" +pygit2==0.28.2 pyinotify==0.9.6 pynacl==1.3.0 # via paramiko pyopenssl==19.0.0 +pyparsing==2.4.5 # via packaging pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto python-etcd==0.4.5 python-gnupg==0.4.4 python-jose==2.0.2 # via moto pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" requests==2.21.0 responses==0.10.6 # via moto @@ -99,8 +99,8 @@ s3transfer==0.2.0 # via boto3 salttesting==2017.6.1 scp==0.13.2 # via junos-eznc setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +setuptools-scm==3.2.0 +six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, packaging, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, websocket-client smmap2==2.0.5 # via gitdb2 strict-rfc3339==0.7 tempora==1.14.1 # via portend @@ -109,8 +109,10 @@ tornado==4.5.3 ; python_version >= "3.4" urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wrapt==1.11.1 # via aws-xray-sdk xmltodict==0.12.0 # via moto zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py3.6/windows-crypto.txt b/requirements/static/py3.6/windows-crypto.txt new file mode 100644 index 000000000000..2e4eaf54bcc0 --- /dev/null +++ b/requirements/static/py3.6/windows-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.6/windows-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.0 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.6/zeromq-windows.txt b/requirements/static/py3.6/windows.txt similarity index 77% rename from requirements/static/py3.6/zeromq-windows.txt rename to requirements/static/py3.6/windows.txt index 3edddc41239c..97dd3082d70c 100644 --- a/requirements/static/py3.6/zeromq-windows.txt +++ b/requirements/static/py3.6/windows.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.6/zeromq-windows.txt -v pkg/windows/req.txt pkg/windows/req_win.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/windows.in +# pip-compile -o requirements/static/py3.6/windows.txt -v pkg/windows/req.txt pkg/windows/req_win.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/windows.in # argh==0.26.2 # via watchdog asn1crypto==0.24.0 # via cryptography @@ -19,25 +19,27 @@ cachetools==3.1.0 # via google-auth certifi==2019.3.9 cffi==1.12.2 chardet==3.0.4 # via requests +cheetah3==3.1.0 cheroot==6.5.5 # via cherrypy cherrypy==17.4.1 colorama==0.4.1 # via pytest contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov cryptography==2.6.1 dmidecode==0.9.0 dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==2.7.0 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose enum34==1.1.6 future==0.17.1 # via python-jose +genshi==0.7.3 gitdb2==2.0.5 # via gitpython gitdb==0.6.4 gitpython==2.1.10 google-auth==1.6.3 # via kubernetes idna==2.8 +importlib-metadata==0.23 # via pluggy, pytest ioloop==0.1a0 ipaddress==1.0.22 jaraco.functools==2.0 # via tempora @@ -56,11 +58,12 @@ mock==2.0.0 # via moto more-itertools==5.0.0 moto==1.3.7 msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 +packaging==19.2 # via pytest patch==1.16 pathtools==0.1.2 # via watchdog pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest +pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -74,13 +77,12 @@ pycurl==7.43.0.2 pygit2==0.28.2 pymysql==0.9.3 pyopenssl==19.0.0 -pytest-cov==2.6.1 +pyparsing==2.4.5 # via packaging pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 python-etcd==0.4.5 python-gnupg==0.4.4 @@ -89,7 +91,7 @@ pythonnet==2.3.0 pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 pywin32==224 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" requests==2.21.0 responses==0.10.6 # via moto @@ -100,7 +102,7 @@ salttesting==2017.6.1 sed==0.3.1 setproctitle==1.1.10 singledispatch==3.4.0.3 -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, packaging, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client smmap2==2.0.5 # via gitdb2 smmap==0.9.0 strict-rfc3339==0.7 @@ -110,10 +112,12 @@ tornado==4.5.3 ; python_version >= "3.4" urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wheel==0.33.4 wmi==1.4.9 wrapt==1.11.1 # via aws-xray-sdk xmltodict==0.12.0 # via moto zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py3.6/zeromq-amzn-2.txt b/requirements/static/py3.6/zeromq-amzn-2.txt deleted file mode 100644 index d6f28b006424..000000000000 --- a/requirements/static/py3.6/zeromq-amzn-2.txt +++ /dev/null @@ -1,114 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.6/zeromq-amzn-2.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/amzn-2.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -kazoo==2.6.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.6/zeromq-arch.txt b/requirements/static/py3.6/zeromq-arch.txt deleted file mode 100644 index 2cc0523cfd12..000000000000 --- a/requirements/static/py3.6/zeromq-arch.txt +++ /dev/null @@ -1,106 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.6/zeromq-arch.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/arch.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -keyring==5.7.1 -kubernetes==3.0.0 -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.6/zeromq-centos-7.txt b/requirements/static/py3.6/zeromq-centos-7.txt deleted file mode 100644 index 600d937990b3..000000000000 --- a/requirements/static/py3.6/zeromq-centos-7.txt +++ /dev/null @@ -1,115 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.6/zeromq-centos-7.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/centos-7.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -kazoo==2.6.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.6/zeromq-debian-10.txt b/requirements/static/py3.6/zeromq-debian-10.txt deleted file mode 100644 index cb09cec51d3d..000000000000 --- a/requirements/static/py3.6/zeromq-debian-10.txt +++ /dev/null @@ -1,116 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.6/zeromq-debian-10.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-10.in -# -apache-libcloud==2.0.0 -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports.functools-lru-cache==1.5 # via cheroot -bcrypt==3.1.7 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -clustershell==1.8.1 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.4 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pylxd, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.2.3 -pbr==5.1.3 # via mock, pylxd -pluggy==0.11.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.1 -pyinotify==0.9.6 -pylxd==2.2.9 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.7.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.2 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto, pylxd -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests-toolbelt==0.9.1 # via pylxd -requests-unixsocket==0.1.5 # via pylxd -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pylxd, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.3 # via botocore, kubernetes, python-etcd, requests, requests-unixsocket -virtualenv==16.4.3 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -ws4py==0.5.1 # via pylxd -xmltodict==0.12.0 # via moto -yamlordereddictloader==0.4.0 -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.6/zeromq-debian-8.txt b/requirements/static/py3.6/zeromq-debian-8.txt deleted file mode 100644 index efa4ca71d485..000000000000 --- a/requirements/static/py3.6/zeromq-debian-8.txt +++ /dev/null @@ -1,114 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.6/zeromq-debian-8.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-8.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.6/zeromq-debian-9.txt b/requirements/static/py3.6/zeromq-debian-9.txt deleted file mode 100644 index d071a806e52c..000000000000 --- a/requirements/static/py3.6/zeromq-debian-9.txt +++ /dev/null @@ -1,114 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.6/zeromq-debian-9.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-9.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.6/zeromq-fedora-29.txt b/requirements/static/py3.6/zeromq-fedora-29.txt deleted file mode 100644 index 65637bb4322f..000000000000 --- a/requirements/static/py3.6/zeromq-fedora-29.txt +++ /dev/null @@ -1,116 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.6/zeromq-fedora-29.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/fedora-29.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.6/zeromq-fedora-30.txt b/requirements/static/py3.6/zeromq-fedora-30.txt deleted file mode 100644 index a84cb092da83..000000000000 --- a/requirements/static/py3.6/zeromq-fedora-30.txt +++ /dev/null @@ -1,116 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.6/zeromq-fedora-30.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/fedora-30.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.6/zeromq-opensuse-leap-15.txt b/requirements/static/py3.6/zeromq-opensuse-leap-15.txt deleted file mode 100644 index 5ee5e665373b..000000000000 --- a/requirements/static/py3.6/zeromq-opensuse-leap-15.txt +++ /dev/null @@ -1,108 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.6/zeromq-opensuse-leap-15.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/opensuse-leap-15.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -hgtools==8.1.1 -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -keyring==5.7.1 -kubernetes==3.0.0 -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -setproctitle==1.1.10 -setuptools-scm==3.2.0 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.6/zeromq-ubuntu-16.04.txt b/requirements/static/py3.6/zeromq-ubuntu-16.04.txt deleted file mode 100644 index 0b7294f9424d..000000000000 --- a/requirements/static/py3.6/zeromq-ubuntu-16.04.txt +++ /dev/null @@ -1,114 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.6/zeromq-ubuntu-16.04.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/ubuntu-16.04.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.6/zeromq-ubuntu-18.04.txt b/requirements/static/py3.6/zeromq-ubuntu-18.04.txt deleted file mode 100644 index ebf60e359a39..000000000000 --- a/requirements/static/py3.6/zeromq-ubuntu-18.04.txt +++ /dev/null @@ -1,114 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.6/zeromq-ubuntu-18.04.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/ubuntu-18.04.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.1.2 ; python_version < "3.7" -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.7/darwin-crypto.txt b/requirements/static/py3.7/darwin-crypto.txt new file mode 100644 index 000000000000..370308dc087b --- /dev/null +++ b/requirements/static/py3.7/darwin-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.7/darwin-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.0 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.6/zeromq-osx.txt b/requirements/static/py3.7/darwin.txt similarity index 74% rename from requirements/static/py3.6/zeromq-osx.txt rename to requirements/static/py3.7/darwin.txt index 9d79e0f24a20..d6aa4e4cad66 100644 --- a/requirements/static/py3.6/zeromq-osx.txt +++ b/requirements/static/py3.7/darwin.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.6/zeromq-osx.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/osx.in +# pip-compile -o requirements/static/py3.7/darwin.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/darwin.in # apache-libcloud==2.4.0 argh==0.26.2 # via watchdog @@ -10,9 +10,9 @@ asn1crypto==0.24.0 # via cryptography atomicwrites==1.3.0 # via pytest attrs==19.1.0 # via pytest aws-xray-sdk==0.95 # via moto -backports-abc==0.5 backports.functools-lru-cache==1.5 # via cheroot backports.ssl_match_hostname==3.7.0.1 +backports_abc==0.5 bcrypt==3.1.6 # via paramiko boto3==1.9.132 boto==2.49.0 @@ -21,26 +21,28 @@ cachetools==3.1.0 # via google-auth certifi==2019.3.9 cffi==1.12.2 chardet==3.0.4 # via requests +cheetah3==3.1.0 cheroot==6.5.5 # via cherrypy cherrypy==17.4.1 click==7.0 clustershell==1.8.1 contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov croniter==0.3.29 cryptography==2.6.1 dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==3.7.2 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose enum34==1.1.6 future==0.17.1 # via python-jose +genshi==0.7.3 gitdb2==2.0.5 # via gitpython gitdb==0.6.4 gitpython==2.1.11 google-auth==1.6.3 # via kubernetes idna==2.8 +importlib-metadata==0.23 # via pluggy, pytest ipaddress==1.0.22 jaraco.functools==2.0 # via tempora jinja2==2.10.1 @@ -60,13 +62,14 @@ mock==2.0.0 # via moto more-itertools==5.0.0 moto==1.3.7 msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc +packaging==19.2 # via pytest paramiko==2.4.2 # via junos-eznc, ncclient, scp pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock, pylxd -pluggy==0.9.0 # via pytest +pbr==5.1.3 # via mock +pluggy==0.13.1 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -74,30 +77,24 @@ pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth pyasn1==0.4.5 pycparser==2.19 -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pylxd==2.2.9 +pycryptodome==3.8.1 ; sys_platform != "win32" pynacl==1.3.0 # via paramiko pyopenssl==19.0.0 +pyparsing==2.4.5 # via packaging pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 python-etcd==0.4.5 python-gnupg==0.4.4 python-jose==2.0.2 # via moto pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" -requests-toolbelt==0.9.1 # via pylxd -requests-unixsocket==0.1.5 # via pylxd requests==2.21.0 responses==0.10.6 # via moto rfc3987==1.3.8 @@ -107,23 +104,24 @@ salttesting==2017.6.1 scp==0.13.2 # via junos-eznc setproctitle==1.1.10 singledispatch==3.4.0.3 -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pylxd, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, packaging, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client smmap2==2.0.5 # via gitdb2 smmap==0.9.0 strict-rfc3339==0.7 tempora==1.14.1 # via portend timelib==0.2.4 tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests, requests-unixsocket +urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 vultr==1.0.1 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wrapt==1.11.1 # via aws-xray-sdk -ws4py==0.5.1 # via pylxd xmltodict==0.12.0 # via moto yamlordereddictloader==0.4.0 zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata # Passthrough dependencies from pkg/osx/req.txt pyobjc==5.1.2 diff --git a/requirements/static/py3.7/linux-crypto.txt b/requirements/static/py3.7/linux-crypto.txt new file mode 100644 index 000000000000..a376a05e786c --- /dev/null +++ b/requirements/static/py3.7/linux-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.7/linux-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.3 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.7/zeromq-centos-7.txt b/requirements/static/py3.7/linux.txt similarity index 73% rename from requirements/static/py3.7/zeromq-centos-7.txt rename to requirements/static/py3.7/linux.txt index 24264c5df0cd..89267b4cd90a 100644 --- a/requirements/static/py3.7/zeromq-centos-7.txt +++ b/requirements/static/py3.7/linux.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.7/zeromq-centos-7.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/centos-7.in +# pip-compile -o requirements/static/py3.7/linux.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/linux.in # apache-libcloud==2.0.0 argh==0.26.2 # via watchdog @@ -10,33 +10,34 @@ asn1crypto==0.24.0 # via cryptography atomicwrites==1.3.0 # via pytest attrs==19.1.0 # via pytest aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client bcrypt==3.1.6 # via paramiko boto3==1.9.132 boto==2.49.0 botocore==1.12.132 # via boto3, moto, s3transfer cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado +certifi==2019.3.9 cffi==1.12.2 chardet==3.0.4 # via requests +cheetah3==3.1.0 cheroot==6.5.4 # via cherrypy cherrypy==17.3.0 contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov croniter==0.3.29 cryptography==2.6.1 # via moto, paramiko, pyopenssl dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==3.7.2 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose future==0.17.1 # via python-jose +genshi==0.7.3 gitdb2==2.0.5 # via gitpython gitpython==2.1.11 google-auth==1.6.3 # via kubernetes +hgtools==8.1.1 idna==2.8 # via requests +importlib-metadata==0.23 # via pluggy, pytest ipaddress==1.0.22 # via kubernetes jaraco.functools==2.0 # via tempora jinja2==2.10.1 @@ -50,18 +51,19 @@ kazoo==2.6.1 keyring==5.7.1 kubernetes==3.0.0 lxml==4.3.3 # via junos-eznc, ncclient +mako==1.1.0 markupsafe==1.1.1 mock==2.0.0 # via moto more-itertools==5.0.0 moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 ncclient==0.6.4 # via junos-eznc netaddr==0.7.19 # via junos-eznc -paramiko==2.2.3 ; python_version >= "3.7" +packaging==19.2 # via pytest +paramiko==2.4.2 pathtools==0.1.2 # via watchdog pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest +pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -69,28 +71,25 @@ pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 +pycryptodome==3.8.1 ; sys_platform != "win32" pygit2==0.28.2 pyinotify==0.9.6 pynacl==1.3.0 # via paramiko pyopenssl==19.0.0 +pyparsing==2.4.5 # via packaging pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto python-etcd==0.4.5 python-gnupg==0.4.4 python-jose==2.0.2 # via moto pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" requests==2.21.0 responses==0.10.6 # via moto @@ -100,8 +99,8 @@ s3transfer==0.2.0 # via boto3 salttesting==2017.6.1 scp==0.13.2 # via junos-eznc setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +setuptools-scm==3.2.0 +six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, packaging, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, websocket-client smmap2==2.0.5 # via gitdb2 strict-rfc3339==0.7 tempora==1.14.1 # via portend @@ -110,8 +109,10 @@ tornado==4.5.3 ; python_version >= "3.4" urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wrapt==1.11.1 # via aws-xray-sdk xmltodict==0.12.0 # via moto zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py3.7/windows-crypto.txt b/requirements/static/py3.7/windows-crypto.txt new file mode 100644 index 000000000000..ee941ca2d08e --- /dev/null +++ b/requirements/static/py3.7/windows-crypto.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.7/windows-crypto.txt -v requirements/static/crypto.in +# +m2crypto==0.35.2 +pycryptodomex==3.9.0 +typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.7/zeromq-windows.txt b/requirements/static/py3.7/windows.txt similarity index 78% rename from requirements/static/py3.7/zeromq-windows.txt rename to requirements/static/py3.7/windows.txt index fdf1c6b74b93..0d4add87f335 100644 --- a/requirements/static/py3.7/zeromq-windows.txt +++ b/requirements/static/py3.7/windows.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.7/zeromq-windows.txt -v pkg/windows/req.txt pkg/windows/req_win.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/windows.in +# pip-compile -o requirements/static/py3.7/windows.txt -v pkg/windows/req.txt pkg/windows/req_win.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/windows.in # argh==0.26.2 # via watchdog asn1crypto==0.24.0 # via cryptography @@ -23,14 +23,13 @@ cheroot==6.5.5 # via cherrypy cherrypy==17.4.1 colorama==0.4.1 # via pytest contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov cryptography==2.6.1 dmidecode==0.9.0 dnspython==1.16.0 docker-pycreds==0.4.0 # via docker docker==2.7.0 docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose +ecdsa==0.13.3 # via python-jose enum34==1.1.6 future==0.17.1 # via python-jose gitdb2==2.0.5 # via gitpython @@ -38,6 +37,7 @@ gitdb==0.6.4 gitpython==2.1.10 google-auth==1.6.3 # via kubernetes idna==2.8 +importlib-metadata==0.23 # via pluggy, pytest ioloop==0.1a0 ipaddress==1.0.22 jaraco.functools==2.0 # via tempora @@ -56,11 +56,12 @@ mock==2.0.0 # via moto more-itertools==5.0.0 moto==1.3.7 msgpack-python==0.5.6 -msgpack==0.6.1 +msgpack==0.5.6 +packaging==19.2 # via pytest patch==1.16 pathtools==0.1.2 # via watchdog pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest +pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy psutil==5.6.1 py==1.8.0 # via pytest @@ -74,13 +75,12 @@ pycurl==7.43.0.2 pygit2==0.28.2 pymysql==0.9.3 pyopenssl==19.0.0 -pytest-cov==2.6.1 +pyparsing==2.4.5 # via packaging pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 +pytest-salt-runtests-bridge==2019.7.10 +pytest-salt==2019.12.18 +pytest-tempdir==2019.10.12 +pytest==4.6.6 python-dateutil==2.8.0 python-etcd==0.4.5 python-gnupg==0.4.4 @@ -89,7 +89,7 @@ pythonnet==2.3.0 pytz==2019.1 # via moto, tempora pyvmomi==6.7.1.2018.12 pywin32==224 -pyyaml==3.13 +pyyaml==5.1.2 pyzmq==18.0.1 ; python_version != "3.4" requests==2.21.0 responses==0.10.6 # via moto @@ -100,7 +100,7 @@ salttesting==2017.6.1 sed==0.3.1 setproctitle==1.1.10 singledispatch==3.4.0.3 -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client +six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, packaging, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client smmap2==2.0.5 # via gitdb2 smmap==0.9.0 strict-rfc3339==0.7 @@ -110,10 +110,12 @@ tornado==4.5.3 ; python_version >= "3.4" urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests virtualenv==16.4.3 watchdog==0.9.0 +wcwidth==0.1.7 # via pytest websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto +werkzeug==0.15.6 # via moto wheel==0.33.4 wmi==1.4.9 wrapt==1.11.1 # via aws-xray-sdk xmltodict==0.12.0 # via moto zc.lockfile==1.4 # via cherrypy +zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py3.7/zeromq-arch.txt b/requirements/static/py3.7/zeromq-arch.txt deleted file mode 100644 index a1cbbc6f9426..000000000000 --- a/requirements/static/py3.7/zeromq-arch.txt +++ /dev/null @@ -1,106 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.7/zeromq-arch.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/arch.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -keyring==5.7.1 -kubernetes==3.0.0 -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.7/zeromq-debian-10.txt b/requirements/static/py3.7/zeromq-debian-10.txt deleted file mode 100644 index 9a322593f24a..000000000000 --- a/requirements/static/py3.7/zeromq-debian-10.txt +++ /dev/null @@ -1,116 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.7/zeromq-debian-10.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-10.in -# -apache-libcloud==2.0.0 -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports.functools-lru-cache==1.5 # via cheroot -bcrypt==3.1.7 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -clustershell==1.8.1 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.4 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pylxd, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.2.3 -pbr==5.1.3 # via mock, pylxd -pluggy==0.11.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.1 -pyinotify==0.9.6 -pylxd==2.2.9 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.7.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.2 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto, pylxd -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests-toolbelt==0.9.1 # via pylxd -requests-unixsocket==0.1.5 # via pylxd -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pylxd, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.3 # via botocore, kubernetes, python-etcd, requests, requests-unixsocket -virtualenv==16.4.3 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -ws4py==0.5.1 # via pylxd -xmltodict==0.12.0 # via moto -yamlordereddictloader==0.4.0 -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.7/zeromq-debian-9.txt b/requirements/static/py3.7/zeromq-debian-9.txt deleted file mode 100644 index 5451b92b50c3..000000000000 --- a/requirements/static/py3.7/zeromq-debian-9.txt +++ /dev/null @@ -1,116 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.7/zeromq-debian-9.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/debian-9.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.2.3 ; python_version >= "3.7" -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.7/zeromq-fedora-29.txt b/requirements/static/py3.7/zeromq-fedora-29.txt deleted file mode 100644 index d6b958fa24c6..000000000000 --- a/requirements/static/py3.7/zeromq-fedora-29.txt +++ /dev/null @@ -1,116 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.7/zeromq-fedora-29.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/fedora-29.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.7/zeromq-fedora-30.txt b/requirements/static/py3.7/zeromq-fedora-30.txt deleted file mode 100644 index 503f447d43ee..000000000000 --- a/requirements/static/py3.7/zeromq-fedora-30.txt +++ /dev/null @@ -1,116 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.7/zeromq-fedora-30.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/fedora-30.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.7/zeromq-opensuse-leap-15.txt b/requirements/static/py3.7/zeromq-opensuse-leap-15.txt deleted file mode 100644 index d3f11ee00d00..000000000000 --- a/requirements/static/py3.7/zeromq-opensuse-leap-15.txt +++ /dev/null @@ -1,108 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.7/zeromq-opensuse-leap-15.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/opensuse-leap-15.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -hgtools==8.1.1 -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -keyring==5.7.1 -kubernetes==3.0.0 -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pyopenssl==19.0.0 -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -setproctitle==1.1.10 -setuptools-scm==3.2.0 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.7/zeromq-ubuntu-16.04.txt b/requirements/static/py3.7/zeromq-ubuntu-16.04.txt deleted file mode 100644 index 3d68419d043c..000000000000 --- a/requirements/static/py3.7/zeromq-ubuntu-16.04.txt +++ /dev/null @@ -1,116 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.7/zeromq-ubuntu-16.04.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/ubuntu-16.04.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.2.3 ; python_version >= "3.7" -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/py3.7/zeromq-ubuntu-18.04.txt b/requirements/static/py3.7/zeromq-ubuntu-18.04.txt deleted file mode 100644 index ffed29d402d8..000000000000 --- a/requirements/static/py3.7/zeromq-ubuntu-18.04.txt +++ /dev/null @@ -1,116 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.7/zeromq-ubuntu-18.04.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/ubuntu-18.04.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 # via tornado -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 # via kubernetes, requests, tornado -cffi==1.12.2 -chardet==3.0.4 # via requests -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -coverage==4.5.3 # via pytest-cov -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.2 # via python-jose -future==0.17.1 # via python-jose -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 # via requests -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 # via boto3, botocore -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -lxml==4.3.3 # via junos-eznc, ncclient -markupsafe==1.1.1 -mock==2.0.0 # via moto -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.6.1 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -paramiko==2.2.3 ; python_version >= "3.7" -pathtools==0.1.2 # via watchdog -pbr==5.1.3 # via mock -pluggy==0.9.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -# Next line explicitly commented out by pip-tools-compile because of the following regex: '^pycrypto==(.*)$' -# pycrypto==2.6.1 ; sys_platform != "win32" -pycryptodome==3.8.1 -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyserial==3.4 # via junos-eznc -pytest-cov==2.6.1 -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.1.30 -pytest-salt==2018.12.8 -pytest-tempdir==2018.8.11 -pytest-timeout==1.3.3 -pytest==4.4.1 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==3.13 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scp==0.13.2 # via junos-eznc -setproctitle==1.1.10 -singledispatch==3.4.0.3 # via tornado -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -tornado==4.5.3 ; python_version >= "3.4" -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.2 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy diff --git a/requirements/static/ubuntu-16.04.in b/requirements/static/ubuntu-16.04.in deleted file mode 100644 index bce0ebf4793d..000000000000 --- a/requirements/static/ubuntu-16.04.in +++ /dev/null @@ -1,42 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -croniter>=0.3.0,!=0.3.22 -dnspython -docker -futures>=2.0; python_version < '3.0' -GitPython -jsonschema<=2.6.0 -junos-eznc -jxmlease -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -paramiko==2.1.2; python_version < '3.7' -paramiko>=2.2.3; python_version >= '3.7' -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pyinotify -pygit2 -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -requests -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -watchdog diff --git a/requirements/static/ubuntu-18.04.in b/requirements/static/ubuntu-18.04.in deleted file mode 100644 index bce0ebf4793d..000000000000 --- a/requirements/static/ubuntu-18.04.in +++ /dev/null @@ -1,42 +0,0 @@ -# This is a compilation of requirements installed on salt-jenkins git.salt state run -apache-libcloud==2.0.0 -boto3 -boto>=2.46.0 -cffi -cherrypy==17.3.0 -croniter>=0.3.0,!=0.3.22 -dnspython -docker -futures>=2.0; python_version < '3.0' -GitPython -jsonschema<=2.6.0 -junos-eznc -jxmlease -keyring==5.7.1 -kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' -more-itertools==5.0.0 -moto -msgpack-python >= 0.4.2, != 0.5.5 -paramiko==2.1.2; python_version < '3.7' -paramiko>=2.2.3; python_version >= '3.7' -psutil -# Let's install cryptodome instead of pycrypto because of pycrypto's outstanding security issues -# PyCrypto, if pulled, will be removed from the generated static requirements -pycryptodome -pyinotify -pygit2 -pyopenssl -python-etcd>0.4.2 -python-gnupg -pyvmomi -requests -rfc3987 -salttesting==2017.6.1 -setproctitle -strict_rfc3339 -supervisor==3.3.5; python_version < '3' -timelib -tornado<5.0 -virtualenv -watchdog diff --git a/requirements/static/windows.in b/requirements/static/windows.in index 5e2cfdfd37bd..0e46bcfe2671 100644 --- a/requirements/static/windows.in +++ b/requirements/static/windows.in @@ -7,15 +7,19 @@ dnspython # We require docker < 3.0.0 because after that they also start locking their pywin32 requirement, actually # pypiwin32, which after version 223 it just makes pywin32 a dep and installs nothing else docker<3.0.0 -jsonschema<=2.6.0 +jsonschema keyring==5.7.1 kubernetes<4.0 -mock>=2.0.0; python_version < '3.6' +mock>=3.0.5; python_version < '3.6' more-itertools==5.0.0 moto<=1.3.7 patch pygit2 python-etcd>0.4.2 +msgpack-python >= 0.4.2, != 0.5.5 +psutil +pyopenssl +python-gnupg pyvmomi rfc3987 salttesting==2017.6.1 @@ -24,3 +28,12 @@ setproctitle strict_rfc3339 supervisor==3.3.5; python_version < '3' virtualenv +timelib +tornado<5.0 +wmi==1.4.9 + +# Available template libraries that can be used +Genshi +Cheetah3==3.1.0 +Mako +wempy; python_version <'3' diff --git a/requirements/tests.txt b/requirements/tests.txt index a80cddc34ad0..e69de29bb2d1 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -1,41 +0,0 @@ --r zeromq.txt --r dev.txt --r pytest.txt -apache-libcloud>=1.0.0 -boto>=2.32.1 -boto3>=1.2.1 -moto>=0.3.6 -docker; sys.platform != 'win32' -docker==2.7.0; sys.platform == 'win32' -virtualenv -setuptools>=30 -six>=1.10.0 -timelib -coverage -keyring==5.7.1 -python-gnupg -python-etcd==0.4.2 -GitPython -supervisor; python_version < '3' -kubernetes<4.0 -psutil -pyvmomi -setproctitle -cherrypy>=3.2.2,<18.0.0; python_version < '3.5' and sys.platform != 'win32' and sys.platform != 'darwin' -cherrypy>=3.2.2; python_version >= '3.5' and sys.platform != 'win32' and sys.platform != 'darwin' -pyinotify; sys.platform != 'win32' and sys.platform != 'darwin' -PyMySQL; sys.platform != 'win32' and sys.platform != 'darwin' -jsonschema -strict_rfc3339 -rfc3987 -pyOpenSSL -ioflo -dnspython -SaltTesting==2017.6.1 -junos-eznc -jxmlease -croniter -kazoo -ansible; sys.platform != 'win32' and sys.platform != 'darwin' and python_version >= '3.5' -ansible; sys.platform != 'win32' and sys.platform != 'darwin' and python_version == '2.7' -pylxd>=2.2.5; sys.platform != 'win32' and sys.platform != 'darwin' diff --git a/requirements/zeromq.txt b/requirements/zeromq.txt index 6e987f400d5f..609f09dad08f 100644 --- a/requirements/zeromq.txt +++ b/requirements/zeromq.txt @@ -1,7 +1,5 @@ -r base.txt +-r crypto.txt -# PyCrypto has issues on Windows, while pycryptodomex does not -pycrypto>=2.6.1; sys.platform != 'win32' -pycryptodomex; sys.platform == 'win32' 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' diff --git a/salt/_logging/__init__.py b/salt/_logging/__init__.py new file mode 100644 index 000000000000..ce33c32a9b79 --- /dev/null +++ b/salt/_logging/__init__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +''' + salt._logging + ~~~~~~~~~~~~~ + + This is salt's new logging setup. + As the name suggests, this is considered an internal API which can change without notice, + although, as best effort, we'll try not to break code relying on it. + + The ``salt._logging`` package should be imported as soon as possible since salt tweaks + the python's logging system. +''' + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import salt libs +from salt._logging.impl import * # pylint: disable=wildcard-import diff --git a/salt/_logging/handlers.py b/salt/_logging/handlers.py new file mode 100644 index 000000000000..65e2cc427051 --- /dev/null +++ b/salt/_logging/handlers.py @@ -0,0 +1,324 @@ +# -*- coding: utf-8 -*- +''' + salt._logging.handlers + ~~~~~~~~~~~~~~~~~~~~~~ + + Salt's logging handlers +''' + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals +import sys +import copy +import logging +import logging.handlers +from collections import deque + +# Import salt libs +from salt._logging.mixins import NewStyleClassMixin, ExcInfoOnLogLevelFormatMixin +from salt.ext.six.moves import queue # pylint: disable=import-error,no-name-in-module +#from salt.utils.versions import warn_until_date + +log = logging.getLogger(__name__) + + +class TemporaryLoggingHandler(logging.NullHandler): + ''' + This logging handler will store all the log records up to its maximum + queue size at which stage the first messages stored will be dropped. + + Should only be used as a temporary logging handler, while the logging + system is not fully configured. + + Once configured, pass any logging handlers that should have received the + initial log messages to the function + :func:`TemporaryLoggingHandler.sync_with_handlers` and all stored log + records will be dispatched to the provided handlers. + + .. versionadded:: 0.17.0 + ''' + + def __init__(self, level=logging.NOTSET, max_queue_size=10000): + #warn_until_date( + # '20220101', + # 'Please stop using \'{name}.TemporaryLoggingHandler\'. ' + # '\'{name}.TemporaryLoggingHandler\' will go away after ' + # '{{date}}.'.format(name=__name__) + #) + self.__max_queue_size = max_queue_size + super(TemporaryLoggingHandler, self).__init__(level=level) + self.__messages = deque(maxlen=max_queue_size) + + def handle(self, record): + self.acquire() + self.__messages.append(record) + self.release() + + def sync_with_handlers(self, handlers=()): + ''' + Sync the stored log records to the provided log handlers. + ''' + if not handlers: + return + + while self.__messages: + record = self.__messages.popleft() + for handler in handlers: + if handler.level > record.levelno: + # If the handler's level is higher than the log record one, + # it should not handle the log record + continue + handler.handle(record) + + +class StreamHandler(ExcInfoOnLogLevelFormatMixin, + logging.StreamHandler, + NewStyleClassMixin): + ''' + Stream handler which properly handles exc_info on a per handler basis + ''' + + +class FileHandler(ExcInfoOnLogLevelFormatMixin, + logging.FileHandler, + NewStyleClassMixin): + ''' + File handler which properly handles exc_info on a per handler basis + ''' + + +class SysLogHandler(ExcInfoOnLogLevelFormatMixin, + logging.handlers.SysLogHandler, + NewStyleClassMixin): + ''' + Syslog handler which properly handles exc_info on a per handler basis + ''' + def handleError(self, record): + ''' + Override the default error handling mechanism for py3 + Deal with syslog os errors when the log file does not exist + ''' + handled = False + if sys.stderr and sys.version_info >= (3, 5, 4): + exc_type, exc, exc_traceback = sys.exc_info() + try: + if exc_type.__name__ in 'FileNotFoundError': + sys.stderr.write( + '[WARNING ] The log_file does not exist. Logging not ' + 'setup correctly or syslog service not started.\n' + ) + handled = True + finally: + # 'del' recommended. See documentation of + # 'sys.exc_info()' for details. + del exc_type, exc, exc_traceback + + if not handled: + super(SysLogHandler, self).handleError(record) + + +class RotatingFileHandler(ExcInfoOnLogLevelFormatMixin, + logging.handlers.RotatingFileHandler, + NewStyleClassMixin): + ''' + Rotating file handler which properly handles exc_info on a per handler basis + ''' + def handleError(self, record): + ''' + Override the default error handling mechanism + + Deal with log file rotation errors due to log file in use + more softly. + ''' + handled = False + + # Can't use "salt.utils.platform.is_windows()" in this file + if (sys.platform.startswith('win') and + logging.raiseExceptions and + sys.stderr): # see Python issue 13807 + exc_type, exc, exc_traceback = sys.exc_info() + try: + # PermissionError is used since Python 3.3. + # OSError is used for previous versions of Python. + if exc_type.__name__ in ('PermissionError', 'OSError') and exc.winerror == 32: + if self.level <= logging.WARNING: + sys.stderr.write( + '[WARNING ] Unable to rotate the log file "{0}" ' + 'because it is in use\n'.format(self.baseFilename) + ) + handled = True + finally: + # 'del' recommended. See documentation of + # 'sys.exc_info()' for details. + del exc_type, exc, exc_traceback + + if not handled: + super(RotatingFileHandler, self).handleError(record) + + +class WatchedFileHandler(ExcInfoOnLogLevelFormatMixin, + logging.handlers.WatchedFileHandler, + NewStyleClassMixin): + ''' + Watched file handler which properly handles exc_info on a per handler basis + ''' + + +if sys.version_info < (3, 2): + class QueueHandler(ExcInfoOnLogLevelFormatMixin, logging.Handler, NewStyleClassMixin): + ''' + This handler sends events to a queue. Typically, it would be used together + with a multiprocessing Queue to centralise logging to file in one process + (in a multi-process application), so as to avoid file write contention + between processes. + + This code is new in Python 3.2, but this class can be copy pasted into + user code for use with earlier Python versions. + ''' + + def __init__(self, queue): + ''' + Initialise an instance, using the passed queue. + ''' + #warn_until_date( + # '20220101', + # 'Please stop using \'{name}.QueueHandler\' and instead ' + # 'use \'logging.handlers.QueueHandler\'. ' + # '\'{name}.QueueHandler\' will go away after ' + # '{{date}}.'.format(name=__name__) + #) + logging.Handler.__init__(self) + self.queue = queue + + def enqueue(self, record): + ''' + Enqueue a record. + + The base implementation uses put_nowait. You may want to override + this method if you want to use blocking, timeouts or custom queue + implementations. + ''' + try: + self.queue.put_nowait(record) + except queue.Full: + sys.stderr.write('[WARNING ] Message queue is full, ' + 'unable to write "{0}" to log'.format(record)) + + def prepare(self, record): + ''' + Prepares a record for queuing. The object returned by this method is + enqueued. + The base implementation formats the record to merge the message + and arguments, and removes unpickleable items from the record + in-place. + You might want to override this method if you want to convert + the record to a dict or JSON string, or send a modified copy + of the record while leaving the original intact. + ''' + # The format operation gets traceback text into record.exc_text + # (if there's exception data), and also returns the formatted + # message. We can then use this to replace the original + # msg + args, as these might be unpickleable. We also zap the + # exc_info and exc_text attributes, as they are no longer + # needed and, if not None, will typically not be pickleable. + msg = self.format(record) + # bpo-35726: make copy of record to avoid affecting other handlers in the chain. + record = copy.copy(record) + record.message = msg + record.msg = msg + record.args = None + record.exc_info = None + record.exc_text = None + return record + + def emit(self, record): + ''' + Emit a record. + + Writes the LogRecord to the queue, preparing it for pickling first. + ''' + try: + self.enqueue(self.prepare(record)) + except Exception: # pylint: disable=broad-except + self.handleError(record) +elif sys.version_info < (3, 7): + # On python versions lower than 3.7, we sill subclass and overwrite prepare to include the fix for: + # https://bugs.python.org/issue35726 + class QueueHandler(ExcInfoOnLogLevelFormatMixin, logging.handlers.QueueHandler): # pylint: disable=no-member,inconsistent-mro + + def __init__(self, queue): + super(QueueHandler, self).__init__(queue) + #warn_until_date( + # '20220101', + # 'Please stop using \'{name}.QueueHandler\' and instead ' + # 'use \'logging.handlers.QueueHandler\'. ' + # '\'{name}.QueueHandler\' will go away after ' + # '{{date}}.'.format(name=__name__) + #) + + def enqueue(self, record): + ''' + Enqueue a record. + + The base implementation uses put_nowait. You may want to override + this method if you want to use blocking, timeouts or custom queue + implementations. + ''' + try: + self.queue.put_nowait(record) + except queue.Full: + sys.stderr.write('[WARNING ] Message queue is full, ' + 'unable to write "{}" to log.\n'.format(record)) + + def prepare(self, record): + ''' + Prepares a record for queuing. The object returned by this method is + enqueued. + The base implementation formats the record to merge the message + and arguments, and removes unpickleable items from the record + in-place. + You might want to override this method if you want to convert + the record to a dict or JSON string, or send a modified copy + of the record while leaving the original intact. + ''' + # The format operation gets traceback text into record.exc_text + # (if there's exception data), and also returns the formatted + # message. We can then use this to replace the original + # msg + args, as these might be unpickleable. We also zap the + # exc_info and exc_text attributes, as they are no longer + # needed and, if not None, will typically not be pickleable. + msg = self.format(record) + # bpo-35726: make copy of record to avoid affecting other handlers in the chain. + record = copy.copy(record) + record.message = msg + record.msg = msg + record.args = None + record.exc_info = None + record.exc_text = None + return record +else: + class QueueHandler(ExcInfoOnLogLevelFormatMixin, logging.handlers.QueueHandler): # pylint: disable=no-member,inconsistent-mro + + def __init__(self, queue): + super(QueueHandler, self).__init__(queue) + #warn_until_date( + # '20220101', + # 'Please stop using \'{name}.QueueHandler\' and instead ' + # 'use \'logging.handlers.QueueHandler\'. ' + # '\'{name}.QueueHandler\' will go away after ' + # '{{date}}.'.format(name=__name__) + #) + + def enqueue(self, record): + ''' + Enqueue a record. + + The base implementation uses put_nowait. You may want to override + this method if you want to use blocking, timeouts or custom queue + implementations. + ''' + try: + self.queue.put_nowait(record) + except queue.Full: + sys.stderr.write('[WARNING ] Message queue is full, ' + 'unable to write "{0}" to log.\n'.format(record)) diff --git a/salt/_logging/impl.py b/salt/_logging/impl.py new file mode 100644 index 000000000000..347259bcf506 --- /dev/null +++ b/salt/_logging/impl.py @@ -0,0 +1,444 @@ +# -*- coding: utf-8 -*- +''' + salt._logging.impl + ~~~~~~~~~~~~~~~~~~ + + Salt's logging implementation classes/functionality +''' + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals +import re +import sys +import types +import logging + +# Let's define these custom logging levels before importing the salt._logging.mixins +# since they will be used there +PROFILE = logging.PROFILE = 15 +TRACE = logging.TRACE = 5 +GARBAGE = logging.GARBAGE = 1 +QUIET = logging.QUIET = 1000 + +# Import Salt libs +from salt._logging.handlers import StreamHandler +#from salt._logging.handlers import SysLogHandler +#from salt._logging.handlers import RotatingFileHandler +#from salt._logging.handlers import WatchedFileHandler +from salt._logging.handlers import TemporaryLoggingHandler +from salt._logging.mixins import LoggingMixinMeta +from salt._logging.mixins import NewStyleClassMixin +from salt.exceptions import LoggingRuntimeError +from salt.utils.ctx import RequestContext +from salt.utils.textformat import TextFormat + +# Import 3rd-party libs +import salt.ext.six as six +#from salt.ext.six.moves.urllib.parse import urlparse # pylint: disable=import-error,no-name-in-module + +LOG_LEVELS = { + 'all': logging.NOTSET, + 'debug': logging.DEBUG, + 'error': logging.ERROR, + 'critical': logging.CRITICAL, + 'garbage': GARBAGE, + 'info': logging.INFO, + 'profile': PROFILE, + 'quiet': QUIET, + 'trace': TRACE, + 'warning': logging.WARNING, +} + +LOG_VALUES_TO_LEVELS = dict((v, k) for (k, v) in LOG_LEVELS.items()) + +LOG_COLORS = { + 'levels': { + 'QUIET': TextFormat('reset'), + 'CRITICAL': TextFormat('bold', 'red'), + 'ERROR': TextFormat('bold', 'red'), + 'WARNING': TextFormat('bold', 'yellow'), + 'INFO': TextFormat('bold', 'green'), + 'PROFILE': TextFormat('bold', 'cyan'), + 'DEBUG': TextFormat('bold', 'cyan'), + 'TRACE': TextFormat('bold', 'magenta'), + 'GARBAGE': TextFormat('bold', 'blue'), + 'NOTSET': TextFormat('reset'), + 'SUBDEBUG': TextFormat('bold', 'cyan'), # used by multiprocessing.log_to_stderr() + 'SUBWARNING': TextFormat('bold', 'yellow'), # used by multiprocessing.log_to_stderr() + }, + 'msgs': { + 'QUIET': TextFormat('reset'), + 'CRITICAL': TextFormat('bold', 'red'), + 'ERROR': TextFormat('red'), + 'WARNING': TextFormat('yellow'), + 'INFO': TextFormat('green'), + 'PROFILE': TextFormat('bold', 'cyan'), + 'DEBUG': TextFormat('cyan'), + 'TRACE': TextFormat('magenta'), + 'GARBAGE': TextFormat('blue'), + 'NOTSET': TextFormat('reset'), + 'SUBDEBUG': TextFormat('bold', 'cyan'), # used by multiprocessing.log_to_stderr() + 'SUBWARNING': TextFormat('bold', 'yellow'), # used by multiprocessing.log_to_stderr() + }, + 'name': TextFormat('bold', 'green'), + 'process': TextFormat('bold', 'blue'), +} + +# Make a list of log level names sorted by log level +SORTED_LEVEL_NAMES = [ + l[0] for l in sorted(six.iteritems(LOG_LEVELS), key=lambda x: x[1]) +] + +MODNAME_PATTERN = re.compile(r'(?P%%\(name\)(?:\-(?P[\d]+))?s)') + + +# ----- REMOVE ME ON REFACTOR COMPLETE ------------------------------------------------------------------------------> +class __NullLoggingHandler(TemporaryLoggingHandler): + ''' + This class exists just to better identify which temporary logging + handler is being used for what. + ''' + + +class __StoreLoggingHandler(TemporaryLoggingHandler): + ''' + This class exists just to better identify which temporary logging + handler is being used for what. + ''' + + +# Store a reference to the temporary queue logging handler +LOGGING_NULL_HANDLER = __NullLoggingHandler(logging.WARNING) + +# Store a reference to the temporary console logger +LOGGING_TEMP_HANDLER = StreamHandler(sys.stderr) + +# Store a reference to the "storing" logging handler +LOGGING_STORE_HANDLER = __StoreLoggingHandler() +# <---- REMOVE ME ON REFACTOR COMPLETE ------------------------------------------------------------------------------- + + +class SaltLogRecord(logging.LogRecord): + def __init__(self, *args, **kwargs): + logging.LogRecord.__init__(self, *args, **kwargs) + self.bracketname = '[{:<17}]'.format(str(self.name)) + self.bracketlevel = '[{:<8}]'.format(str(self.levelname)) + self.bracketprocess = '[{:>5}]'.format(str(self.process)) + + +class SaltColorLogRecord(SaltLogRecord): + def __init__(self, *args, **kwargs): + SaltLogRecord.__init__(self, *args, **kwargs) + + reset = TextFormat('reset') + clevel = LOG_COLORS['levels'].get(self.levelname, reset) + cmsg = LOG_COLORS['msgs'].get(self.levelname, reset) + + self.colorname = '{}[{:<17}]{}'.format(LOG_COLORS['name'], + self.name, + reset) + self.colorlevel = '{}[{:<8}]{}'.format(clevel, + self.levelname, + reset) + self.colorprocess = '{}[{:>5}]{}'.format(LOG_COLORS['process'], + self.process, + reset) + self.colormsg = '{}{}{}'.format(cmsg, self.getMessage(), reset) + + +def get_log_record_factory(): + ''' + Get the logging log record factory + ''' + try: + return get_log_record_factory.__factory__ + except AttributeError: + return + + +def set_log_record_factory(factory): + ''' + Set the logging log record factory + ''' + get_log_record_factory.__factory__ = factory + if not six.PY2: + logging.setLogRecordFactory(factory) + + +set_log_record_factory(SaltLogRecord) + + +# Store an instance of the current logging logger class +LOGGING_LOGGER_CLASS = logging.getLoggerClass() + + +class SaltLoggingClass(six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS, NewStyleClassMixin)): + def __new__(cls, *args): + ''' + We override `__new__` in our logging logger class in order to provide + some additional features like expand the module name padding if length + is being used, and also some Unicode fixes. + + This code overhead will only be executed when the class is + instantiated, i.e.: + + logging.getLogger(__name__) + + ''' + instance = super(SaltLoggingClass, cls).__new__(cls) + + try: + max_logger_length = len(max( + list(logging.Logger.manager.loggerDict), key=len + )) + for handler in logging.root.handlers: + if handler in (LOGGING_NULL_HANDLER, + LOGGING_STORE_HANDLER, + LOGGING_TEMP_HANDLER): + continue + + formatter = handler.formatter + if not formatter: + continue + + if not handler.lock: + handler.createLock() + handler.acquire() + + fmt = formatter._fmt.replace('%', '%%') + + match = MODNAME_PATTERN.search(fmt) + if not match: + # Not matched. Release handler and return. + handler.release() + return instance + + if 'digits' not in match.groupdict(): + # No digits group. Release handler and return. + handler.release() + return instance + + digits = match.group('digits') + if not digits or not (digits and digits.isdigit()): + # No valid digits. Release handler and return. + handler.release() + return instance + + if int(digits) < max_logger_length: + # Formatter digits value is lower than current max, update. + fmt = fmt.replace(match.group('name'), '%%(name)-%ds') + formatter = logging.Formatter( + fmt % max_logger_length, + datefmt=formatter.datefmt + ) + handler.setFormatter(formatter) + handler.release() + except ValueError: + # There are no registered loggers yet + pass + return instance + + def _log(self, level, msg, args, exc_info=None, + extra=None, # pylint: disable=arguments-differ + stack_info=False, + stacklevel=1, + exc_info_on_loglevel=None): + if extra is None: + extra = {} + + # pylint: disable=no-member + current_jid = RequestContext.current.get('data', {}).get('jid', None) + log_fmt_jid = RequestContext.current.get('opts', {}).get('log_fmt_jid', None) + # pylint: enable=no-member + + if current_jid is not None: + extra['jid'] = current_jid + + if log_fmt_jid is not None: + extra['log_fmt_jid'] = log_fmt_jid + + # If both exc_info and exc_info_on_loglevel are both passed, let's fail + if exc_info and exc_info_on_loglevel: + raise LoggingRuntimeError( + 'Only one of \'exc_info\' and \'exc_info_on_loglevel\' is ' + 'permitted' + ) + if exc_info_on_loglevel is not None: + if isinstance(exc_info_on_loglevel, six.string_types): + exc_info_on_loglevel = LOG_LEVELS.get(exc_info_on_loglevel, + logging.ERROR) + elif not isinstance(exc_info_on_loglevel, int): + raise RuntimeError( + 'The value of \'exc_info_on_loglevel\' needs to be a ' + 'logging level or a logging level name, not \'{}\'' + .format(exc_info_on_loglevel) + ) + if extra is None: + extra = {'exc_info_on_loglevel': exc_info_on_loglevel} + else: + extra['exc_info_on_loglevel'] = exc_info_on_loglevel + + if sys.version_info < (3,): + LOGGING_LOGGER_CLASS._log( + self, level, msg, args, exc_info=exc_info, extra=extra + ) + elif sys.version_info < (3, 8): + LOGGING_LOGGER_CLASS._log( + self, level, msg, args, exc_info=exc_info, extra=extra, + stack_info=stack_info + ) + else: + LOGGING_LOGGER_CLASS._log( + self, level, msg, args, exc_info=exc_info, extra=extra, + stack_info=stack_info, stacklevel=stacklevel + ) + + def makeRecord(self, name, level, fn, lno, msg, args, exc_info, + func=None, extra=None, sinfo=None): + # Let's remove exc_info_on_loglevel from extra + exc_info_on_loglevel = extra.pop('exc_info_on_loglevel') + + jid = extra.pop('jid', '') + if jid: + log_fmt_jid = extra.pop('log_fmt_jid') + jid = log_fmt_jid % {'jid': jid} + + if not extra: + # If nothing else is in extra, make it None + extra = None + + # Let's try to make every logging message unicode + try: + salt_system_encoding = __salt_system_encoding__ + if salt_system_encoding == 'ascii': + # Encoding detection most likely failed, let's use the utf-8 + # value which we defaulted before __salt_system_encoding__ was + # implemented + salt_system_encoding = 'utf-8' + except NameError: + salt_system_encoding = 'utf-8' + + if isinstance(msg, six.string_types) and not isinstance(msg, six.text_type): + try: + _msg = msg.decode(salt_system_encoding, 'replace') + except UnicodeDecodeError: + _msg = msg.decode(salt_system_encoding, 'ignore') + else: + _msg = msg + + _args = [] + for item in args: + if isinstance(item, six.string_types) \ + and not isinstance(item, six.text_type): + try: + _args.append(item.decode(salt_system_encoding, 'replace')) + except UnicodeDecodeError: + _args.append(item.decode(salt_system_encoding, 'ignore')) + else: + _args.append(item) + _args = tuple(_args) + + if six.PY2: + # Recreate what's done for Py >= 3.5 + _log_record_factory = get_log_record_factory() + logrecord = _log_record_factory( + name, + level, + fn, + lno, + _msg, + _args, + exc_info, + func) + + if extra is not None: + for key in extra: + if (key in ['message', 'asctime']) or (key in logrecord.__dict__): + raise KeyError( + 'Attempt to overwrite \'{}\' in LogRecord'.format(key) + ) + logrecord.__dict__[key] = extra[key] + else: + logrecord = LOGGING_LOGGER_CLASS.makeRecord( + self, + name, + level, + fn, + lno, + _msg, + _args, + exc_info, + func, + sinfo) + + if exc_info_on_loglevel is not None: + # Let's add some custom attributes to the LogRecord class in order + # to include the exc_info on a per handler basis. This will allow + # showing tracebacks on logfiles but not on console if the logfile + # handler is enabled for the log level "exc_info_on_loglevel" and + # console handler is not. + logrecord.exc_info_on_loglevel_instance = sys.exc_info() + logrecord.exc_info_on_loglevel_formatted = None + + logrecord.exc_info_on_loglevel = exc_info_on_loglevel + logrecord.jid = jid + return logrecord + + +# Override the python's logging logger class as soon as this module is imported +if logging.getLoggerClass() is not SaltLoggingClass: + + logging.setLoggerClass(SaltLoggingClass) + logging.addLevelName(QUIET, 'QUIET') + logging.addLevelName(PROFILE, 'PROFILE') + logging.addLevelName(TRACE, 'TRACE') + logging.addLevelName(GARBAGE, 'GARBAGE') + + # ----- REMOVE ON REFACTORING COMPLETE --------------------------------------------------------------------------> + if not logging.root.handlers: + # No configuration to the logging system has been done so far. + # Set the root logger at the lowest level possible + logging.root.setLevel(GARBAGE) + + # Add a Null logging handler until logging is configured(will be + # removed at a later stage) so we stop getting: + # No handlers could be found for logger 'foo' + logging.root.addHandler(LOGGING_NULL_HANDLER) + + # Add the queue logging handler so we can later sync all message records + # with the additional logging handlers + logging.root.addHandler(LOGGING_STORE_HANDLER) + # <---- REMOVE ON REFACTORING COMPLETE --------------------------------------------------------------------------- + + +# Now that we defined the default logging logger class, we can instantiate our logger +# DO NOT MOVE THIS +log = logging.getLogger(__name__) + + +def __get_exposed_module_attributes(): + ''' + This function just ``dir()``'s this module and filters out any functions + or variables which should not be available when wildcard importing it + ''' + exposed = [] + module = sys.modules[__name__] + for name in dir(module): + if name.startswith('_'): + continue + obj = getattr(module, name) + if not isinstance(obj, types.FunctionType): + if name.startswith(('LOG_', 'SORTED_')): + exposed.append(name) + continue + if obj.__module__ != __name__: + continue + exposed.append(name) + return exposed + + +# Define what can be imported by wildcard imports +__all__ = __get_exposed_module_attributes() + +# We're done with the function, nuke it +del __get_exposed_module_attributes diff --git a/salt/_logging/mixins.py b/salt/_logging/mixins.py new file mode 100644 index 000000000000..82d673dd22e6 --- /dev/null +++ b/salt/_logging/mixins.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +''' + salt._logging.mixins + ~~~~~~~~~~~~~~~~~~~~ + + Logging related mix-ins +''' + +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals +import sys +import logging + + +class NewStyleClassMixin(object): + ''' + Simple new style class to make pylint shut up! + This is required because SaltLoggingClass can't subclass object directly: + + 'Cannot create a consistent method resolution order (MRO) for bases' + ''' + + +class LoggingProfileMixin(object): + ''' + Simple mix-in class to add a trace method to python's logging. + ''' + + def profile(self, msg, *args, **kwargs): + self.log(getattr(logging, 'PROFILE', 15), msg, *args, **kwargs) + + +class LoggingTraceMixin(object): + ''' + Simple mix-in class to add a trace method to python's logging. + ''' + + def trace(self, msg, *args, **kwargs): + self.log(getattr(logging, 'TRACE', 5), msg, *args, **kwargs) + + +class LoggingGarbageMixin(object): + ''' + Simple mix-in class to add a garbage method to python's logging. + ''' + + def garbage(self, msg, *args, **kwargs): + self.log(getattr(logging, 'GARBAGE', 5), msg, *args, **kwargs) + + +class LoggingMixinMeta(type): + ''' + This class is called whenever a new instance of ``SaltLoggingClass`` is + created. + + What this class does is check if any of the bases have a `trace()` or a + `garbage()` method defined, if they don't we add the respective mix-ins to + the bases. + ''' + def __new__(mcs, name, bases, attrs): + include_profile = include_trace = include_garbage = True + bases = list(bases) + if name == 'SaltLoggingClass': + for base in bases: + if hasattr(base, 'trace'): + include_trace = False + if hasattr(base, 'garbage'): + include_garbage = False + if include_profile: + bases.append(LoggingProfileMixin) + if include_trace: + bases.append(LoggingTraceMixin) + if include_garbage: + bases.append(LoggingGarbageMixin) + return super(LoggingMixinMeta, mcs).__new__(mcs, name, tuple(bases), attrs) + + +class ExcInfoOnLogLevelFormatMixin(object): + ''' + Logging handler class mixin to properly handle including exc_info on a per logging handler basis + ''' + + def format(self, record): + ''' + Format the log record to include exc_info if the handler is enabled for a specific log level + ''' + formatted_record = super(ExcInfoOnLogLevelFormatMixin, self).format(record) + exc_info_on_loglevel = getattr(record, 'exc_info_on_loglevel', None) + exc_info_on_loglevel_formatted = getattr(record, 'exc_info_on_loglevel_formatted', None) + if exc_info_on_loglevel is None and exc_info_on_loglevel_formatted is None: + return formatted_record + + # If we reached this far it means the log record was created with exc_info_on_loglevel + # If this specific handler is enabled for that record, then we should format it to + # include the exc_info details + if self.level > exc_info_on_loglevel: + # This handler is not enabled for the desired exc_info_on_loglevel, don't include exc_info + return formatted_record + + # If we reached this far it means we should include exc_info + if not record.exc_info_on_loglevel_instance and not exc_info_on_loglevel_formatted: + # This should actually never occur + return formatted_record + + if record.exc_info_on_loglevel_formatted is None: + # Let's cache the formatted exception to avoid recurring conversions and formatting calls + if self.formatter is None: # pylint: disable=access-member-before-definition + self.formatter = logging._defaultFormatter + record.exc_info_on_loglevel_formatted = self.formatter.formatException( + record.exc_info_on_loglevel_instance + ) + + # Let's format the record to include exc_info just like python's logging formatted does + if formatted_record[-1:] != '\n': + formatted_record += '\n' + + try: + formatted_record += record.exc_info_on_loglevel_formatted + except UnicodeError: + # According to the standard library logging formatter comments: + # + # Sometimes filenames have non-ASCII chars, which can lead + # to errors when s is Unicode and record.exc_text is str + # See issue 8924. + # We also use replace for when there are multiple + # encodings, e.g. UTF-8 for the filesystem and latin-1 + # for a script. See issue 13232. + formatted_record += record.exc_info_on_loglevel_formatted.decode( + sys.getfilesystemencoding(), + 'replace' + ) + # Reset the record.exc_info_on_loglevel_instance because it might need + # to "travel" through a multiprocessing process and it might contain + # data which is not pickle'able + record.exc_info_on_loglevel_instance = None + return formatted_record diff --git a/salt/auth/__init__.py b/salt/auth/__init__.py index d9b1a64c0c17..626665b7a127 100644 --- a/salt/auth/__init__.py +++ b/salt/auth/__init__.py @@ -484,199 +484,6 @@ def check_authentication(self, load, auth_type, key=None, show_username=False): return ret -class Authorize(object): - ''' - The authorization engine used by EAUTH - ''' - def __init__(self, opts, load, loadauth=None): - salt.utils.versions.warn_until( - 'Neon', - 'The \'Authorize\' class has been deprecated. Please use the ' - '\'LoadAuth\', \'Reslover\', or \'AuthUser\' classes instead. ' - 'Support for the \'Authorze\' class will be removed in Salt ' - '{version}.' - ) - self.opts = salt.config.master_config(opts['conf_file']) - self.load = load - self.ckminions = salt.utils.minions.CkMinions(opts) - if loadauth is None: - self.loadauth = LoadAuth(opts) - else: - self.loadauth = loadauth - - @property - def auth_data(self): - ''' - Gather and create the authorization data sets - - We're looking at several constructs here. - - Standard eauth: allow jsmith to auth via pam, and execute any command - on server web1 - external_auth: - pam: - jsmith: - - web1: - - .* - - Django eauth: Import the django library, dynamically load the Django - model called 'model'. That model returns a data structure that - matches the above for standard eauth. This is what determines - who can do what to which machines - - django: - ^model: - - - Active Directory Extended: - - Users in the AD group 'webadmins' can run any command on server1 - Users in the AD group 'webadmins' can run test.ping and service.restart - on machines that have a computer object in the AD 'webservers' OU - Users in the AD group 'webadmins' can run commands defined in the - custom attribute (custom attribute not implemented yet, this is for - future use) - ldap: - webadmins%: - - server1: - - .* - - ldap(OU=webservers,dc=int,dc=bigcompany,dc=com): - - test.ping - - service.restart - - ldap(OU=Domain Controllers,dc=int,dc=bigcompany,dc=com): - - allowed_fn_list_attribute^ - ''' - auth_data = self.opts['external_auth'] - merge_lists = self.opts['pillar_merge_lists'] - - if 'django' in auth_data and '^model' in auth_data['django']: - auth_from_django = salt.auth.django.retrieve_auth_entries() - auth_data = salt.utils.dictupdate.merge(auth_data, - auth_from_django, - strategy='list', - merge_lists=merge_lists) - - if 'ldap' in auth_data and __opts__.get('auth.ldap.activedirectory', False): - auth_data['ldap'] = salt.auth.ldap.__expand_ldap_entries(auth_data['ldap']) - log.debug(auth_data['ldap']) - - #for auth_back in self.opts.get('external_auth_sources', []): - # fstr = '{0}.perms'.format(auth_back) - # if fstr in self.loadauth.auth: - # auth_data.append(getattr(self.loadauth.auth)()) - return auth_data - - def token(self, adata, load): - ''' - Determine if token auth is valid and yield the adata - ''' - try: - token = self.loadauth.get_tok(load['token']) - except Exception as exc: - log.error('Exception occurred when generating auth token: %s', exc) - yield {} - if not token: - log.warning('Authentication failure of type "token" occurred.') - yield {} - for sub_auth in adata: - for sub_adata in adata: - if token['eauth'] not in adata: - continue - if not ((token['name'] in adata[token['eauth']]) | - ('*' in adata[token['eauth']])): - continue - yield {'sub_auth': sub_auth, 'token': token} - yield {} - - def eauth(self, adata, load): - ''' - Determine if the given eauth is valid and yield the adata - ''' - for sub_auth in [adata]: - if load['eauth'] not in sub_auth: - continue - try: - name = self.loadauth.load_name(load) - if not ((name in sub_auth[load['eauth']]) | - ('*' in sub_auth[load['eauth']])): - continue - if not self.loadauth.time_auth(load): - continue - except Exception as exc: - log.error('Exception occurred while authenticating: %s', exc) - continue - yield {'sub_auth': sub_auth, 'name': name} - yield {} - - def rights_check(self, form, sub_auth, name, load, eauth=None): - ''' - Read in the access system to determine if the validated user has - requested rights - ''' - if load.get('eauth'): - sub_auth = sub_auth[load['eauth']] - good = self.ckminions.any_auth( - form, - sub_auth[name] if name in sub_auth else sub_auth['*'], - load.get('fun', None), - load.get('arg', None), - load.get('tgt', None), - load.get('tgt_type', 'glob')) - - # Handle possible return of dict data structure from any_auth call to - # avoid a stacktrace. As mentioned in PR #43181, this entire class is - # dead code and is marked for removal in Salt Neon. But until then, we - # should handle the dict return, which is an error and should return - # False until this class is removed. - if isinstance(good, dict): - return False - - if not good: - # Accept find_job so the CLI will function cleanly - if load.get('fun', '') != 'saltutil.find_job': - return good - return good - - def rights(self, form, load): - ''' - Determine what type of authentication is being requested and pass - authorization - - Note: this will check that the user has at least one right that will let - the user execute "load", this does not deal with conflicting rules - ''' - - adata = self.auth_data - good = False - if load.get('token', False): - for sub_auth in self.token(self.auth_data, load): - if sub_auth: - if self.rights_check( - form, - self.auth_data[sub_auth['token']['eauth']], - sub_auth['token']['name'], - load, - sub_auth['token']['eauth']): - return True - log.warning( - 'Authentication failure of type "token" occurred.' - ) - elif load.get('eauth'): - for sub_auth in self.eauth(self.auth_data, load): - if sub_auth: - if self.rights_check( - form, - sub_auth['sub_auth'], - sub_auth['name'], - load, - load['eauth']): - return True - log.warning( - 'Authentication failure of type "eauth" occurred.' - ) - return False - - class Resolver(object): ''' The class used to resolve options for the command line and for generic @@ -687,12 +494,14 @@ def __init__(self, opts): self.auth = salt.loader.auth(opts) def _send_token_request(self, load): - master_uri = 'tcp://' + salt.utils.zeromq.ip_bracket(self.opts['interface']) + \ - ':' + six.text_type(self.opts['ret_port']) - channel = salt.transport.client.ReqChannel.factory(self.opts, - crypt='clear', - master_uri=master_uri) - return channel.send(load) + master_uri = 'tcp://{}:{}'.format( + salt.utils.zeromq.ip_bracket(self.opts['interface']), + six.text_type(self.opts['ret_port']) + ) + with salt.transport.client.ReqChannel.factory(self.opts, + crypt='clear', + master_uri=master_uri) as channel: + return channel.send(load) def cli(self, eauth): ''' diff --git a/salt/beacons/__init__.py b/salt/beacons/__init__.py index 458f3f12bc00..26e90d36510a 100644 --- a/salt/beacons/__init__.py +++ b/salt/beacons/__init__.py @@ -233,9 +233,9 @@ def list_beacons(self, beacons = self._get_beacons(include_pillar, include_opts) # Fire the complete event back along with the list of beacons - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': True, 'beacons': beacons}, - tag='/salt/minion/minion_beacons_list_complete') + with salt.utils.event.get_event('minion', opts=self.opts) as evt: + evt.fire_event({'complete': True, 'beacons': beacons}, + tag='/salt/minion/minion_beacons_list_complete') return True @@ -247,9 +247,9 @@ def list_available_beacons(self): for _beacon in self.beacons if '.beacon' in _beacon] # Fire the complete event back along with the list of beacons - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': True, 'beacons': _beacons}, - tag='/salt/minion/minion_beacons_list_available_complete') + with salt.utils.event.get_event('minion', opts=self.opts) as evt: + evt.fire_event({'complete': True, 'beacons': _beacons}, + tag='/salt/minion/minion_beacons_list_available_complete') return True @@ -270,11 +270,11 @@ def validate_beacon(self, name, beacon_data): valid = True # Fire the complete event back along with the list of beacons - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': True, - 'vcomment': vcomment, - 'valid': valid}, - tag='/salt/minion/minion_beacon_validation_complete') + with salt.utils.event.get_event('minion', opts=self.opts) as evt: + evt.fire_event({'complete': True, + 'vcomment': vcomment, + 'valid': valid}, + tag='/salt/minion/minion_beacon_validation_complete') return True @@ -300,10 +300,10 @@ def add_beacon(self, name, beacon_data): self.opts['beacons'].update(data) # Fire the complete event back along with updated list of beacons - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': complete, 'comment': comment, - 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacon_add_complete') + with salt.utils.event.get_event('minion', opts=self.opts) as evt: + evt.fire_event({'complete': complete, 'comment': comment, + 'beacons': self.opts['beacons']}, + tag='/salt/minion/minion_beacon_add_complete') return True @@ -326,10 +326,10 @@ def modify_beacon(self, name, beacon_data): self.opts['beacons'].update(data) # Fire the complete event back along with updated list of beacons - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': complete, 'comment': comment, - 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacon_modify_complete') + with salt.utils.event.get_event('minion', opts=self.opts) as evt: + evt.fire_event({'complete': complete, 'comment': comment, + 'beacons': self.opts['beacons']}, + tag='/salt/minion/minion_beacon_modify_complete') return True def delete_beacon(self, name): @@ -350,10 +350,10 @@ def delete_beacon(self, name): complete = True # Fire the complete event back along with updated list of beacons - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': complete, 'comment': comment, - 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacon_delete_complete') + with salt.utils.event.get_event('minion', opts=self.opts) as evt: + evt.fire_event({'complete': complete, 'comment': comment, + 'beacons': self.opts['beacons']}, + tag='/salt/minion/minion_beacon_delete_complete') return True @@ -365,9 +365,9 @@ def enable_beacons(self): self.opts['beacons']['enabled'] = True # Fire the complete event back along with updated list of beacons - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': True, 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacons_enabled_complete') + with salt.utils.event.get_event('minion', opts=self.opts) as evt: + evt.fire_event({'complete': True, 'beacons': self.opts['beacons']}, + tag='/salt/minion/minion_beacons_enabled_complete') return True @@ -379,9 +379,9 @@ def disable_beacons(self): self.opts['beacons']['enabled'] = False # Fire the complete event back along with updated list of beacons - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': True, 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacons_disabled_complete') + with salt.utils.event.get_event('minion', opts=self.opts) as evt: + evt.fire_event({'complete': True, 'beacons': self.opts['beacons']}, + tag='/salt/minion/minion_beacons_disabled_complete') return True @@ -400,10 +400,10 @@ def enable_beacon(self, name): complete = True # Fire the complete event back along with updated list of beacons - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': complete, 'comment': comment, - 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacon_enabled_complete') + with salt.utils.event.get_event('minion', opts=self.opts) as evt: + evt.fire_event({'complete': complete, 'comment': comment, + 'beacons': self.opts['beacons']}, + tag='/salt/minion/minion_beacon_enabled_complete') return True @@ -422,10 +422,10 @@ def disable_beacon(self, name): complete = True # Fire the complete event back along with updated list of beacons - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': complete, 'comment': comment, - 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacon_disabled_complete') + with salt.utils.event.get_event('minion', opts=self.opts) as evt: + evt.fire_event({'complete': complete, 'comment': comment, + 'beacons': self.opts['beacons']}, + tag='/salt/minion/minion_beacon_disabled_complete') return True diff --git a/salt/beacons/inotify.py b/salt/beacons/inotify.py index da68b6ab5743..e3bfe4eb8947 100644 --- a/salt/beacons/inotify.py +++ b/salt/beacons/inotify.py @@ -117,6 +117,10 @@ def validate(config): if 'files' not in _config: return False, 'Configuration for inotify beacon must include files.' else: + if not isinstance(_config['files'], dict): + return False, ('Configuration for inotify beacon invalid, ' + 'files must be a dict.') + for path in _config.get('files'): if not isinstance(_config['files'][path], dict): diff --git a/salt/cli/caller.py b/salt/cli/caller.py index a7bda65c3504..6329cec3de05 100644 --- a/salt/cli/caller.py +++ b/salt/cli/caller.py @@ -315,11 +315,9 @@ def return_pub(self, ret): ''' Return the data up to the master ''' - channel = salt.transport.client.ReqChannel.factory(self.opts, usage='salt_call') - load = {'cmd': '_return', 'id': self.opts['id']} - for key, value in six.iteritems(ret): - load[key] = value - try: + with salt.transport.client.ReqChannel.factory(self.opts, + usage='salt_call') as channel: + load = {'cmd': '_return', 'id': self.opts['id']} + for key, value in six.iteritems(ret): + load[key] = value channel.send(load) - finally: - channel.close() diff --git a/salt/client/__init__.py b/salt/client/__init__.py index 6e23e2372e8b..70f5180a2c12 100644 --- a/salt/client/__init__.py +++ b/salt/client/__init__.py @@ -21,6 +21,7 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals import os +import sys import time import random import logging @@ -338,9 +339,9 @@ def run_job( 'The salt master could not be contacted. Is master running?' ) except AuthenticationError as err: - raise AuthenticationError(err) + six.reraise(*sys.exc_info()) except AuthorizationError as err: - raise AuthorizationError(err) + six.reraise(*sys.exc_info()) except Exception as general_exception: # Convert to generic client error and pass along message raise SaltClientError(general_exception) @@ -1717,56 +1718,56 @@ def pub(self, timeout, **kwargs) - master_uri = 'tcp://' + salt.utils.zeromq.ip_bracket(self.opts['interface']) + \ - ':' + six.text_type(self.opts['ret_port']) - channel = salt.transport.client.ReqChannel.factory(self.opts, - crypt='clear', - master_uri=master_uri) + master_uri = 'tcp://{}:{}'.format( + salt.utils.zeromq.ip_bracket(self.opts['interface']), + six.text_type(self.opts['ret_port']) + ) - try: - # Ensure that the event subscriber is connected. - # If not, we won't get a response, so error out - if listen and not self.event.connect_pub(timeout=timeout): - raise SaltReqTimeoutError() - payload = channel.send(payload_kwargs, timeout=timeout) - except SaltReqTimeoutError: - raise SaltReqTimeoutError( - 'Salt request timed out. The master is not responding. You ' - 'may need to run your command with `--async` in order to ' - 'bypass the congested event bus. With `--async`, the CLI tool ' - 'will print the job id (jid) and exit immediately without ' - 'listening for responses. You can then use ' - '`salt-run jobs.lookup_jid` to look up the results of the job ' - 'in the job cache later.' - ) + with salt.transport.client.ReqChannel.factory(self.opts, + crypt='clear', + master_uri=master_uri) as channel: + try: + # Ensure that the event subscriber is connected. + # If not, we won't get a response, so error out + if listen and not self.event.connect_pub(timeout=timeout): + raise SaltReqTimeoutError() + payload = channel.send(payload_kwargs, timeout=timeout) + except SaltReqTimeoutError as err: + log.error(err) + raise SaltReqTimeoutError( + 'Salt request timed out. The master is not responding. You ' + 'may need to run your command with `--async` in order to ' + 'bypass the congested event bus. With `--async`, the CLI tool ' + 'will print the job id (jid) and exit immediately without ' + 'listening for responses. You can then use ' + '`salt-run jobs.lookup_jid` to look up the results of the job ' + 'in the job cache later.' + ) - if not payload: - # The master key could have changed out from under us! Regen - # and try again if the key has changed - key = self.__read_master_key() - if key == self.key: + if not payload: + # The master key could have changed out from under us! Regen + # and try again if the key has changed + key = self.__read_master_key() + if key == self.key: + return payload + self.key = key + payload_kwargs['key'] = self.key + payload = channel.send(payload_kwargs) + + error = payload.pop('error', None) + if error is not None: + if isinstance(error, dict): + err_name = error.get('name', '') + err_msg = error.get('message', '') + if err_name == 'AuthenticationError': + raise AuthenticationError(err_msg) + elif err_name == 'AuthorizationError': + raise AuthorizationError(err_msg) + + raise PublishError(error) + + if not payload: return payload - self.key = key - payload_kwargs['key'] = self.key - payload = channel.send(payload_kwargs) - - error = payload.pop('error', None) - if error is not None: - if isinstance(error, dict): - err_name = error.get('name', '') - err_msg = error.get('message', '') - if err_name == 'AuthenticationError': - raise AuthenticationError(err_msg) - elif err_name == 'AuthorizationError': - raise AuthorizationError(err_msg) - - raise PublishError(error) - - if not payload: - return payload - - # We have the payload, let's get rid of the channel fast(GC'ed faster) - channel.close() return {'jid': payload['load']['jid'], 'minions': payload['load']['minions']} @@ -1826,59 +1827,57 @@ def pub_async(self, master_uri = 'tcp://' + salt.utils.zeromq.ip_bracket(self.opts['interface']) + \ ':' + six.text_type(self.opts['ret_port']) - channel = salt.transport.client.AsyncReqChannel.factory(self.opts, - io_loop=io_loop, - crypt='clear', - master_uri=master_uri) - try: - # Ensure that the event subscriber is connected. - # If not, we won't get a response, so error out - if listen and not self.event.connect_pub(timeout=timeout): - raise SaltReqTimeoutError() - payload = yield channel.send(payload_kwargs, timeout=timeout) - except SaltReqTimeoutError: - raise SaltReqTimeoutError( - 'Salt request timed out. The master is not responding. You ' - 'may need to run your command with `--async` in order to ' - 'bypass the congested event bus. With `--async`, the CLI tool ' - 'will print the job id (jid) and exit immediately without ' - 'listening for responses. You can then use ' - '`salt-run jobs.lookup_jid` to look up the results of the job ' - 'in the job cache later.' - ) + with salt.transport.client.AsyncReqChannel.factory(self.opts, + io_loop=io_loop, + crypt='clear', + master_uri=master_uri) as channel: + try: + # Ensure that the event subscriber is connected. + # If not, we won't get a response, so error out + if listen and not self.event.connect_pub(timeout=timeout): + raise SaltReqTimeoutError() + payload = yield channel.send(payload_kwargs, timeout=timeout) + except SaltReqTimeoutError: + raise SaltReqTimeoutError( + 'Salt request timed out. The master is not responding. You ' + 'may need to run your command with `--async` in order to ' + 'bypass the congested event bus. With `--async`, the CLI tool ' + 'will print the job id (jid) and exit immediately without ' + 'listening for responses. You can then use ' + '`salt-run jobs.lookup_jid` to look up the results of the job ' + 'in the job cache later.' + ) - if not payload: - # The master key could have changed out from under us! Regen - # and try again if the key has changed - key = self.__read_master_key() - if key == self.key: + if not payload: + # The master key could have changed out from under us! Regen + # and try again if the key has changed + key = self.__read_master_key() + if key == self.key: + raise tornado.gen.Return(payload) + self.key = key + payload_kwargs['key'] = self.key + payload = yield channel.send(payload_kwargs) + + error = payload.pop('error', None) + if error is not None: + if isinstance(error, dict): + err_name = error.get('name', '') + err_msg = error.get('message', '') + if err_name == 'AuthenticationError': + raise AuthenticationError(err_msg) + elif err_name == 'AuthorizationError': + raise AuthorizationError(err_msg) + + raise PublishError(error) + + if not payload: raise tornado.gen.Return(payload) - self.key = key - payload_kwargs['key'] = self.key - payload = yield channel.send(payload_kwargs) - - error = payload.pop('error', None) - if error is not None: - if isinstance(error, dict): - err_name = error.get('name', '') - err_msg = error.get('message', '') - if err_name == 'AuthenticationError': - raise AuthenticationError(err_msg) - elif err_name == 'AuthorizationError': - raise AuthorizationError(err_msg) - - raise PublishError(error) - - if not payload: - raise tornado.gen.Return(payload) - - # We have the payload, let's get rid of the channel fast(GC'ed faster) - channel.close() raise tornado.gen.Return({'jid': payload['load']['jid'], 'minions': payload['load']['minions']}) + # pylint: disable=W1701 def __del__(self): # This IS really necessary! # When running tests, if self.events is not destroyed, we leak 2 @@ -1886,6 +1885,7 @@ def __del__(self): if hasattr(self, 'event'): # The call below will take care of calling 'self.event.destroy()' del self.event + # pylint: enable=W1701 def _clean_up_subscriptions(self, job_id): if self.opts.get('order_masters'): diff --git a/salt/client/mixins.py b/salt/client/mixins.py index 480145eb8d22..a784edf54b49 100644 --- a/salt/client/mixins.py +++ b/salt/client/mixins.py @@ -136,17 +136,15 @@ def master_call(self, **kwargs): ''' load = kwargs load['cmd'] = self.client - channel = salt.transport.client.ReqChannel.factory(self.opts, - crypt='clear', - usage='master_call') - try: + + with salt.transport.client.ReqChannel.factory(self.opts, + crypt='clear', + usage='master_call') as channel: ret = channel.send(load) - finally: - channel.close() - if isinstance(ret, collections.Mapping): - if 'error' in ret: - salt.utils.error.raise_error(**ret['error']) - return ret + if isinstance(ret, collections.Mapping): + if 'error' in ret: + salt.utils.error.raise_error(**ret['error']) + return ret def cmd_sync(self, low, timeout=None, full_return=False): ''' @@ -164,19 +162,19 @@ def cmd_sync(self, low, timeout=None, full_return=False): 'eauth': 'pam', }) ''' - event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=True) - job = self.master_call(**low) - ret_tag = salt.utils.event.tagify('ret', base=job['tag']) + with salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=True) as event: + job = self.master_call(**low) + ret_tag = salt.utils.event.tagify('ret', base=job['tag']) - if timeout is None: - timeout = self.opts.get('rest_timeout', 300) - ret = event.get_event(tag=ret_tag, full=True, wait=timeout, auto_reconnect=True) - if ret is None: - raise salt.exceptions.SaltClientTimeout( - "RunnerClient job '{0}' timed out".format(job['jid']), - jid=job['jid']) + if timeout is None: + timeout = self.opts.get('rest_timeout', 300) + ret = event.get_event(tag=ret_tag, full=True, wait=timeout, auto_reconnect=True) + if ret is None: + raise salt.exceptions.SaltClientTimeout( + "RunnerClient job '{0}' timed out".format(job['jid']), + jid=job['jid']) - return ret if full_return else ret['data']['return'] + return ret if full_return else ret['data']['return'] def cmd(self, fun, arg=None, pub_data=None, kwarg=None, print_event=True, full_return=False): ''' @@ -295,13 +293,6 @@ def low(self, fun, low, print_event=True, full_return=False): 'user': low.get('__user__', 'UNKNOWN'), } - event = salt.utils.event.get_event( - 'master', - self.opts['sock_dir'], - self.opts['transport'], - opts=self.opts, - listen=False) - if print_event: print_func = self.print_async_event \ if hasattr(self, 'print_async_event') \ @@ -311,117 +302,121 @@ def low(self, fun, low, print_event=True, full_return=False): # runner/wheel output during orchestration). print_func = None - namespaced_event = salt.utils.event.NamespacedEvent( - event, + with salt.utils.event.NamespacedEvent( + salt.utils.event.get_event( + 'master', + self.opts['sock_dir'], + self.opts['transport'], + opts=self.opts, + listen=False, + ), tag, print_func=print_func - ) + ) as namespaced_event: + + # TODO: test that they exist + # TODO: Other things to inject?? + func_globals = {'__jid__': jid, + '__user__': data['user'], + '__tag__': tag, + # weak ref to avoid the Exception in interpreter + # teardown of event + '__jid_event__': weakref.proxy(namespaced_event), + } - # TODO: test that they exist - # TODO: Other things to inject?? - func_globals = {'__jid__': jid, - '__user__': data['user'], - '__tag__': tag, - # weak ref to avoid the Exception in interpreter - # teardown of event - '__jid_event__': weakref.proxy(namespaced_event), - } - - try: - self_functions = pycopy.copy(self.functions) - salt.utils.lazy.verify_fun(self_functions, fun) - - # Inject some useful globals to *all* the function's global - # namespace only once per module-- not per func - completed_funcs = [] - - for mod_name in six.iterkeys(self_functions): - if '.' not in mod_name: - continue - mod, _ = mod_name.split('.', 1) - if mod in completed_funcs: - continue - completed_funcs.append(mod) - for global_key, value in six.iteritems(func_globals): - self.functions[mod_name].__globals__[global_key] = value - - # There are some discrepancies of what a "low" structure is in the - # publisher world it is a dict including stuff such as jid, fun, - # arg (a list of args, with kwargs packed in). Historically this - # particular one has had no "arg" and just has had all the kwargs - # packed into the top level object. The plan is to move away from - # that since the caller knows what is an arg vs a kwarg, but while - # we make the transition we will load "kwargs" using format_call if - # there are no kwargs in the low object passed in. - - if 'arg' in low and 'kwarg' in low: - args = low['arg'] - kwargs = low['kwarg'] - else: - f_call = salt.utils.args.format_call( - self.functions[fun], - low, - expected_extra_kws=CLIENT_INTERNAL_KEYWORDS - ) - args = f_call.get('args', ()) - kwargs = f_call.get('kwargs', {}) - - # Update the event data with loaded args and kwargs - data['fun_args'] = list(args) + ([kwargs] if kwargs else []) - func_globals['__jid_event__'].fire_event(data, 'new') - - # Initialize a context for executing the method. - with tornado.stack_context.StackContext(self.functions.context_dict.clone): - func = self.functions[fun] - try: - data['return'] = func(*args, **kwargs) - except TypeError as exc: - data['return'] = '\nPassed invalid arguments: {0}\n\nUsage:\n{1}'.format(exc, func.__doc__) - try: - data['success'] = self.context.get('retcode', 0) == 0 - except AttributeError: - # Assume a True result if no context attribute - data['success'] = True - if isinstance(data['return'], dict) and 'data' in data['return']: - # some functions can return boolean values - data['success'] = salt.utils.state.check_result(data['return']['data']) - except (Exception, SystemExit) as ex: - if isinstance(ex, salt.exceptions.NotImplemented): - data['return'] = six.text_type(ex) - else: - data['return'] = 'Exception occurred in {0} {1}: {2}'.format( - self.client, - fun, - traceback.format_exc(), - ) - data['success'] = False - - if self.store_job: try: - salt.utils.job.store_job( - self.opts, - { - 'id': self.opts['id'], - 'tgt': self.opts['id'], - 'jid': data['jid'], - 'return': data, - }, - event=None, - mminion=self.mminion, + self_functions = pycopy.copy(self.functions) + salt.utils.lazy.verify_fun(self_functions, fun) + + # Inject some useful globals to *all* the function's global + # namespace only once per module-- not per func + completed_funcs = [] + + for mod_name in six.iterkeys(self_functions): + if '.' not in mod_name: + continue + mod, _ = mod_name.split('.', 1) + if mod in completed_funcs: + continue + completed_funcs.append(mod) + for global_key, value in six.iteritems(func_globals): + self.functions[mod_name].__globals__[global_key] = value + + # There are some discrepancies of what a "low" structure is in the + # publisher world it is a dict including stuff such as jid, fun, + # arg (a list of args, with kwargs packed in). Historically this + # particular one has had no "arg" and just has had all the kwargs + # packed into the top level object. The plan is to move away from + # that since the caller knows what is an arg vs a kwarg, but while + # we make the transition we will load "kwargs" using format_call if + # there are no kwargs in the low object passed in. + + if 'arg' in low and 'kwarg' in low: + args = low['arg'] + kwargs = low['kwarg'] + else: + f_call = salt.utils.args.format_call( + self.functions[fun], + low, + expected_extra_kws=CLIENT_INTERNAL_KEYWORDS ) - except salt.exceptions.SaltCacheError: - log.error('Could not store job cache info. ' - 'Job details for this run may be unavailable.') - - # Outputters _can_ mutate data so write to the job cache first! - namespaced_event.fire_event(data, 'ret') - - # if we fired an event, make sure to delete the event object. - # This will ensure that we call destroy, which will do the 0MQ linger - log.info('Runner completed: %s', data['jid']) - del event - del namespaced_event - return data if full_return else data['return'] + args = f_call.get('args', ()) + kwargs = f_call.get('kwargs', {}) + + # Update the event data with loaded args and kwargs + data['fun_args'] = list(args) + ([kwargs] if kwargs else []) + func_globals['__jid_event__'].fire_event(data, 'new') + + # Initialize a context for executing the method. + with tornado.stack_context.StackContext(self.functions.context_dict.clone): + func = self.functions[fun] + try: + data['return'] = func(*args, **kwargs) + except TypeError as exc: + data['return'] = '\nPassed invalid arguments: {0}\n\nUsage:\n{1}'.format(exc, func.__doc__) + try: + data['success'] = self.context.get('retcode', 0) == 0 + except AttributeError: + # Assume a True result if no context attribute + data['success'] = True + if isinstance(data['return'], dict) and 'data' in data['return']: + # some functions can return boolean values + data['success'] = salt.utils.state.check_result(data['return']['data']) + except (Exception, SystemExit) as ex: + if isinstance(ex, salt.exceptions.NotImplemented): + data['return'] = six.text_type(ex) + else: + data['return'] = 'Exception occurred in {0} {1}: {2}'.format( + self.client, + fun, + traceback.format_exc(), + ) + data['success'] = False + + if self.store_job: + try: + salt.utils.job.store_job( + self.opts, + { + 'id': self.opts['id'], + 'tgt': self.opts['id'], + 'jid': data['jid'], + 'return': data, + }, + event=None, + mminion=self.mminion, + ) + except salt.exceptions.SaltCacheError: + log.error('Could not store job cache info. ' + 'Job details for this run may be unavailable.') + + # Outputters _can_ mutate data so write to the job cache first! + namespaced_event.fire_event(data, 'ret') + + # if we fired an event, make sure to delete the event object. + # This will ensure that we call destroy, which will do the 0MQ linger + log.info('Runner completed: %s', data['jid']) + return data if full_return else data['return'] def get_docs(self, arg=None): ''' @@ -508,9 +503,9 @@ def asynchronous(self, fun, low, user='UNKNOWN', pub=None): to watch for the return ''' async_pub = pub if pub is not None else self._gen_async_pub() - - proc = salt.utils.process.SignalHandlingMultiprocessingProcess( + proc = salt.utils.process.SignalHandlingProcess( target=self._proc_function, + name='ProcessFunc', args=(fun, low, user, async_pub['tag'], async_pub['jid'])) with salt.utils.process.default_signals(signal.SIGINT, signal.SIGTERM): # Reset current signals before starting the process in diff --git a/salt/client/netapi.py b/salt/client/netapi.py index ec6ee7a6642c..acf96053c91c 100644 --- a/salt/client/netapi.py +++ b/salt/client/netapi.py @@ -14,7 +14,7 @@ log = logging.getLogger(__name__) -class RunNetapi(salt.utils.process.SignalHandlingMultiprocessingProcess): +class RunNetapi(salt.utils.process.SignalHandlingProcess): ''' Runner class that's pickable for netapi modules ''' @@ -27,7 +27,6 @@ def __init__(self, opts, fname, **kwargs): # We do this so that __init__ will be invoked on Windows in the child # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): - self._is_child = True self.__init__( state['opts'], state['fname'], diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py index 1453430e7325..302eba298c62 100644 --- a/salt/client/ssh/__init__.py +++ b/salt/client/ssh/__init__.py @@ -48,7 +48,7 @@ import salt.utils.url import salt.utils.verify from salt.utils.platform import is_windows -from salt.utils.process import MultiprocessingProcess +from salt.utils.process import Process import salt.roster from salt.template import compile_template @@ -586,7 +586,7 @@ def handle_ssh(self, mine=False): self.targets[host], mine, ) - routine = MultiprocessingProcess( + routine = Process( target=self.handle_routine, args=args) routine.start() diff --git a/salt/client/ssh/wrapper/state.py b/salt/client/ssh/wrapper/state.py index b4272e4b55b4..3f1cead4b169 100644 --- a/salt/client/ssh/wrapper/state.py +++ b/salt/client/ssh/wrapper/state.py @@ -167,6 +167,16 @@ def _cleanup_slsmod_high_data(high_data): stateconf_data['slsmod'] = None +def _parse_mods(mods): + ''' + Parse modules. + ''' + if isinstance(mods, six.string_types): + mods = [item.strip() for item in mods.split(',') if item.strip()] + + return mods + + def sls(mods, saltenv='base', test=None, exclude=None, **kwargs): ''' Create the seed file for a state.sls run @@ -181,8 +191,7 @@ def sls(mods, saltenv='base', test=None, exclude=None, **kwargs): __salt__, __context__['fileclient']) st_.push_active() - if isinstance(mods, six.string_types): - mods = mods.split(',') + mods = _parse_mods(mods) high_data, errors = st_.render_highstate({saltenv: mods}) if exclude: if isinstance(exclude, six.string_types): @@ -922,8 +931,7 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs): err += __pillar__['_errors'] return err - if isinstance(mods, six.string_types): - split_mods = mods.split(',') + split_mods = _parse_mods(mods) st_.push_active() high_, errors = st_.render_highstate({opts['saltenv']: split_mods}) errors += st_.state.verify_high(high_) @@ -980,8 +988,7 @@ def show_sls(mods, saltenv='base', test=None, **kwargs): __salt__, __context__['fileclient']) st_.push_active() - if isinstance(mods, six.string_types): - mods = mods.split(',') + mods = _parse_mods(mods) high_data, errors = st_.render_highstate({saltenv: mods}) high_data, ext_errors = st_.state.reconcile_extend(high_data) errors += ext_errors @@ -1025,8 +1032,7 @@ def show_low_sls(mods, saltenv='base', test=None, **kwargs): __salt__, __context__['fileclient']) st_.push_active() - if isinstance(mods, six.string_types): - mods = mods.split(',') + mods = _parse_mods(mods) high_data, errors = st_.render_highstate({saltenv: mods}) high_data, ext_errors = st_.state.reconcile_extend(high_data) errors += ext_errors diff --git a/salt/cloud/clouds/ec2.py b/salt/cloud/clouds/ec2.py index e9ce08cd8bff..887f7593be0e 100644 --- a/salt/cloud/clouds/ec2.py +++ b/salt/cloud/clouds/ec2.py @@ -92,7 +92,6 @@ import binascii import datetime import base64 -import msgpack import re import decimal @@ -102,6 +101,7 @@ import salt.utils.files import salt.utils.hashutils import salt.utils.json +import salt.utils.msgpack import salt.utils.stringutils import salt.utils.yaml from salt._compat import ElementTree as ET @@ -1932,6 +1932,18 @@ def request_instance(vm_=None, call=None): 'del_root_vol_on_destroy', vm_, __opts__, search_global=False ) + set_termination_protection = config.get_cloud_config_value( + 'termination_protection', vm_, __opts__, search_global=False + ) + + if set_termination_protection is not None: + if not isinstance(set_termination_protection, bool): + raise SaltCloudConfigError( + '\'termination_protection\' should be a boolean value.' + ) + params.update(_param_from_config(spot_prefix + 'DisableApiTermination', + set_termination_protection)) + if set_del_root_vol_on_destroy and not isinstance(set_del_root_vol_on_destroy, bool): raise SaltCloudConfigError( '\'del_root_vol_on_destroy\' should be a boolean value.' @@ -5000,7 +5012,7 @@ def _parse_pricing(url, name): __opts__['cachedir'], 'ec2-pricing-{0}.p'.format(name) ) with salt.utils.files.fopen(outfile, 'w') as fho: - msgpack.dump(regions, fho) + salt.utils.msgpack.dump(regions, fho) return True @@ -5068,7 +5080,8 @@ def show_pricing(kwargs=None, call=None): update_pricing({'type': name}, 'function') with salt.utils.files.fopen(pricefile, 'r') as fhi: - ec2_price = salt.utils.stringutils.to_unicode(msgpack.load(fhi)) + ec2_price = salt.utils.stringutils.to_unicode( + salt.utils.msgpack.load(fhi)) region = get_location(profile) size = profile.get('size', None) diff --git a/salt/cloud/clouds/gce.py b/salt/cloud/clouds/gce.py index 8466ac20ad1c..30d929d6ed97 100644 --- a/salt/cloud/clouds/gce.py +++ b/salt/cloud/clouds/gce.py @@ -53,7 +53,6 @@ import re import pprint import logging -import msgpack from ast import literal_eval from salt.utils.versions import LooseVersion as _LooseVersion @@ -91,6 +90,7 @@ import salt.utils.cloud import salt.utils.files import salt.utils.http +import salt.utils.msgpack import salt.config as config from salt.cloud.libcloudfuncs import * # pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import from salt.exceptions import ( @@ -2629,7 +2629,7 @@ def update_pricing(kwargs=None, call=None): __opts__['cachedir'], 'gce-pricing.p' ) with salt.utils.files.fopen(outfile, 'w') as fho: - msgpack.dump(price_json['dict'], fho) + salt.utils.msgpack.dump(price_json['dict'], fho) return True @@ -2668,7 +2668,7 @@ def show_pricing(kwargs=None, call=None): update_pricing() with salt.utils.files.fopen(pricefile, 'r') as fho: - sizes = msgpack.load(fho) + sizes = salt.utils.msgpack.load(fho) per_hour = float(sizes['gcp_price_list'][size][region]) diff --git a/salt/cloud/clouds/libvirt.py b/salt/cloud/clouds/libvirt.py index 4192591a9086..55c1b2db9017 100644 --- a/salt/cloud/clouds/libvirt.py +++ b/salt/cloud/clouds/libvirt.py @@ -58,8 +58,9 @@ from __future__ import absolute_import, print_function, unicode_literals import logging -import uuid import os +import sys +import uuid from xml.etree import ElementTree @@ -465,10 +466,10 @@ def create(vm_): ) return ret - except Exception as e: # pylint: disable=broad-except + except Exception: # pylint: disable=broad-except do_cleanup(cleanup) # throw the root cause after cleanup - raise e + six.reraise(*sys.exc_info()) def do_cleanup(cleanup): diff --git a/salt/cloud/deploy/bootstrap-salt.sh b/salt/cloud/deploy/bootstrap-salt.sh old mode 100755 new mode 100644 index a4de8713c488..4e197abd4def --- a/salt/cloud/deploy/bootstrap-salt.sh +++ b/salt/cloud/deploy/bootstrap-salt.sh @@ -23,7 +23,7 @@ #====================================================================================================================== set -o nounset # Treat unset variables as an error -__ScriptVersion="2019.05.20" +__ScriptVersion="2019.11.04" __ScriptName="bootstrap-salt.sh" __ScriptFullName="$0" @@ -499,7 +499,7 @@ exec 2>"$LOGPIPE" # 14 SIGALRM # 15 SIGTERM #---------------------------------------------------------------------------------------------------------------------- -APT_ERR=$(mktemp /tmp/apt_error.XXXX) +APT_ERR=$(mktemp /tmp/apt_error.XXXXXX) __exit_cleanup() { EXIT_CODE=$? @@ -601,7 +601,11 @@ elif [ "$ITYPE" = "stable" ]; then STABLE_REV="$1" shift elif [ "$(echo "$1" | grep -E '^([0-9]*\.[0-9]*\.[0-9]*)$')" != "" ]; then - STABLE_REV="archive/$1" + if [ "$(uname)" = "Darwin" ]; then + STABLE_REV="$1" + else + STABLE_REV="archive/$1" + fi shift else echo "Unknown stable version: $1 (valid: 1.6, 1.7, 2014.1, 2014.7, 2015.5, 2015.8, 2016.3, 2016.11, 2017.7, 2018.3, 2019.2, latest, \$MAJOR.\$MINOR.\$PATCH)" @@ -667,7 +671,11 @@ fi # Check if we're installing via a different Python executable and set major version variables if [ -n "$_PY_EXE" ]; then - _PY_PKG_VER=$(echo "$_PY_EXE" | sed -r "s/\\.//g") + if [ "$(uname)" = "Darwin" ]; then + _PY_PKG_VER=$(echo "$_PY_EXE" | sed "s/\\.//g") + else + _PY_PKG_VER=$(echo "$_PY_EXE" | sed -r "s/\\.//g") + fi _PY_MAJOR_VERSION=$(echo "$_PY_PKG_VER" | cut -c 7) if [ "$_PY_MAJOR_VERSION" != 3 ] && [ "$_PY_MAJOR_VERSION" != 2 ]; then @@ -883,7 +891,6 @@ __derive_debian_numeric_version() { elif [ "$INPUT_VERSION" = "stretch/sid" ]; then NUMERIC_VERSION=$(__parse_version_string "9.0") elif [ "$INPUT_VERSION" = "buster/sid" ]; then - # Let's start detecting the upcoming Debian 10 (Buster) release NUMERIC_VERSION=$(__parse_version_string "10.0") else echowarn "Unable to parse the Debian Version (codename: '$INPUT_VERSION')" @@ -1001,7 +1008,7 @@ __gather_linux_system_info() { elif [ "${DISTRO_NAME}" = "OracleServer" ]; then # This the Oracle Linux Server 6.5 DISTRO_NAME="Oracle Linux" - elif [ "${DISTRO_NAME}" = "AmazonAMI" ]; then + elif [ "${DISTRO_NAME}" = "AmazonAMI" ] || [ "${DISTRO_NAME}" = "Amazon" ]; then DISTRO_NAME="Amazon Linux AMI" elif [ "${DISTRO_NAME}" = "ManjaroLinux" ]; then DISTRO_NAME="Arch Linux" @@ -1243,6 +1250,16 @@ __gather_bsd_system_info() { } +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: __gather_osx_system_info +# DESCRIPTION: Discover MacOS X +#---------------------------------------------------------------------------------------------------------------------- +__gather_osx_system_info() { + DISTRO_NAME="MacOSX" + DISTRO_VERSION=$(sw_vers -productVersion) +} + + #--- FUNCTION ------------------------------------------------------------------------------------------------------- # NAME: __gather_system_info # DESCRIPTION: Discover which system and distribution we are running. @@ -1258,6 +1275,9 @@ __gather_system_info() { openbsd|freebsd|netbsd ) __gather_bsd_system_info ;; + darwin ) + __gather_osx_system_info + ;; * ) echoerror "${OS_NAME} not supported."; exit 1 @@ -1448,6 +1468,7 @@ __debian_derivatives_translation() { linuxmint_1_debian_base="8.0" raspbian_8_debian_base="8.0" raspbian_9_debian_base="9.0" + raspbian_10_debian_base="10.0" bunsenlabs_9_debian_base="9.0" turnkey_9_debian_base="9.0" @@ -1769,7 +1790,7 @@ elif [ "${DISTRO_NAME_L}" = "debian" ]; then __debian_codename_translation fi -if [ "$(echo "${DISTRO_NAME_L}" | grep -E '(debian|ubuntu|centos|red_hat|oracle|scientific|amazon)')" = "" ] && [ "$ITYPE" = "stable" ] && [ "$STABLE_REV" != "latest" ]; then +if [ "$(echo "${DISTRO_NAME_L}" | grep -E '(debian|ubuntu|centos|red_hat|oracle|scientific|amazon|fedora)')" = "" ] && [ "$ITYPE" = "stable" ] && [ "$STABLE_REV" != "latest" ]; then echoerror "${DISTRO_NAME} does not have major version pegged packages support" exit 1 fi @@ -2254,8 +2275,10 @@ __overwriteconfig() { tempfile="/tmp/salt-config-$$" fi + if [ -n "$_PY_EXE" ]; then + good_python="$_PY_EXE" # If python does not have yaml installed we're on Arch and should use python2 - if python -c "import yaml" 2> /dev/null; then + elif python -c "import yaml" 2> /dev/null; then good_python=python else good_python=python2 @@ -2495,9 +2518,16 @@ __install_pip_pkgs() { # Install pip and pip dependencies if ! __check_command_exists "${_pip_cmd} --version"; then - __PACKAGES="${_py_pkg}-setuptools ${_py_pkg}-pip gcc ${_py_pkg}-devel" + __PACKAGES="${_py_pkg}-setuptools ${_py_pkg}-pip gcc" # shellcheck disable=SC2086 - __yum_install_noinput ${__PACKAGES} || return 1 + if [ "$DISTRO_NAME_L" = "debian" ];then + __PACKAGES="${__PACKAGES} ${_py_pkg}-dev" + __apt_get_install_noinput ${__PACKAGES} || return 1 + else + __PACKAGES="${__PACKAGES} ${_py_pkg}-devel" + __yum_install_noinput ${__PACKAGES} || return 1 + fi + fi echoinfo "Installing pip packages: ${_pip_pkgs} using ${_py_exe}" @@ -2505,6 +2535,18 @@ __install_pip_pkgs() { ${_pip_cmd} install ${_pip_pkgs} || return 1 } +#--- FUNCTION ------------------------------------------------------------------------------------------------------- +# NAME: __install_tornado_pip +# PARAMETERS: python executable +# DESCRIPTION: Return 0 or 1 if successfully able to install tornado<5.0 +#---------------------------------------------------------------------------------------------------------------------- +__install_tornado_pip() { + # OS needs tornado <5.0 from pip + __check_pip_allowed "You need to allow pip based installations (-P) for Tornado <5.0 in order to install Salt on Python 3" + ## install pip if its not installed and install tornado + __install_pip_pkgs "tornado<5.0" "${1}" || return 1 +} + #--- FUNCTION ------------------------------------------------------------------------------------------------------- # NAME: __install_pip_deps # DESCRIPTION: Return 0 or 1 if successfully able to install pip packages via requirements file @@ -2648,13 +2690,12 @@ __install_saltstack_ubuntu_repository() { UBUNTU_CODENAME=${DISTRO_CODENAME} fi - __PACKAGES='' - # Install downloader backend for GPG keys fetching - if [ "$DISTRO_MAJOR_VERSION" -gt 16 ]; then - __PACKAGES="${__PACKAGES} gnupg dirmngr" - else - __PACKAGES="${__PACKAGES} gnupg-curl" + __PACKAGES='wget' + + # Required as it is not installed by default on Ubuntu 18+ + if [ "$DISTRO_MAJOR_VERSION" -ge 18 ]; then + __PACKAGES="${__PACKAGES} gnupg" fi # Make sure https transport is available @@ -2698,6 +2739,12 @@ install_ubuntu_deps() { __PACKAGES="upstart" fi + if [ -n "$_PY_EXE" ] && [ "$_PY_MAJOR_VERSION" -eq 3 ]; then + PY_PKG_VER=3 + else + PY_PKG_VER="" + fi + if [ "$DISTRO_MAJOR_VERSION" -ge 16 ] && [ -z "$_PY_EXE" ]; then __PACKAGES="${__PACKAGES} python2.7" fi @@ -2706,13 +2753,13 @@ install_ubuntu_deps() { __PACKAGES="${__PACKAGES} python-virtualenv" fi # Need python-apt for managing packages via Salt - __PACKAGES="${__PACKAGES} python-apt" + __PACKAGES="${__PACKAGES} python${PY_PKG_VER}-apt" # requests is still used by many salt modules - __PACKAGES="${__PACKAGES} python-requests" + __PACKAGES="${__PACKAGES} python${PY_PKG_VER}-requests" # YAML module is used for generating custom master/minion configs - __PACKAGES="${__PACKAGES} python-yaml" + __PACKAGES="${__PACKAGES} python${PY_PKG_VER}-yaml" # Additionally install procps and pciutils which allows for Docker bootstraps. See 366#issuecomment-39666813 __PACKAGES="${__PACKAGES} procps pciutils" @@ -3032,30 +3079,20 @@ install_ubuntu_check_services() { # Debian Install Functions # __install_saltstack_debian_repository() { - if [ "$DISTRO_MAJOR_VERSION" -eq 10 ]; then - # Packages for Debian 10 at repo.saltstack.com are not yet available - # Set up repository for Debian 9 for Debian 10 for now until support - # is available at repo.saltstack.com for Debian 10. - echowarn "Debian 10 distribution detected, but stable packages requested. Trying packages from Debian 9. You may experience problems." - DEBIAN_RELEASE="9" - DEBIAN_CODENAME="stretch" - else - DEBIAN_RELEASE="$DISTRO_MAJOR_VERSION" - DEBIAN_CODENAME="$DISTRO_CODENAME" - fi + DEBIAN_RELEASE="$DISTRO_MAJOR_VERSION" + DEBIAN_CODENAME="$DISTRO_CODENAME" __PY_VERSION_REPO="apt" if [ -n "$_PY_EXE" ] && [ "$_PY_MAJOR_VERSION" -eq 3 ]; then __PY_VERSION_REPO="py3" fi - __PACKAGES='' - # Install downloader backend for GPG keys fetching + __PACKAGES='wget' + + # Required as it is not installed by default on Debian 9+ if [ "$DISTRO_MAJOR_VERSION" -ge 9 ]; then - __PACKAGES="${__PACKAGES} gnupg2 dirmngr" - else - __PACKAGES="${__PACKAGES} gnupg-curl" + __PACKAGES="${__PACKAGES} gnupg2" fi # Make sure https transport is available @@ -3124,6 +3161,24 @@ install_debian_deps() { return 0 } +install_debian_git_pre() { + if ! __check_command_exists git; then + __apt_get_install_noinput git || return 1 + fi + + if [ "$_INSECURE_DL" -eq $BS_FALSE ] && [ "${_SALT_REPO_URL%%://*}" = "https" ]; then + __apt_get_install_noinput ca-certificates + fi + + __git_clone_and_checkout || return 1 + + # Let's trigger config_salt() + if [ "$_TEMP_CONFIG_DIR" = "null" ]; then + _TEMP_CONFIG_DIR="${_SALT_GIT_CHECKOUT_DIR}/conf/" + CONFIG_SALT_FUNC="config_salt" + fi +} + install_debian_git_deps() { if ! __check_command_exists git; then __apt_get_install_noinput git || return 1 @@ -3273,7 +3328,25 @@ install_debian_9_git_deps() { } install_debian_10_git_deps() { - install_debian_9_git_deps || return 1 + install_debian_git_pre || return 1 + + if [ -n "$_PY_EXE" ] && [ "$_PY_MAJOR_VERSION" -eq 3 ]; then + _py=${_PY_EXE} + PY_PKG_VER=3 + __PACKAGES="python${PY_PKG_VER}-distutils" + else + _py="python" + PY_PKG_VER="" + __PACKAGES="" + fi + + __install_tornado_pip ${_py}|| return 1 + __PACKAGES="${__PACKAGES} python${PY_PKG_VER}-msgpack python${PY_PKG_VER}-jinja2" + __PACKAGES="${__PACKAGES} python${PY_PKG_VER}-tornado python${PY_PKG_VER}-yaml python${PY_PKG_VER}-zmq" + + # shellcheck disable=SC2086 + __apt_get_install_noinput ${__PACKAGES} || return 1 + return 0 } @@ -3375,14 +3448,8 @@ install_debian_git_post() { # Install initscripts for Debian 7 "Wheezy" elif [ ! -f "/etc/init.d/salt-$fname" ] || \ { [ -f "/etc/init.d/salt-$fname" ] && [ "$_FORCE_OVERWRITE" -eq $BS_TRUE ]; }; then - if [ -f "${_SALT_GIT_CHECKOUT_DIR}/pkg/salt-$fname.init" ]; then - __copyfile "${_SALT_GIT_CHECKOUT_DIR}/pkg/salt-${fname}.init" "/etc/init.d/salt-${fname}" - __copyfile "${_SALT_GIT_CHECKOUT_DIR}/pkg/salt-${fname}.environment" "/etc/default/salt-${fname}" - else - # Make sure wget is available - __check_command_exists wget || __apt_get_install_noinput wget || return 1 - __fetch_url "/etc/init.d/salt-${fname}" "${HTTP_VAL}://anonscm.debian.org/cgit/pkg-salt/salt.git/plain/debian/salt-${fname}.init" - fi + __copyfile "${_SALT_GIT_CHECKOUT_DIR}/pkg/deb/salt-${fname}.init" "/etc/init.d/salt-${fname}" + __copyfile "${_SALT_GIT_CHECKOUT_DIR}/pkg/deb/salt-${fname}.environment" "/etc/default/salt-${fname}" if [ ! -f "/etc/init.d/salt-${fname}" ]; then echowarn "The init script for salt-${fname} was not found, skipping it..." @@ -3731,13 +3798,33 @@ install_centos_stable_deps() { __install_saltstack_rhel_repository || return 1 fi - __PACKAGES="yum-utils chkconfig" + if [ "$DISTRO_MAJOR_VERSION" -ge 8 ]; then + __PACKAGES="dnf-utils chkconfig" + else + __PACKAGES="yum-utils chkconfig" + fi - # YAML module is used for generating custom master/minion configs - if [ -n "$_PY_EXE" ] && [ "$_PY_MAJOR_VERSION" -eq 3 ]; then - __PACKAGES="${__PACKAGES} python34-PyYAML" + if [ "$DISTRO_MAJOR_VERSION" -ge 8 ]; then + # YAML module is used for generating custom master/minion configs + if [ -n "$_PY_EXE" ] && [ "$_PY_MAJOR_VERSION" -eq 3 ]; then + __PACKAGES="${__PACKAGES} python3-pyyaml" + else + __PACKAGES="${__PACKAGES} python2-pyyaml" + fi + elif [ "$DISTRO_MAJOR_VERSION" -eq 7 ]; then + # YAML module is used for generating custom master/minion configs + if [ -n "$_PY_EXE" ] && [ "$_PY_MAJOR_VERSION" -eq 3 ]; then + __PACKAGES="${__PACKAGES} python36-PyYAML" + else + __PACKAGES="${__PACKAGES} PyYAML" + fi else - __PACKAGES="${__PACKAGES} PyYAML" + # YAML module is used for generating custom master/minion configs + if [ -n "$_PY_EXE" ] && [ "$_PY_MAJOR_VERSION" -eq 3 ]; then + __PACKAGES="${__PACKAGES} python34-PyYAML" + else + __PACKAGES="${__PACKAGES} PyYAML" + fi fi # shellcheck disable=SC2086 @@ -3819,12 +3906,26 @@ install_centos_git_deps() { __git_clone_and_checkout || return 1 - __PACKAGES="m2crypto" + __PACKAGES="" + _install_m2crypto_req=false if [ -n "$_PY_EXE" ] && [ "$_PY_MAJOR_VERSION" -eq 3 ]; then - # Packages are named python34- - PY_PKG_VER=34 + _py=${_PY_EXE} + if [ "$DISTRO_MAJOR_VERSION" -gt 6 ]; then + _install_m2crypto_req=true + fi + if [ "$DISTRO_MAJOR_VERSION" -ge 8 ]; then + # Packages are named python3- + PY_PKG_VER=3 + else + # Packages are named python36- + PY_PKG_VER=36 + fi else + if [ "$DISTRO_MAJOR_VERSION" -eq 6 ]; then + _install_m2crypto_req=true + fi + _py="python" PY_PKG_VER="" # Only Py2 needs python-futures @@ -3836,7 +3937,14 @@ install_centos_git_deps() { fi fi - __PACKAGES="${__PACKAGES} python${PY_PKG_VER}-crypto python${PY_PKG_VER}-jinja2" + if [ "$DISTRO_MAJOR_VERSION" -ge 8 ]; then + __install_tornado_pip ${_py} || return 1 + __PACKAGES="${__PACKAGES} python3-m2crypto" + else + __PACKAGES="${__PACKAGES} m2crypto python${PY_PKG_VER}-crypto" + fi + + __PACKAGES="${__PACKAGES} python${PY_PKG_VER}-jinja2" __PACKAGES="${__PACKAGES} python${PY_PKG_VER}-msgpack python${PY_PKG_VER}-requests" __PACKAGES="${__PACKAGES} python${PY_PKG_VER}-tornado python${PY_PKG_VER}-zmq" @@ -3854,7 +3962,7 @@ install_centos_git_deps() { _PIP_PACKAGES="m2crypto!=0.33.0 jinja2 msgpack-python pycrypto PyYAML tornado<5.0 zmq futures>=2.0" # install swig and openssl on cent6 - if [ "$DISTRO_MAJOR_VERSION" -eq 6 ]; then + if $_install_m2crypto_req; then __yum_install_noinput openssl-devel swig || return 1 fi @@ -6611,6 +6719,117 @@ daemons_running_voidlinux() { # ####################################################################################################################### +####################################################################################################################### +# +# OS X / Darwin Install Functions +# + +__macosx_get_packagesite() { + DARWIN_ARCH="x86_64" + + __PY_VERSION_REPO="py2" + if [ -n "$_PY_EXE" ] && [ "$_PY_MAJOR_VERSION" -eq 3 ]; then + __PY_VERSION_REPO="py3" + fi + + PKG="salt-${STABLE_REV}-${__PY_VERSION_REPO}-${DARWIN_ARCH}.pkg" + SALTPKGCONFURL="https://repo.saltstack.com/osx/${PKG}" +} + +# Using a separate conf step to head for idempotent install... +__configure_macosx_pkg_details() { + __macosx_get_packagesite || return 1 + return 0 +} + +install_macosx_stable_deps() { + __configure_macosx_pkg_details || return 1 + return 0 +} + +install_macosx_git_deps() { + install_macosx_stable_deps || return 1 + + __fetch_url "/tmp/get-pip.py" "https://bootstrap.pypa.io/get-pip.py" || return 1 + + if [ -n "$_PY_EXE" ]; then + _PYEXE=${_PY_EXE} + else + _PYEXE=python2.7 + fi + + # Install PIP + $_PYEXE /tmp/get-pip.py || return 1 + + __git_clone_and_checkout || return 1 + + __PIP_REQUIREMENTS="dev_python27.txt" + if [ -n "$_PY_EXE" ] && [ "$_PY_MAJOR_VERSION" -eq 3 ]; then + __PIP_REQUIREMENTS="dev_python34.txt" + fi + + requirements_file="${_SALT_GIT_CHECKOUT_DIR}/requirements/${__PIP_REQUIREMENTS}" + pip install -U -r "${requirements_file}" --install-option="--prefix=/opt/salt" || return 1 + + return 0 +} + +install_macosx_stable() { + install_macosx_stable_deps || return 1 + + /usr/bin/curl "${SALTPKGCONFURL}" > "/tmp/${PKG}" || return 1 + + /usr/sbin/installer -pkg "/tmp/${PKG}" -target / || return 1 + + return 0 +} + +install_macosx_git() { + + if [ -n "$_PY_EXE" ]; then + _PYEXE=${_PY_EXE} + else + _PYEXE=python2.7 + fi + + if [ -f "${_SALT_GIT_CHECKOUT_DIR}/salt/syspaths.py" ]; then + $_PYEXE setup.py --salt-config-dir="$_SALT_ETC_DIR" --salt-cache-dir="${_SALT_CACHE_DIR}" ${SETUP_PY_INSTALL_ARGS} install --prefix=/opt/salt || return 1 + else + $_PYEXE setup.py ${SETUP_PY_INSTALL_ARGS} install --prefix=/opt/salt || return 1 + fi + + return 0 +} + +install_macosx_stable_post() { + if [ ! -f /etc/paths.d/salt ]; then + print "%s\n" "/opt/salt/bin" "/usr/local/sbin" > /etc/paths.d/salt + fi + + # shellcheck disable=SC1091 + . /etc/profile + + return 0 +} + +install_macosx_git_post() { + install_macosx_stable_post || return 1 + return 0 +} + +install_macosx_restart_daemons() { + [ $_START_DAEMONS -eq $BS_FALSE ] && return + + /bin/launchctl unload -w /Library/LaunchDaemons/com.saltstack.salt.minion.plist || return 1 + /bin/launchctl load -w /Library/LaunchDaemons/com.saltstack.salt.minion.plist || return 1 + + return 0 +} +# +# Ended OS X / Darwin Install Functions +# +####################################################################################################################### + ####################################################################################################################### # # Default minion configuration function. Matches ANY distribution as long as diff --git a/salt/config/__init__.py b/salt/config/__init__.py index 422a5c644c7b..4a6605ca5892 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -36,6 +36,7 @@ import salt.syspaths import salt.exceptions import salt.defaults.exitcodes +import salt.utils.immutabletypes as immutabletypes try: import psutil @@ -100,11 +101,7 @@ def _gather_buffer_space(): # TODO: Reserved for future use _DFLT_IPC_RBUFFER = _gather_buffer_space() * .5 -FLO_DIR = os.path.join( - os.path.dirname(os.path.dirname(__file__)), - 'daemons', 'flo') - -VALID_OPTS = { +VALID_OPTS = immutabletypes.freeze({ # The address of the salt master. May be specified as IP address or hostname 'master': (six.string_types, list), @@ -707,8 +704,6 @@ def _gather_buffer_space(): 'hgfs_root': six.string_types, 'hgfs_base': six.string_types, 'hgfs_branch_method': six.string_types, - 'hgfs_env_whitelist': list, - 'hgfs_env_blacklist': list, 'hgfs_saltenv_whitelist': list, 'hgfs_saltenv_blacklist': list, 'svnfs_remotes': list, @@ -717,8 +712,6 @@ def _gather_buffer_space(): 'svnfs_trunk': six.string_types, 'svnfs_branches': six.string_types, 'svnfs_tags': six.string_types, - 'svnfs_env_whitelist': list, - 'svnfs_env_blacklist': list, 'svnfs_saltenv_whitelist': list, 'svnfs_saltenv_blacklist': list, 'minionfs_env': six.string_types, @@ -925,6 +918,9 @@ def _gather_buffer_space(): # Set a hard limit for the amount of memory modules can consume on a minion. 'modules_max_memory': int, + # Blacklist specific core grains to be filtered + 'grains_blacklist': list, + # The number of minutes between the minion refreshing its cache of grains 'grains_refresh_every': int, @@ -954,6 +950,9 @@ def _gather_buffer_space(): # Always generate minion id in lowercase. 'minion_id_lowercase': bool, + # Remove either a single domain (foo.org), or all (True) from a generated minion id. + 'minion_id_remove_domain': (six.string_types, bool), + # If set, the master will sign all publications before they are sent out 'sign_pub_messages': bool, @@ -1166,9 +1165,6 @@ def _gather_buffer_space(): # Subconfig entries can be specified by using the ':' notation (e.g. key:subkey) 'pass_to_ext_pillars': (six.string_types, list), - # Used by salt.modules.dockermod.compare_container_networks to specify which keys are compared - 'docker.compare_container_networks': dict, - # SSDP discovery publisher description. # Contains publisher configuration and minion mapping. # Setting it to False disables discovery @@ -1191,10 +1187,10 @@ def _gather_buffer_space(): # Thorium top file location 'thorium_top': six.string_types, -} +}) # default configurations -DEFAULT_MINION_OPTS = { +DEFAULT_MINION_OPTS = immutabletypes.freeze({ 'interface': '0.0.0.0', 'master': 'salt', 'master_type': 'str', @@ -1222,6 +1218,7 @@ def _gather_buffer_space(): 'cachedir': os.path.join(salt.syspaths.CACHE_DIR, 'minion'), 'append_minionid_config_dirs': [], 'cache_jobs': False, + 'grains_blacklist': [], 'grains_cache': False, 'grains_cache_expiration': 300, 'grains_deep_merge': False, @@ -1439,6 +1436,7 @@ def _gather_buffer_space(): 'grains_refresh_every': 0, 'minion_id_caching': True, 'minion_id_lowercase': False, + 'minion_id_remove_domain': False, 'keysize': 2048, 'transport': 'zeromq', 'auth_timeout': 5, @@ -1477,17 +1475,12 @@ def _gather_buffer_space(): 'extmod_whitelist': {}, 'extmod_blacklist': {}, 'minion_sign_messages': False, - 'docker.compare_container_networks': { - 'static': ['Aliases', 'Links', 'IPAMConfig'], - 'automatic': ['IPAddress', 'Gateway', - 'GlobalIPv6Address', 'IPv6Gateway'], - }, 'discovery': False, 'schedule': {}, 'ssh_merge_pillar': True -} +}) -DEFAULT_MASTER_OPTS = { +DEFAULT_MASTER_OPTS = immutabletypes.freeze({ 'interface': '0.0.0.0', 'publish_port': 4505, 'zmq_backlog': 1000, @@ -1585,8 +1578,6 @@ def _gather_buffer_space(): 'hgfs_root': '', 'hgfs_base': 'default', 'hgfs_branch_method': 'branches', - 'hgfs_env_whitelist': [], - 'hgfs_env_blacklist': [], 'hgfs_saltenv_whitelist': [], 'hgfs_saltenv_blacklist': [], 'show_timeout': True, @@ -1598,8 +1589,6 @@ def _gather_buffer_space(): 'svnfs_trunk': 'trunk', 'svnfs_branches': 'branches', 'svnfs_tags': 'tags', - 'svnfs_env_whitelist': [], - 'svnfs_env_blacklist': [], 'svnfs_saltenv_whitelist': [], 'svnfs_saltenv_blacklist': [], 'max_event_size': 1048576, @@ -1812,12 +1801,12 @@ def _gather_buffer_space(): 'auth_events': True, 'minion_data_cache_events': True, 'enable_ssh_minions': False, -} +}) # ----- Salt Proxy Minion Configuration Defaults -----------------------------------> # These are merged with DEFAULT_MINION_OPTS since many of them also apply here. -DEFAULT_PROXY_MINION_OPTS = { +DEFAULT_PROXY_MINION_OPTS = immutabletypes.freeze({ 'conf_file': os.path.join(salt.syspaths.CONFIG_DIR, 'proxy'), 'log_file': os.path.join(salt.syspaths.LOGS_DIR, 'proxy'), 'add_proxymodule_to_opts': False, @@ -1843,9 +1832,10 @@ def _gather_buffer_space(): 'pki_dir': os.path.join(salt.syspaths.CONFIG_DIR, 'pki', 'proxy'), 'cachedir': os.path.join(salt.syspaths.CACHE_DIR, 'proxy'), 'sock_dir': os.path.join(salt.syspaths.SOCK_DIR, 'proxy'), -} +}) + # ----- Salt Cloud Configuration Defaults -----------------------------------> -DEFAULT_CLOUD_OPTS = { +DEFAULT_CLOUD_OPTS = immutabletypes.freeze({ 'verify_env': True, 'default_include': 'cloud.conf.d/*.conf', # Global defaults @@ -1873,17 +1863,17 @@ def _gather_buffer_space(): 'log_rotate_backup_count': 0, 'bootstrap_delay': None, 'cache': 'localfs', -} +}) -DEFAULT_API_OPTS = { +DEFAULT_API_OPTS = immutabletypes.freeze({ # ----- Salt master settings overridden by Salt-API ---------------------> 'api_pidfile': os.path.join(salt.syspaths.PIDFILE_DIR, 'salt-api.pid'), 'api_logfile': os.path.join(salt.syspaths.LOGS_DIR, 'api'), 'rest_timeout': 300, # <---- Salt master settings overridden by Salt-API ---------------------- -} +}) -DEFAULT_SPM_OPTS = { +DEFAULT_SPM_OPTS = immutabletypes.freeze({ # ----- Salt master settings overridden by SPM ---------------------> 'spm_conf_file': os.path.join(salt.syspaths.CONFIG_DIR, 'spm'), 'formula_path': salt.syspaths.SPM_FORMULA_PATH, @@ -1904,15 +1894,15 @@ def _gather_buffer_space(): 'spm_node_type': '', 'spm_share_dir': os.path.join(salt.syspaths.SHARE_DIR, 'spm'), # <---- Salt master settings overridden by SPM ---------------------- -} +}) -VM_CONFIG_DEFAULTS = { +VM_CONFIG_DEFAULTS = immutabletypes.freeze({ 'default_include': 'cloud.profiles.d/*.conf', -} +}) -PROVIDER_CONFIG_DEFAULTS = { +PROVIDER_CONFIG_DEFAULTS = immutabletypes.freeze({ 'default_include': 'cloud.providers.d/*.conf', -} +}) # <---- Salt Cloud Configuration Defaults ------------------------------------ @@ -2456,10 +2446,10 @@ def syndic_config(master_config_path, master_defaults=None): if minion_defaults is None: - minion_defaults = DEFAULT_MINION_OPTS + minion_defaults = DEFAULT_MINION_OPTS.copy() if master_defaults is None: - master_defaults = DEFAULT_MASTER_OPTS + master_defaults = DEFAULT_MASTER_OPTS.copy() opts = {} master_opts = master_config( @@ -2778,7 +2768,7 @@ def apply_cloud_config(overrides, defaults=None): Return a cloud config ''' if defaults is None: - defaults = DEFAULT_CLOUD_OPTS + defaults = DEFAULT_CLOUD_OPTS.copy() config = defaults.copy() if overrides: @@ -3563,6 +3553,26 @@ def call_id_function(opts): sys.exit(salt.defaults.exitcodes.EX_GENERIC) +def remove_domain_from_fqdn(opts, newid): + ''' + Depending on the values of `minion_id_remove_domain`, + remove all domains or a single domain from a FQDN, effectivly generating a hostname. + ''' + opt_domain = opts.get('minion_id_remove_domain') + if opt_domain is True: + if '.' in newid: + # Remove any domain + newid, xdomain = newid.split('.', 1) + log.debug('Removed any domain (%s) from minion id.', xdomain) + else: + # Must be string type + if newid.upper().endswith('.' + opt_domain.upper()): + # Remove single domain + newid = newid[:-len('.' + opt_domain)] + log.debug('Removed single domain %s from minion id.', opt_domain) + return newid + + def get_id(opts, cache_minion_id=False): ''' Guess the id of the minion. @@ -3614,6 +3624,11 @@ def get_id(opts, cache_minion_id=False): if opts.get('minion_id_lowercase'): newid = newid.lower() log.debug('Changed minion id %s to lowercase.', newid) + + # Optionally remove one or many domains in a generated minion id + if opts.get('minion_id_remove_domain'): + newid = remove_domain_from_fqdn(opts, newid) + if '__role' in opts and opts.get('__role') == 'minion': if opts.get('id_function'): log.debug( @@ -3678,7 +3693,7 @@ def apply_minion_config(overrides=None, Returns minion configurations dict. ''' if defaults is None: - defaults = DEFAULT_MINION_OPTS + defaults = DEFAULT_MINION_OPTS.copy() if overrides is None: overrides = {} @@ -3833,7 +3848,7 @@ def master_config(path, env_var='SALT_MASTER_CONFIG', defaults=None, exit_on_con :py:func:`salt.client.client_config`. ''' if defaults is None: - defaults = DEFAULT_MASTER_OPTS + defaults = DEFAULT_MASTER_OPTS.copy() if not os.environ.get(env_var, None): # No valid setting was given using the configuration variable. @@ -3875,7 +3890,7 @@ def apply_master_config(overrides=None, defaults=None): Returns master configurations dict. ''' if defaults is None: - defaults = DEFAULT_MASTER_OPTS + defaults = DEFAULT_MASTER_OPTS.copy() if overrides is None: overrides = {} @@ -4050,7 +4065,7 @@ def client_config(path, env_var='SALT_CLIENT_CONFIG', defaults=None): :py:class:`~salt.client.LocalClient`. ''' if defaults is None: - defaults = DEFAULT_MASTER_OPTS + defaults = DEFAULT_MASTER_OPTS.copy() xdg_dir = salt.utils.xdg.xdg_config_dir() if os.path.isdir(xdg_dir): @@ -4118,10 +4133,10 @@ def api_config(path): need to be stubbed out for salt-api ''' # Let's grab a copy of salt-api's required defaults - opts = DEFAULT_API_OPTS + opts = DEFAULT_API_OPTS.copy() # Let's override them with salt's master opts - opts.update(client_config(path, defaults=DEFAULT_MASTER_OPTS)) + opts.update(client_config(path, defaults=DEFAULT_MASTER_OPTS.copy())) # Let's set the pidfile and log_file values in opts to api settings opts.update({ diff --git a/salt/crypt.py b/salt/crypt.py index f944f4bbf2c7..b36370f8622e 100644 --- a/salt/crypt.py +++ b/salt/crypt.py @@ -595,10 +595,9 @@ def _authenticate(self): acceptance_wait_time_max = acceptance_wait_time creds = None - channel = salt.transport.client.AsyncReqChannel.factory(self.opts, - crypt='clear', - io_loop=self.io_loop) - try: + with salt.transport.client.AsyncReqChannel.factory(self.opts, + crypt='clear', + io_loop=self.io_loop) as channel: error = None while True: try: @@ -652,13 +651,8 @@ def _authenticate(self): self._authenticate_future.set_result(True) # mark the sign-in as complete # Notify the bus about creds change if self.opts.get('auth_events') is True: - event = salt.utils.event.get_event(self.opts.get('__role'), opts=self.opts, listen=False) - event.fire_event( - {'key': key, 'creds': creds}, - salt.utils.event.tagify(prefix='auth', suffix='creds') - ) - finally: - channel.close() + with salt.utils.event.get_event(self.opts.get('__role'), opts=self.opts, listen=False) as event: + event.fire_event({'key': key, 'creds': creds}, salt.utils.event.tagify(prefix='auth', suffix='creds')) @tornado.gen.coroutine def sign_in(self, timeout=60, safe=True, tries=1, channel=None): @@ -1223,10 +1217,9 @@ def authenticate(self, _=None): # TODO: remove unused var ''' acceptance_wait_time = self.opts['acceptance_wait_time'] acceptance_wait_time_max = self.opts['acceptance_wait_time_max'] - channel = salt.transport.client.ReqChannel.factory(self.opts, crypt='clear') if not acceptance_wait_time_max: acceptance_wait_time_max = acceptance_wait_time - try: + with salt.transport.client.ReqChannel.factory(self.opts, crypt='clear') as channel: while True: creds = self.sign_in(channel=channel) if creds == 'retry': @@ -1252,8 +1245,6 @@ def authenticate(self, _=None): # TODO: remove unused var break self._creds = creds self._crypticle = Crypticle(self.opts, creds['aes']) - finally: - channel.close() def sign_in(self, timeout=60, safe=True, tries=1, channel=None): ''' diff --git a/salt/engines/__init__.py b/salt/engines/__init__.py index 7b4a08a4038a..8a9315e41592 100644 --- a/salt/engines/__init__.py +++ b/salt/engines/__init__.py @@ -12,7 +12,7 @@ import salt import salt.loader import salt.utils.platform -from salt.utils.process import SignalHandlingMultiprocessingProcess +from salt.utils.process import SignalHandlingProcess log = logging.getLogger(__name__) @@ -65,7 +65,7 @@ def start_engines(opts, proc_mgr, proxy=None): ) -class Engine(SignalHandlingMultiprocessingProcess): +class Engine(SignalHandlingProcess): ''' Execute the given engine in a new process ''' @@ -85,7 +85,6 @@ def __init__(self, opts, fun, config, funcs, runners, proxy, **kwargs): # We do this so that __init__ will be invoked on Windows in the child # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): - self._is_child = True self.__init__( state['opts'], state['fun'], diff --git a/salt/engines/hipchat.py b/salt/engines/hipchat.py deleted file mode 100644 index d25e428e72ea..000000000000 --- a/salt/engines/hipchat.py +++ /dev/null @@ -1,429 +0,0 @@ -# -*- coding: utf-8 -*- -''' -An engine that reads messages from Hipchat and sends them to the Salt -event bus. Alternatively Salt commands can be sent to the Salt master -via Hipchat by setting the control parameter to ``True`` and using command -prefaced with a ``!``. Only token key is required, but room and control -keys make the engine interactive. - -.. versionadded: 2016.11.0 - -:depends: hypchat -:configuration: Example configuration - - .. code-block:: yaml - - engines: - - hipchat: - api_url: http://api.hipchat.myteam.com - token: 'XXXXXX' - room: 'salt' - control: True - valid_users: - - SomeUser - valid_commands: - - test.ping - - cmd.run - - list_jobs - - list_commands - aliases: - list_jobs: - cmd: jobs.list_jobs - list_commands: - cmd: pillar.get salt:engines:hipchat:valid_commands target=saltmaster - max_rooms: 0 - wait_time: 1 -''' - -from __future__ import absolute_import, print_function, unicode_literals -import logging -import time -import os - - -try: - import hypchat -except ImportError: - hypchat = None - -import salt.utils.args -import salt.utils.event -import salt.utils.files -import salt.utils.http -import salt.utils.json -import salt.utils.stringutils -import salt.runner -import salt.client -import salt.loader -import salt.output -from salt.ext import six - -log = logging.getLogger(__name__) - -_DEFAULT_API_URL = 'https://api.hipchat.com' -_DEFAULT_SLEEP = 5 -_DEFAULT_MAX_ROOMS = 1000 - -__virtualname__ = 'hipchat' - - -def __virtual__(): - return __virtualname__ if hypchat is not None \ - else (False, 'hypchat is not installed') - - -def _publish_file(token, room, filepath, message='', outputter=None, api_url=None): - ''' - Send file to a HipChat room via API version 2 - - Parameters - ---------- - token : str - HipChat API version 2 compatible token - must be token for active user - room: str - Name or API ID of the room to notify - filepath: str - Full path of file to be sent - message: str, optional - Message to send to room - api_url: str, optional - Hipchat API URL to use, defaults to http://api.hipchat.com - ''' - - if not os.path.isfile(filepath): - raise ValueError("File '{0}' does not exist".format(filepath)) - if len(message) > 1000: - raise ValueError('Message too long') - - url = "{0}/v2/room/{1}/share/file".format(api_url, room) - headers = {'Content-type': 'multipart/related; boundary=boundary123456'} - headers['Authorization'] = "Bearer " + token - msg = salt.utils.json.dumps({'message': message}) - - # future lint: disable=blacklisted-function - with salt.utils.files.fopen(filepath, 'rb') as rfh: - payload = str('''\ ---boundary123456 -Content-Type: application/json; charset=UTF-8 -Content-Disposition: attachment; name="metadata" - -{0} - ---boundary123456 -Content-Disposition: attachment; name="file"; filename="{1}" - -{2} - ---boundary123456--\ -''').format(msg, - os.path.basename(salt.utils.stringutils.to_str(filepath)), - salt.utils.stringutils.to_str(rfh.read())) - # future lint: enable=blacklisted-function - - salt.utils.http.query(url, method='POST', header_dict=headers, data=payload) - - -def _publish_html_message(token, room, data, message='', outputter='nested', api_url=None): - ''' - Publishes the HTML-formatted message. - ''' - url = "{0}/v2/room/{1}/notification".format(api_url, room) - headers = { - 'Content-type': 'text/plain' - } - headers['Authorization'] = 'Bearer ' + token - salt.utils.http.query( - url, - 'POST', - data=message, - decode=True, - status=True, - header_dict=headers, - opts=__opts__, - ) - headers['Content-type'] = 'text/html' - message = salt.output.html_format(data, outputter, opts=__opts__) - salt.utils.http.query( - url, - 'POST', - data=message, - decode=True, - status=True, - header_dict=headers, - opts=__opts__, - ) - - -def _publish_code_message(token, room, data, message='', outputter='nested', api_url=None): - ''' - Publishes the output format as code. - ''' - url = "{0}/v2/room/{1}/notification".format(api_url, room) - headers = { - 'Content-type': 'text/plain' - } - headers['Authorization'] = 'Bearer ' + token - salt.utils.http.query( - url, - 'POST', - data=message, - decode=True, - status=True, - header_dict=headers, - opts=__opts__, - ) - message = '/code ' - message += salt.output.string_format(data, outputter, opts=__opts__) - salt.utils.http.query( - url, - 'POST', - data=message, - decode=True, - status=True, - header_dict=headers, - opts=__opts__, - ) - - -def start(token, - room='salt', - aliases=None, - valid_users=None, - valid_commands=None, - control=False, - trigger="!", - tag='salt/engines/hipchat/incoming', - api_key=None, - api_url=None, - max_rooms=None, - wait_time=None, - output_type='file', - outputter='nested'): - ''' - Listen to Hipchat messages and forward them to Salt. - - token - The HipChat API key. It requires a key for global usgae, - assigned per user, rather than room. - - room - The HipChat room name. - - aliases - Define custom aliases. - - valid_users - Restrict access only to certain users. - - valid_commands - Restrict the execution to a limited set of commands. - - control - Send commands to the master. - - trigger: ``!`` - Special character that triggers the execution of salt commands. - - tag: ``salt/engines/hipchat/incoming`` - The event tag on the Salt bus. - - api_url: ``https://api.hipchat.com`` - The URL to the HipChat API. - - .. versionadded:: 2017.7.0 - - max_rooms: ``1000`` - Maximum number of rooms allowed to fetch. If set to 0, - it is able to retrieve the entire list of rooms. - - wait_time: ``5`` - Maximum wait time, in seconds. - - output_type: ``file`` - The type of the output. Choose bewteen: - - - ``file``: save the output into a temporary file and upload - - ``html``: send the output as HTML - - ``code``: send the output as code - - This can be overridden when executing a command, using the ``--out-type`` argument. - - .. versionadded:: 2017.7.0 - - outputter: ``nested`` - The format to display the data, using the outputters available on the CLI. - This argument can also be overridden when executing a command, using the ``--out`` option. - - .. versionadded:: 2017.7.0 - - HipChat Example: - - .. code-block:: text - - ! test.ping - ! test.ping target=minion1 - ! test.ping --out=nested - ! test.ping --out-type=code --out=table - ''' - target_room = None - - if __opts__.get('__role') == 'master': - fire_master = salt.utils.event.get_master_event( - __opts__, - __opts__['sock_dir']).fire_event - else: - fire_master = None - - def fire(tag, msg): - ''' - fire event to salt bus - ''' - - if fire_master: - fire_master(msg, tag) - else: - __salt__['event.send'](tag, msg) - - def _eval_bot_mentions(all_messages, trigger): - ''' yield partner message ''' - for message in all_messages: - message_text = message['message'] - if message_text.startswith(trigger): - fire(tag, message) - text = message_text.replace(trigger, '').strip() - yield message['from']['mention_name'], text - - token = token or api_key - if not token: - raise UserWarning("Hipchat token not found") - - runner_functions = sorted(salt.runner.Runner(__opts__).functions) - - if not api_url: - api_url = _DEFAULT_API_URL - hipc = hypchat.HypChat(token, endpoint=api_url) - if not hipc: - raise UserWarning("Unable to connect to hipchat") - - log.debug('Connected to Hipchat') - rooms_kwargs = {} - if max_rooms is None: - max_rooms = _DEFAULT_MAX_ROOMS - rooms_kwargs['max_results'] = max_rooms - elif max_rooms > 0: - rooms_kwargs['max_results'] = max_rooms - # if max_rooms is 0 => retrieve all (rooms_kwargs is empty dict) - all_rooms = hipc.rooms(**rooms_kwargs)['items'] - for a_room in all_rooms: - if a_room['name'] == room: - target_room = a_room - if not target_room: - log.debug("Unable to connect to room %s", room) - # wait for a bit as to not burn through api calls - time.sleep(30) - raise UserWarning("Unable to connect to room {0}".format(room)) - - after_message_id = target_room.latest(maxResults=1)['items'][0]['id'] - - while True: - try: - new_messages = target_room.latest( - not_before=after_message_id)['items'] - except hypchat.requests.HttpServiceUnavailable: - time.sleep(15) - continue - - after_message_id = new_messages[-1]['id'] - for partner, text in _eval_bot_mentions(new_messages[1:], trigger): - # bot summoned by partner - - if not control: - log.debug("Engine not configured for control") - return - - # Ensure the user is allowed to run commands - if valid_users: - if partner not in valid_users: - target_room.message('{0} not authorized to run Salt commands'.format(partner)) - return - - args = [] - kwargs = {} - - cmdline = salt.utils.args.shlex_split(text) - cmd = cmdline[0] - - # Evaluate aliases - if aliases and isinstance(aliases, dict) and cmd in aliases.keys(): - cmdline = aliases[cmd].get('cmd') - cmdline = salt.utils.args.shlex_split(cmdline) - cmd = cmdline[0] - - # Parse args and kwargs - if len(cmdline) > 1: - for item in cmdline[1:]: - if '=' in item: - (key, value) = item.split('=', 1) - kwargs[key] = value - else: - args.append(item) - - # Check for target. Otherwise assume * - if 'target' not in kwargs: - target = '*' - else: - target = kwargs['target'] - del kwargs['target'] - - # Check for tgt_type. Otherwise assume glob - if 'tgt_type' not in kwargs: - tgt_type = 'glob' - else: - tgt_type = kwargs['tgt_type'] - del kwargs['tgt_type'] - - # Check for outputter. Otherwise assume nested - if '--out' in kwargs: - outputter = kwargs['--out'] - del kwargs['--out'] - - # Check for outputter. Otherwise assume nested - if '--out-type' in kwargs: - output_type = kwargs['--out-type'] - del kwargs['--out-type'] - - # Ensure the command is allowed - if valid_commands: - if cmd not in valid_commands: - target_room.message('Using {0} is not allowed.'.format(cmd)) - return - - ret = {} - if cmd in runner_functions: - runner = salt.runner.RunnerClient(__opts__) - ret = runner.cmd(cmd, arg=args, kwarg=kwargs) - - # Default to trying to run as a client module. - else: - local = salt.client.LocalClient() - ret = local.cmd('{0}'.format(target), cmd, args, kwargs, tgt_type='{0}'.format(tgt_type)) - - nice_args = (' ' + ' '.join(args)) if args else '' - nice_kwargs = (' ' + ' '.join('{0}={1}'.format(key, value) for (key, value) in six.iteritems(kwargs))) \ - if kwargs else '' - message_string = '@{0} Results for: {1}{2}{3} on {4}'.format(partner, - cmd, - nice_args, - nice_kwargs, - target) - if output_type == 'html': - _publish_html_message(token, room, ret, message=message_string, outputter=outputter, api_url=api_url) - elif output_type == 'code': - _publish_code_message(token, room, ret, message=message_string, outputter=outputter, api_url=api_url) - else: - tmp_path_fn = salt.utils.files.mkstemp() - with salt.utils.files.fopen(tmp_path_fn, 'w+') as fp_: - salt.utils.json.dump(ret, fp_, sort_keys=True, indent=4) - _publish_file(token, room, tmp_path_fn, message=message_string, api_url=api_url) - salt.utils.files.safe_rm(tmp_path_fn) - time.sleep(wait_time or _DEFAULT_SLEEP) diff --git a/salt/engines/http_logstash.py b/salt/engines/http_logstash.py index 9a07319809fb..fdb4c83a55d9 100644 --- a/salt/engines/http_logstash.py +++ b/salt/engines/http_logstash.py @@ -98,22 +98,24 @@ def start(url, funs=None, tags=None): instance = 'master' else: instance = 'minion' - event_bus = salt.utils.event.get_event(instance, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'], - opts=__opts__) - while True: - event = event_bus.get_event(full=True) - if event: - publish = True - if tags and isinstance(tags, list): - found_match = False - for tag in tags: - if fnmatch.fnmatch(event['tag'], tag): - found_match = True - publish = found_match - if funs and 'fun' in event['data']: - if not event['data']['fun'] in funs: - publish = False - if publish: - _logstash(url, event['data']) + with salt.utils.event.get_event( + instance, + sock_dir=__opts__['sock_dir'], + transport=__opts__['transport'], + opts=__opts__, + ) as event_bus: + while True: + event = event_bus.get_event(full=True) + if event: + publish = True + if tags and isinstance(tags, list): + found_match = False + for tag in tags: + if fnmatch.fnmatch(event['tag'], tag): + found_match = True + publish = found_match + if funs and 'fun' in event['data']: + if not event['data']['fun'] in funs: + publish = False + if publish: + _logstash(url, event['data']) diff --git a/salt/engines/logentries.py b/salt/engines/logentries.py index c84698de94b6..a2cd05e91e19 100644 --- a/salt/engines/logentries.py +++ b/salt/engines/logentries.py @@ -171,6 +171,22 @@ def open_connection(self): SocketAppender = TLSSocketAppender +def event_bus_context(opts): + if opts.get('id').endswith('_master'): + event_bus = salt.utils.event.get_master_event( + opts, + opts['sock_dir'], + listen=True) + else: + event_bus = salt.utils.event.get_event( + 'minion', + transport=opts['transport'], + opts=opts, + sock_dir=opts['sock_dir'], + listen=True) + return event_bus + + def start(endpoint='data.logentries.com', port=10000, token=None, @@ -178,38 +194,26 @@ def start(endpoint='data.logentries.com', ''' Listen to salt events and forward them to Logentries ''' - if __opts__.get('id').endswith('_master'): - event_bus = salt.utils.event.get_master_event( - __opts__, - __opts__['sock_dir'], - listen=True) - else: - event_bus = salt.utils.event.get_event( - 'minion', - transport=__opts__['transport'], - opts=__opts__, - sock_dir=__opts__['sock_dir'], - listen=True) - log.debug('Logentries engine started') - - try: - val = uuid.UUID(token) - except ValueError: - log.warning('Not a valid logentries token') - - appender = SocketAppender(verbose=False, LE_API=endpoint, LE_PORT=port) - appender.reopen_connection() - - while True: - event = event_bus.get_event() - if event: - # future lint: disable=blacklisted-function - msg = str(' ').join(( - salt.utils.stringutils.to_str(token), - salt.utils.stringutils.to_str(tag), - salt.utils.json.dumps(event) - )) - # future lint: enable=blacklisted-function - appender.put(msg) - - appender.close_connection() + with event_bus_context(__opts__) as event_bus: + log.debug('Logentries engine started') + try: + val = uuid.UUID(token) + except ValueError: + log.warning('Not a valid logentries token') + + appender = SocketAppender(verbose=False, LE_API=endpoint, LE_PORT=port) + appender.reopen_connection() + + while True: + event = event_bus.get_event() + if event: + # future lint: disable=blacklisted-function + msg = str(' ').join(( + salt.utils.stringutils.to_str(token), + salt.utils.stringutils.to_str(tag), + salt.utils.json.dumps(event) + )) + # future lint: enable=blacklisted-function + appender.put(msg) + + appender.close_connection() diff --git a/salt/engines/logstash_engine.py b/salt/engines/logstash_engine.py index fa323312240c..78a7c21d5392 100644 --- a/salt/engines/logstash_engine.py +++ b/salt/engines/logstash_engine.py @@ -44,6 +44,22 @@ def __virtual__(): else (False, 'python-logstash not installed') +def event_bus_context(opts): + if opts.get('id').endswith('_master'): + event_bus = salt.utils.event.get_master_event( + opts, + opts['sock_dir'], + listen=True) + else: + event_bus = salt.utils.event.get_event( + 'minion', + transport=opts['transport'], + opts=opts, + sock_dir=opts['sock_dir'], + listen=True) + return event_bus + + def start(host, port=5959, tag='salt/engine/logstash', proto='udp'): ''' Listen to salt events and forward them to logstash @@ -58,21 +74,9 @@ def start(host, port=5959, tag='salt/engine/logstash', proto='udp'): logstash_logger.setLevel(logging.INFO) logstash_logger.addHandler(logstashHandler(host, port, version=1)) - if __opts__.get('id').endswith('_master'): - event_bus = salt.utils.event.get_master_event( - __opts__, - __opts__['sock_dir'], - listen=True) - else: - event_bus = salt.utils.event.get_event( - 'minion', - transport=__opts__['transport'], - opts=__opts__, - sock_dir=__opts__['sock_dir'], - listen=True) + with event_bus_context(__opts__) as event_bus: log.debug('Logstash engine started') - - while True: - event = event_bus.get_event() - if event: - logstash_logger.info(tag, extra=event) + while True: + event = event_bus.get_event() + if event: + logstash_logger.info(tag, extra=event) diff --git a/salt/engines/stalekey.py b/salt/engines/stalekey.py index 79707be4a5fb..4b8cd916b66c 100644 --- a/salt/engines/stalekey.py +++ b/salt/engines/stalekey.py @@ -28,11 +28,11 @@ import salt.key import salt.utils.files import salt.utils.minions +import salt.utils.msgpack import salt.wheel # Import 3rd-party libs from salt.ext import six -import msgpack log = logging.getLogger(__name__) @@ -60,7 +60,7 @@ def start(interval=3600, expire=604800): if os.path.exists(presence_file): try: with salt.utils.files.fopen(presence_file, 'r') as f: - minions = msgpack.load(f) + minions = salt.utils.msgpack.load(f) except IOError as e: log.error('Could not open presence file %s: %s', presence_file, e) time.sleep(interval) @@ -95,7 +95,7 @@ def start(interval=3600, expire=604800): try: with salt.utils.files.fopen(presence_file, 'w') as f: - msgpack.dump(minions, f) + salt.utils.msgpack.dump(minions, f) except IOError as e: log.error('Could not write to presence file %s: %s', presence_file, e) time.sleep(interval) diff --git a/salt/engines/test.py b/salt/engines/test.py index a078e403ab1d..e91dbe596125 100644 --- a/salt/engines/test.py +++ b/salt/engines/test.py @@ -14,26 +14,30 @@ log = logging.getLogger(__name__) -def start(): - ''' - Listen to events and write them to a log file - ''' - if __opts__['__role'] == 'master': +def event_bus_context(opts): + if opts['__role'] == 'master': event_bus = salt.utils.event.get_master_event( - __opts__, - __opts__['sock_dir'], + opts, + opts['sock_dir'], listen=True) else: event_bus = salt.utils.event.get_event( 'minion', - transport=__opts__['transport'], - opts=__opts__, - sock_dir=__opts__['sock_dir'], + transport=opts['transport'], + opts=opts, + sock_dir=opts['sock_dir'], listen=True) log.debug('test engine started') + return event_bus + - while True: - event = event_bus.get_event() - jevent = salt.utils.json.dumps(event) - if event: - log.debug(jevent) +def start(): + ''' + Listen to events and write them to a log file + ''' + with event_bus_context(__opts__) as event_bus: + while True: + event = event_bus.get_event() + jevent = salt.utils.json.dumps(event) + if event: + log.debug(jevent) diff --git a/salt/exceptions.py b/salt/exceptions.py index f3cb1acbaccb..cb696f0fe4e2 100644 --- a/salt/exceptions.py +++ b/salt/exceptions.py @@ -572,3 +572,9 @@ class MissingSmb(SaltException): ''' Raised when no smb library is found. ''' + + +class LoggingRuntimeError(RuntimeError): + ''' + Raised when we encounter an error while logging + ''' diff --git a/salt/executors/splay.py b/salt/executors/splay.py index 21ecd1d9e4f9..8c815a1fdd7d 100644 --- a/salt/executors/splay.py +++ b/salt/executors/splay.py @@ -68,7 +68,7 @@ def execute(opts, data, func, args, kwargs): .. code-block:: bash # With specified splaytime (5 minutes) and timeout with 10 second buffer - salt -t 310 --module-executors='[slpay, direct_call]' --executor-opts='{splaytime: 300}' '*' pkg.version cowsay + salt -t 310 --module-executors='[splay, direct_call]' --executor-opts='{splaytime: 300}' '*' pkg.version cowsay ''' if 'executor_opts' in data and 'splaytime' in data['executor_opts']: splaytime = data['executor_opts']['splaytime'] diff --git a/salt/fileclient.py b/salt/fileclient.py index 718f957c2098..cb648b283ca9 100644 --- a/salt/fileclient.py +++ b/salt/fileclient.py @@ -1018,11 +1018,16 @@ def _refresh_channel(self): ''' Reset the channel, in the event of an interruption ''' + # Close the previous channel + self.channel.close() + # Instantiate a new one self.channel = salt.transport.client.ReqChannel.factory(self.opts) return self.channel + # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 def destroy(self): if self._closing: diff --git a/salt/fileserver/hgfs.py b/salt/fileserver/hgfs.py index 3b15b6bc2d47..6cdf0e666767 100644 --- a/salt/fileserver/hgfs.py +++ b/salt/fileserver/hgfs.py @@ -558,13 +558,13 @@ def update(): # if there is a change, fire an event if __opts__.get('fileserver_events', False): - event = salt.utils.event.get_event( + with salt.utils.event.get_event( 'master', __opts__['sock_dir'], __opts__['transport'], opts=__opts__, - listen=False) - event.fire_event(data, tagify(['hgfs', 'update'], prefix='fileserver')) + listen=False) as event: + event.fire_event(data, tagify(['hgfs', 'update'], prefix='fileserver')) try: salt.fileserver.reap_fileserver_cache_dir( os.path.join(__opts__['cachedir'], 'hgfs/hash'), @@ -580,30 +580,10 @@ def _env_is_exposed(env): Check if an environment is exposed by comparing it against a whitelist and blacklist. ''' - if __opts__['hgfs_env_whitelist']: - salt.utils.versions.warn_until( - 'Neon', - 'The hgfs_env_whitelist config option has been renamed to ' - 'hgfs_saltenv_whitelist. Please update your configuration.' - ) - whitelist = __opts__['hgfs_env_whitelist'] - else: - whitelist = __opts__['hgfs_saltenv_whitelist'] - - if __opts__['hgfs_env_blacklist']: - salt.utils.versions.warn_until( - 'Neon', - 'The hgfs_env_blacklist config option has been renamed to ' - 'hgfs_saltenv_blacklist. Please update your configuration.' - ) - blacklist = __opts__['hgfs_env_blacklist'] - else: - blacklist = __opts__['hgfs_saltenv_blacklist'] - return salt.utils.stringutils.check_whitelist_blacklist( env, - whitelist=whitelist, - blacklist=blacklist, + whitelist=__opts__['hgfs_saltenv_whitelist'], + blacklist=__opts__['hgfs_saltenv_blacklist'], ) diff --git a/salt/fileserver/roots.py b/salt/fileserver/roots.py index 0466a1b9f9c7..cf75a76aca99 100644 --- a/salt/fileserver/roots.py +++ b/salt/fileserver/roots.py @@ -203,14 +203,15 @@ def update(): if __opts__.get('fileserver_events', False): # if there is a change, fire an event - event = salt.utils.event.get_event( + with salt.utils.event.get_event( 'master', __opts__['sock_dir'], __opts__['transport'], opts=__opts__, - listen=False) - event.fire_event(data, - salt.utils.event.tagify(['roots', 'update'], prefix='fileserver')) + listen=False) as event: + event.fire_event( + data, + salt.utils.event.tagify(['roots', 'update'], prefix='fileserver')) def file_hash(load, fnd): diff --git a/salt/fileserver/svnfs.py b/salt/fileserver/svnfs.py index 2ddcf3d52874..6bd3f042a098 100644 --- a/salt/fileserver/svnfs.py +++ b/salt/fileserver/svnfs.py @@ -471,13 +471,13 @@ def update(): # if there is a change, fire an event if __opts__.get('fileserver_events', False): - event = salt.utils.event.get_event( + with salt.utils.event.get_event( 'master', __opts__['sock_dir'], __opts__['transport'], opts=__opts__, - listen=False) - event.fire_event(data, tagify(['svnfs', 'update'], prefix='fileserver')) + listen=False) as event: + event.fire_event(data, tagify(['svnfs', 'update'], prefix='fileserver')) try: salt.fileserver.reap_fileserver_cache_dir( os.path.join(__opts__['cachedir'], 'svnfs/hash'), @@ -493,30 +493,10 @@ def _env_is_exposed(env): Check if an environment is exposed by comparing it against a whitelist and blacklist. ''' - if __opts__['svnfs_env_whitelist']: - salt.utils.versions.warn_until( - 'Neon', - 'The svnfs_env_whitelist config option has been renamed to ' - 'svnfs_saltenv_whitelist. Please update your configuration.' - ) - whitelist = __opts__['svnfs_env_whitelist'] - else: - whitelist = __opts__['svnfs_saltenv_whitelist'] - - if __opts__['svnfs_env_blacklist']: - salt.utils.versions.warn_until( - 'Neon', - 'The svnfs_env_blacklist config option has been renamed to ' - 'svnfs_saltenv_blacklist. Please update your configuration.' - ) - blacklist = __opts__['svnfs_env_blacklist'] - else: - blacklist = __opts__['svnfs_saltenv_blacklist'] - return salt.utils.stringutils.check_whitelist_blacklist( env, - whitelist=whitelist, - blacklist=blacklist, + whitelist=__opts__['svnfs_saltenv_whitelist'], + blacklist=__opts__['svnfs_saltenv_blacklist'], ) diff --git a/salt/grains/core.py b/salt/grains/core.py index 04c1ae91b5f5..c4d4d0dca5d1 100644 --- a/salt/grains/core.py +++ b/salt/grains/core.py @@ -104,6 +104,10 @@ def linux_distribution(**kwargs): _INTERFACES = {} +# Possible value for h_errno defined in netdb.h +HOST_NOT_FOUND = 1 +NO_DATA = 4 + def _windows_cpudata(): ''' @@ -625,7 +629,7 @@ def _windows_virtual(osdata): if osdata['kernel'] != 'Windows': return grains - grains['virtual'] = 'physical' + grains['virtual'] = osdata.get('virtual', 'physical') # It is possible that the 'manufacturer' and/or 'productname' grains # exist but have a value of None. @@ -680,7 +684,8 @@ def _virtual(osdata): # Provides: # virtual # virtual_subtype - grains = {'virtual': 'physical'} + + grains = {'virtual': osdata.get('virtual', 'physical')} # Skip the below loop on platforms which have none of the desired cmds # This is a temporary measure until we can write proper virtual hardware @@ -945,6 +950,7 @@ def _virtual(osdata): with salt.utils.files.fopen('/proc/1/cgroup', 'r') as fhr: fhr_contents = fhr.read() if ':/lxc/' in fhr_contents: + grains['virtual'] = 'container' grains['virtual_subtype'] = 'LXC' elif ':/kubepods/' in fhr_contents: grains['virtual_subtype'] = 'kubernetes' @@ -954,6 +960,7 @@ def _virtual(osdata): if any(x in fhr_contents for x in (':/system.slice/docker', ':/docker/', ':/docker-ce/')): + grains['virtual'] = 'container' grains['virtual_subtype'] = 'Docker' except IOError: pass @@ -1056,6 +1063,11 @@ def _virtual(osdata): if os.path.isfile('/var/run/xenconsoled.pid'): grains['virtual_subtype'] = 'Xen Dom0' + # If we have a virtual_subtype, we're virtual, but maybe we couldn't + # figure out what specific virtual type we were? + if grains.get('virtual_subtype') and grains['virtual'] == 'physical': + grains['virtual'] = 'virtual' + for command in failed_commands: log.info( "Although '%s' was found in path, the current user " @@ -2225,7 +2237,7 @@ def fqdns(): try: fqdns.add(socket.getfqdn(socket.gethostbyaddr(ip)[0])) except socket.herror as err: - if err.errno == 0: + if err.errno in (0, HOST_NOT_FOUND, NO_DATA): # No FQDN for this IP address, so we don't need to know this all the time. log.debug("Unable to resolve address %s: %s", ip, err) else: @@ -2403,6 +2415,13 @@ def get_machine_id(): return {'machine_id': machineid.read().strip()} +def cwd(): + ''' + Current working directory + ''' + return {'cwd': os.getcwd()} + + def path(): ''' Return the path @@ -2753,26 +2772,6 @@ def _hw_data(osdata): else: log.error('The \'prtconf\' binary was not found in $PATH.') - elif osdata['kernel'] == 'AIX': - cmd = salt.utils.path.which('prtconf') - if data: - data = __salt__['cmd.run']('{0}'.format(cmd)) + os.linesep - for dest, regstring in (('serialnumber', r'(?im)^\s*Machine\s+Serial\s+Number:\s+(\S+)'), - ('systemfirmware', r'(?im)^\s*Firmware\s+Version:\s+(.*)')): - for regex in [re.compile(r) for r in [regstring]]: - res = regex.search(data) - if res and len(res.groups()) >= 1: - grains[dest] = res.group(1).strip().replace("'", '') - - product_regexes = [re.compile(r'(?im)^\s*System\s+Model:\s+(\S+)')] - for regex in product_regexes: - res = regex.search(data) - if res and len(res.groups()) >= 1: - grains['manufacturer'], grains['productname'] = res.group(1).strip().replace("'", "").split(",") - break - else: - log.error('The \'prtconf\' binary was not found in $PATH.') - return grains diff --git a/salt/grains/mdata.py b/salt/grains/mdata.py index db207a1e52a9..4ba6465410cc 100644 --- a/salt/grains/mdata.py +++ b/salt/grains/mdata.py @@ -125,30 +125,6 @@ def _sdc_mdata(mdata_list=None, mdata_get=None): return grains -def _legacy_grains(grains): - ''' - Grains for backwards compatibility - Remove this function in Neon - ''' - # parse legacy sdc grains - if 'mdata' in grains and 'sdc' in grains['mdata']: - if 'server_uuid' not in grains['mdata']['sdc'] or 'FAILURE' in grains['mdata']['sdc']['server_uuid']: - grains['hypervisor_uuid'] = 'unknown' - else: - grains['hypervisor_uuid'] = grains['mdata']['sdc']['server_uuid'] - - if 'datacenter_name' not in grains['mdata']['sdc'] or 'FAILURE' in grains['mdata']['sdc']['datacenter_name']: - grains['datacenter'] = 'unknown' - else: - grains['datacenter'] = grains['mdata']['sdc']['datacenter_name'] - - # parse rules grains - if 'mdata' in grains and 'rules' in grains['mdata']: - grains['roles'] = grains['mdata']['roles'].split(',') - - return grains - - def mdata(): ''' Provide grains from the SmartOS metadata @@ -159,8 +135,6 @@ def mdata(): grains = salt.utils.dictupdate.update(grains, _user_mdata(mdata_list, mdata_get), merge_lists=True) grains = salt.utils.dictupdate.update(grains, _sdc_mdata(mdata_list, mdata_get), merge_lists=True) - ## remove _legacy_grains in Neon - grains = _legacy_grains(grains) return grains diff --git a/salt/grains/minion_process.py b/salt/grains/minion_process.py index 5ad95d5d8b55..b1120de617f1 100644 --- a/salt/grains/minion_process.py +++ b/salt/grains/minion_process.py @@ -3,75 +3,57 @@ Set grains describing the minion process. ''' +# Import Python Libs from __future__ import absolute_import, print_function, unicode_literals - import os # Import salt libs +import salt.utils.user import salt.utils.platform -try: - import pwd -except ImportError: - import getpass - pwd = None - -try: - import grp -except ImportError: - grp = None - def _uid(): ''' Grain for the minion User ID ''' - if salt.utils.platform.is_windows(): - return None - return os.getuid() + return salt.utils.user.get_uid() def _username(): ''' Grain for the minion username ''' - if pwd: - username = pwd.getpwuid(os.getuid()).pw_name - else: - username = getpass.getuser() - - return username + return salt.utils.user.get_user() def _gid(): ''' Grain for the minion Group ID ''' - if salt.utils.platform.is_windows(): - return None - return os.getgid() + return salt.utils.user.get_gid() def _groupname(): ''' Grain for the minion groupname ''' - if grp: - try: - groupname = grp.getgrgid(os.getgid()).gr_name - except KeyError: - groupname = '' - else: - groupname = '' - - return groupname + try: + return salt.utils.user.get_default_group(_username()) or '' + except KeyError: + return '' def _pid(): + ''' + Return the current process pid + ''' return os.getpid() def grains(): + ''' + Return the grains dictionary + ''' ret = { 'username': _username(), 'groupname': _groupname(), diff --git a/salt/grains/napalm.py b/salt/grains/napalm.py index 80dedbeb521f..d61ad9a38e03 100644 --- a/salt/grains/napalm.py +++ b/salt/grains/napalm.py @@ -3,7 +3,7 @@ NAPALM Grains ============= -:codeauthor: Mircea Ulinic +:codeauthor: Mircea Ulinic :maturity: new :depends: napalm :platform: unix diff --git a/salt/grains/nvme.py b/salt/grains/nvme.py new file mode 100644 index 000000000000..34726914b5b4 --- /dev/null +++ b/salt/grains/nvme.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +''' +Grains for NVMe Qualified Names (NQN). + +.. versionadded:: Flourine + +To enable these grains set `nvme_grains: True`. + +.. code-block:: yaml + + nvme_grains: True +''' +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +import errno +import logging + +# Import Salt libs +import salt.utils.files +import salt.utils.path +import salt.utils.platform + +__virtualname__ = 'nvme' + +# Get logging started +log = logging.getLogger(__name__) + + +def __virtual__(): + if __opts__.get('nvme_grains', False) is False: + return False + return __virtualname__ + + +def nvme_nqn(): + ''' + Return NVMe NQN + ''' + grains = {} + grains['nvme_nqn'] = False + if salt.utils.platform.is_linux(): + grains['nvme_nqn'] = _linux_nqn() + return grains + + +def _linux_nqn(): + ''' + Return NVMe NQN from a Linux host. + ''' + ret = [] + + initiator = '/etc/nvme/hostnqn' + try: + with salt.utils.files.fopen(initiator, 'r') as _nvme: + for line in _nvme: + line = line.strip() + if line.startswith('nqn.'): + ret.append(line) + except IOError as ex: + if ex.errno != errno.ENOENT: + log.debug("Error while accessing '%s': %s", initiator, ex) + + return ret diff --git a/salt/loader.py b/salt/loader.py index 0cff0c72bbbd..fe731bc87352 100644 --- a/salt/loader.py +++ b/salt/loader.py @@ -34,6 +34,7 @@ import salt.utils.odict import salt.utils.platform import salt.utils.versions +import salt.utils.stringutils from salt.exceptions import LoaderError from salt.template import check_render_pipe_str from salt.utils.decorators import Depends @@ -276,8 +277,8 @@ def minion_mods( ret[f_key] = funcs[func] if notify: - evt = salt.utils.event.get_event('minion', opts=opts, listen=False) - evt.fire_event({'complete': True}, tag='/salt/minion/minion_mod_complete') + with salt.utils.event.get_event('minion', opts=opts, listen=False) as evt: + evt.fire_event({'complete': True}, tag='/salt/minion/minion_mod_complete') return ret @@ -536,7 +537,7 @@ def thorium(opts, functions, runners): return ret -def states(opts, functions, utils, serializers, whitelist=None, proxy=None): +def states(opts, functions, utils, serializers, whitelist=None, proxy=None, context=None): ''' Returns the state modules @@ -552,6 +553,9 @@ def states(opts, functions, utils, serializers, whitelist=None, proxy=None): __opts__ = salt.config.minion_config('/etc/salt/minion') statemods = salt.loader.states(__opts__, None, None) ''' + if context is None: + context = {} + ret = LazyLoader( _module_dirs(opts, 'states'), opts, @@ -562,6 +566,7 @@ def states(opts, functions, utils, serializers, whitelist=None, proxy=None): ret.pack['__states__'] = ret ret.pack['__utils__'] = utils ret.pack['__serializers__'] = serializers + ret.pack['__context__'] = context return ret @@ -622,12 +627,17 @@ def ssh_wrapper(opts, functions=None, context=None): ) -def render(opts, functions, states=None, proxy=None): +def render(opts, functions, states=None, proxy=None, context=None): ''' Returns the render modules ''' + if context is None: + context = {} + pack = {'__salt__': functions, - '__grains__': opts.get('grains', {})} + '__grains__': opts.get('grains', {}), + '__context__': context} + if states: pack['__states__'] = states pack['__proxy__'] = proxy or {} @@ -773,6 +783,7 @@ def grains(opts, force_refresh=False, proxy=None): opts['grains'] = {} grains_data = {} + blist = opts.get('grains_blacklist', []) funcs = grain_funcs(opts, proxy=proxy) if force_refresh: # if we refresh, lets reload grain modules funcs.clear() @@ -784,6 +795,14 @@ def grains(opts, force_refresh=False, proxy=None): ret = funcs[key]() if not isinstance(ret, dict): continue + if blist: + for key in list(ret): + for block in blist: + if salt.utils.stringutils.expr_match(key, block): + del ret[key] + log.trace('Filtering %s grain', key) + if not ret: + continue if grains_deep_merge: salt.utils.dictupdate.update(grains_data, ret) else: @@ -819,6 +838,14 @@ def grains(opts, force_refresh=False, proxy=None): continue if not isinstance(ret, dict): continue + if blist: + for key in list(ret): + for block in blist: + if salt.utils.stringutils.expr_match(key, block): + del ret[key] + log.trace('Filtering %s grain', key) + if not ret: + continue if grains_deep_merge: salt.utils.dictupdate.update(grains_data, ret) else: @@ -939,7 +966,7 @@ def sdb(opts, functions=None, whitelist=None, utils=None): '__sdb__': functions, '__opts__': opts, '__utils__': utils, - '__salt__': minion_mods(opts, utils), + '__salt__': minion_mods(opts, utils=utils), }, whitelist=whitelist, ) @@ -1883,20 +1910,6 @@ def _process_virtual(self, mod, module_name, virtual_func='__virtual__'): # with the new name log.trace('Loaded %s as virtual %s', module_name, virtual) - if not hasattr(mod, '__virtualname__'): - salt.utils.versions.warn_until( - 'Hydrogen', - 'The \'{0}\' module is renaming itself in its ' - '__virtual__() function ({1} => {2}). Please ' - 'set it\'s virtual name as the ' - '\'__virtualname__\' module attribute. ' - 'Example: "__virtualname__ = \'{2}\'"'.format( - mod.__name__, - module_name, - virtual - ) - ) - if virtualname != virtual: # The __virtualname__ attribute does not match what's # being returned by the __virtual__() function. This diff --git a/salt/log/handlers/__init__.py b/salt/log/handlers/__init__.py index ce16c72af1b2..de262b972159 100644 --- a/salt/log/handlers/__init__.py +++ b/salt/log/handlers/__init__.py @@ -7,290 +7,26 @@ Custom logging handlers to be used in salt. ''' -from __future__ import absolute_import, print_function, unicode_literals # Import python libs -import sys -import copy +from __future__ import absolute_import, print_function, unicode_literals import logging -import threading -import logging.handlers # Import salt libs -from salt.log.mixins import NewStyleClassMixIn, ExcInfoOnLogLevelFormatMixIn -from salt.ext.six.moves import queue - -log = logging.getLogger(__name__) - - -if sys.version_info < (2, 7): - # Since the NullHandler is only available on python >= 2.7, here's a copy - # with NewStyleClassMixIn so it's also a new style class - class NullHandler(logging.Handler, NewStyleClassMixIn): - ''' - This is 1 to 1 copy of python's 2.7 NullHandler - ''' - def handle(self, record): - pass - - def emit(self, record): - pass - - def createLock(self): # pylint: disable=C0103 - self.lock = None - - logging.NullHandler = NullHandler - - -class TemporaryLoggingHandler(logging.NullHandler): - ''' - This logging handler will store all the log records up to its maximum - queue size at which stage the first messages stored will be dropped. - - Should only be used as a temporary logging handler, while the logging - system is not fully configured. - - Once configured, pass any logging handlers that should have received the - initial log messages to the function - :func:`TemporaryLoggingHandler.sync_with_handlers` and all stored log - records will be dispatched to the provided handlers. - - .. versionadded:: 0.17.0 - ''' - - def __init__(self, level=logging.NOTSET, max_queue_size=10000): - self.__max_queue_size = max_queue_size - super(TemporaryLoggingHandler, self).__init__(level=level) - self.__messages = [] - - def handle(self, record): - self.acquire() - if len(self.__messages) >= self.__max_queue_size: - # Loose the initial log records - self.__messages.pop(0) - self.__messages.append(record) - self.release() - - def sync_with_handlers(self, handlers=()): - ''' - Sync the stored log records to the provided log handlers. - ''' - if not handlers: - return - - while self.__messages: - record = self.__messages.pop(0) - for handler in handlers: - if handler.level > record.levelno: - # If the handler's level is higher than the log record one, - # it should not handle the log record - continue - handler.handle(record) - - -class StreamHandler(ExcInfoOnLogLevelFormatMixIn, logging.StreamHandler, NewStyleClassMixIn): - ''' - Stream handler which properly handles exc_info on a per handler basis - ''' - - -class FileHandler(ExcInfoOnLogLevelFormatMixIn, logging.FileHandler, NewStyleClassMixIn): - ''' - File handler which properly handles exc_info on a per handler basis - ''' - - -class SysLogHandler(ExcInfoOnLogLevelFormatMixIn, logging.handlers.SysLogHandler, NewStyleClassMixIn): - ''' - Syslog handler which properly handles exc_info on a per handler basis - ''' - def handleError(self, record): - ''' - Override the default error handling mechanism for py3 - Deal with syslog os errors when the log file does not exist - ''' - handled = False - if sys.stderr and sys.version_info >= (3, 5, 4): - t, v, tb = sys.exc_info() - if t.__name__ in 'FileNotFoundError': - sys.stderr.write('[WARNING ] The log_file does not exist. Logging not setup correctly or syslog service not started.\n') - handled = True - - if not handled: - super(SysLogHandler, self).handleError(record) - - -class RotatingFileHandler(ExcInfoOnLogLevelFormatMixIn, logging.handlers.RotatingFileHandler, NewStyleClassMixIn): - ''' - Rotating file handler which properly handles exc_info on a per handler basis - ''' - def handleError(self, record): - ''' - Override the default error handling mechanism - - Deal with log file rotation errors due to log file in use - more softly. - ''' - handled = False - - # Can't use "salt.utils.platform.is_windows()" in this file - if (sys.platform.startswith('win') and - logging.raiseExceptions and - sys.stderr): # see Python issue 13807 - exc_type, exc, exc_traceback = sys.exc_info() - try: - # PermissionError is used since Python 3.3. - # OSError is used for previous versions of Python. - if exc_type.__name__ in ('PermissionError', 'OSError') and exc.winerror == 32: - if self.level <= logging.WARNING: - sys.stderr.write('[WARNING ] Unable to rotate the log file "{0}" ' - 'because it is in use\n'.format(self.baseFilename) - ) - handled = True - finally: - # 'del' recommended. See documentation of - # 'sys.exc_info()' for details. - del exc_type, exc, exc_traceback - - if not handled: - super(RotatingFileHandler, self).handleError(record) - - -if sys.version_info > (2, 6): - class WatchedFileHandler(ExcInfoOnLogLevelFormatMixIn, logging.handlers.WatchedFileHandler, NewStyleClassMixIn): - ''' - Watched file handler which properly handles exc_info on a per handler basis - ''' - - -if sys.version_info < (3, 2): - class QueueHandler(ExcInfoOnLogLevelFormatMixIn, logging.Handler, NewStyleClassMixIn): - ''' - This handler sends events to a queue. Typically, it would be used together - with a multiprocessing Queue to centralise logging to file in one process - (in a multi-process application), so as to avoid file write contention - between processes. - - This code is new in Python 3.2, but this class can be copy pasted into - user code for use with earlier Python versions. - ''' - - def __init__(self, queue): - ''' - Initialise an instance, using the passed queue. - ''' - logging.Handler.__init__(self) - self.queue = queue - - def enqueue(self, record): - ''' - Enqueue a record. - - The base implementation uses put_nowait. You may want to override - this method if you want to use blocking, timeouts or custom queue - implementations. - ''' - try: - self.queue.put_nowait(record) - except queue.Full: - sys.stderr.write('[WARNING ] Message queue is full, ' - 'unable to write "{0}" to log'.format(record)) - - def prepare(self, record): - ''' - Prepares a record for queuing. The object returned by this method is - enqueued. - The base implementation formats the record to merge the message - and arguments, and removes unpickleable items from the record - in-place. - You might want to override this method if you want to convert - the record to a dict or JSON string, or send a modified copy - of the record while leaving the original intact. - ''' - # The format operation gets traceback text into record.exc_text - # (if there's exception data), and also returns the formatted - # message. We can then use this to replace the original - # msg + args, as these might be unpickleable. We also zap the - # exc_info and exc_text attributes, as they are no longer - # needed and, if not None, will typically not be pickleable. - msg = self.format(record) - # bpo-35726: make copy of record to avoid affecting other handlers in the chain. - record = copy.copy(record) - record.message = msg - record.msg = msg - record.args = None - record.exc_info = None - record.exc_text = None - return record - - def emit(self, record): - ''' - Emit a record. - - Writes the LogRecord to the queue, preparing it for pickling first. - ''' - try: - self.enqueue(self.prepare(record)) - except Exception: - self.handleError(record) -elif sys.version_info < (3, 7): - # On python versions lower than 3.7, we sill subclass and overwrite prepare to include the fix for: - # https://bugs.python.org/issue35726 - class QueueHandler(ExcInfoOnLogLevelFormatMixIn, logging.handlers.QueueHandler): # pylint: disable=no-member,E0240 - - def enqueue(self, record): - ''' - Enqueue a record. - - The base implementation uses put_nowait. You may want to override - this method if you want to use blocking, timeouts or custom queue - implementations. - ''' - try: - self.queue.put_nowait(record) - except queue.Full: - sys.stderr.write('[WARNING ] Message queue is full, ' - 'unable to write "{0}" to log'.format(record)) - - def prepare(self, record): - ''' - Prepares a record for queuing. The object returned by this method is - enqueued. - The base implementation formats the record to merge the message - and arguments, and removes unpickleable items from the record - in-place. - You might want to override this method if you want to convert - the record to a dict or JSON string, or send a modified copy - of the record while leaving the original intact. - ''' - # The format operation gets traceback text into record.exc_text - # (if there's exception data), and also returns the formatted - # message. We can then use this to replace the original - # msg + args, as these might be unpickleable. We also zap the - # exc_info and exc_text attributes, as they are no longer - # needed and, if not None, will typically not be pickleable. - msg = self.format(record) - # bpo-35726: make copy of record to avoid affecting other handlers in the chain. - record = copy.copy(record) - record.message = msg - record.msg = msg - record.args = None - record.exc_info = None - record.exc_text = None - return record -else: - class QueueHandler(ExcInfoOnLogLevelFormatMixIn, logging.handlers.QueueHandler): # pylint: disable=no-member,E0240 - - def enqueue(self, record): - ''' - Enqueue a record. - - The base implementation uses put_nowait. You may want to override - this method if you want to use blocking, timeouts or custom queue - implementations. - ''' - try: - self.queue.put_nowait(record) - except queue.Full: - sys.stderr.write('[WARNING ] Message queue is full, ' - 'unable to write "{0}" to log'.format(record)) +from salt._logging.handlers import TemporaryLoggingHandler +from salt._logging.handlers import StreamHandler +from salt._logging.handlers import FileHandler +from salt._logging.handlers import SysLogHandler +from salt._logging.handlers import RotatingFileHandler +from salt._logging.handlers import WatchedFileHandler +from salt._logging.handlers import QueueHandler +#from salt.utils.versions import warn_until_date +#warn_until_date( +# '20220101', +# 'Please stop using \'{name}\' and instead use \'salt._logging.handlers\'. ' +# '\'{name}\' will go away after {{date}}.'.format( +# name=__name__ +# ) +#) + +NullHandler = logging.NullHandler diff --git a/salt/log/handlers/fluent_mod.py b/salt/log/handlers/fluent_mod.py index 85fc13276831..06844077f0a0 100644 --- a/salt/log/handlers/fluent_mod.py +++ b/salt/log/handlers/fluent_mod.py @@ -86,6 +86,7 @@ # Import salt libs from salt.log.setup import LOG_LEVELS from salt.log.mixins import NewStyleClassMixIn +import salt.utils.msgpack import salt.utils.network # Import Third party libs @@ -93,26 +94,6 @@ log = logging.getLogger(__name__) -try: - # Attempt to import msgpack - import msgpack - # There is a serialization issue on ARM and potentially other platforms - # for some msgpack bindings, check for it - if msgpack.loads(msgpack.dumps([1, 2, 3]), use_list=True) is None: - raise ImportError -except ImportError: - # Fall back to msgpack_pure - try: - import msgpack_pure as msgpack - except ImportError: - # TODO: Come up with a sane way to get a configured logfile - # and write to the logfile when this error is hit also - LOG_FORMAT = '[%(levelname)-8s] %(message)s' - salt.log.setup_console_logger(log_format=LOG_FORMAT) - log.fatal('Unable to import msgpack or msgpack_pure python modules') - # Don't exit if msgpack is not available, this is to make local mode - # work without msgpack - #sys.exit(salt.exitcodes.EX_GENERIC) # Define the module's virtual name __virtualname__ = 'fluent' @@ -455,7 +436,7 @@ def _make_packet(self, label, timestamp, data): packet = (tag, timestamp, data) if self.verbose: print(packet) - return msgpack.packb(packet) + return salt.utils.msgpack.packb(packet) def _send(self, bytes_): self.lock.acquire() diff --git a/salt/log/mixins.py b/salt/log/mixins.py index 383964858a91..6e8645d96dd7 100644 --- a/salt/log/mixins.py +++ b/salt/log/mixins.py @@ -10,132 +10,24 @@ Some mix-in classes to be used in salt's logging ''' -from __future__ import absolute_import, print_function, unicode_literals # Import python libs -import sys -import logging - - -class LoggingProfileMixIn(object): - ''' - Simple mix-in class to add a trace method to python's logging. - ''' - - def profile(self, msg, *args, **kwargs): - self.log(getattr(logging, 'PROFILE', 15), msg, *args, **kwargs) - - -class LoggingTraceMixIn(object): - ''' - Simple mix-in class to add a trace method to python's logging. - ''' - - def trace(self, msg, *args, **kwargs): - self.log(getattr(logging, 'TRACE', 5), msg, *args, **kwargs) - - -class LoggingGarbageMixIn(object): - ''' - Simple mix-in class to add a garbage method to python's logging. - ''' - - def garbage(self, msg, *args, **kwargs): - self.log(getattr(logging, 'GARBAGE', 5), msg, *args, **kwargs) - - -class LoggingMixInMeta(type): - ''' - This class is called whenever a new instance of ``SaltLoggingClass`` is - created. - - What this class does is check if any of the bases have a `trace()` or a - `garbage()` method defined, if they don't we add the respective mix-ins to - the bases. - ''' - def __new__(mcs, name, bases, attrs): - include_profile = include_trace = include_garbage = True - bases = list(bases) - if name == 'SaltLoggingClass': - for base in bases: - if hasattr(base, 'trace'): - include_trace = False - if hasattr(base, 'garbage'): - include_garbage = False - if include_profile: - bases.append(LoggingProfileMixIn) - if include_trace: - bases.append(LoggingTraceMixIn) - if include_garbage: - bases.append(LoggingGarbageMixIn) - return super(LoggingMixInMeta, mcs).__new__( - mcs, name, tuple(bases), attrs - ) - - -class NewStyleClassMixIn(object): - ''' - Simple new style class to make pylint shut up! - This is required because SaltLoggingClass can't subclass object directly: - - 'Cannot create a consistent method resolution order (MRO) for bases' - ''' - - -class ExcInfoOnLogLevelFormatMixIn(object): - ''' - Logging handler class mixin to properly handle including exc_info on a per logging handler basis - ''' - - def format(self, record): - ''' - Format the log record to include exc_info if the handler is enabled for a specific log level - ''' - formatted_record = super(ExcInfoOnLogLevelFormatMixIn, self).format(record) - exc_info_on_loglevel = getattr(record, 'exc_info_on_loglevel', None) - exc_info_on_loglevel_formatted = getattr(record, 'exc_info_on_loglevel_formatted', None) - if exc_info_on_loglevel is None and exc_info_on_loglevel_formatted is None: - return formatted_record - - # If we reached this far it means the log record was created with exc_info_on_loglevel - # If this specific handler is enabled for that record, then we should format it to - # include the exc_info details - if self.level > exc_info_on_loglevel: - # This handler is not enabled for the desired exc_info_on_loglevel, don't include exc_info - return formatted_record - - # If we reached this far it means we should include exc_info - if not record.exc_info_on_loglevel_instance and not exc_info_on_loglevel_formatted: - # This should actually never occur - return formatted_record - - if record.exc_info_on_loglevel_formatted is None: - # Let's cache the formatted exception to avoid recurring conversions and formatting calls - if self.formatter is None: # pylint: disable=access-member-before-definition - self.formatter = logging._defaultFormatter - record.exc_info_on_loglevel_formatted = self.formatter.formatException( - record.exc_info_on_loglevel_instance - ) - - # Let's format the record to include exc_info just like python's logging formatted does - if formatted_record[-1:] != '\n': - formatted_record += '\n' +from __future__ import absolute_import, print_function, unicode_literals - try: - formatted_record += record.exc_info_on_loglevel_formatted - except UnicodeError: - # According to the standard library logging formatter comments: - # - # Sometimes filenames have non-ASCII chars, which can lead - # to errors when s is Unicode and record.exc_text is str - # See issue 8924. - # We also use replace for when there are multiple - # encodings, e.g. UTF-8 for the filesystem and latin-1 - # for a script. See issue 13232. - formatted_record += record.exc_info_on_loglevel_formatted.decode(sys.getfilesystemencoding(), - 'replace') - # Reset the record.exc_info_on_loglevel_instance because it might need - # to "travel" through a multiprocessing process and it might contain - # data which is not pickle'able - record.exc_info_on_loglevel_instance = None - return formatted_record +# Import Salt libs +# pylint: disable=unused-import +from salt._logging.mixins import NewStyleClassMixin as NewStyleClassMixIn +from salt._logging.mixins import LoggingProfileMixin as LoggingProfileMixIn +from salt._logging.mixins import LoggingTraceMixin as LoggingTraceMixIn +from salt._logging.mixins import LoggingGarbageMixin as LoggingGarbageMixIn +from salt._logging.mixins import LoggingMixinMeta as LoggingMixInMeta +from salt._logging.mixins import ExcInfoOnLogLevelFormatMixin as ExcInfoOnLogLevelFormatMixIn +# pylint: enable=unused-import +#from salt.utils.versions import warn_until_date +#warn_until_date( +# '20220101', +# 'Please stop using \'{name}\' and instead use \'salt._logging.mixins\'. ' +# '\'{name}\' will go away after {{date}}.'.format( +# name=__name__ +# ) +#) diff --git a/salt/log/setup.py b/salt/log/setup.py index 7405d456c2d6..2601131b924c 100644 --- a/salt/log/setup.py +++ b/salt/log/setup.py @@ -16,7 +16,6 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals import os -import re import sys import time import types @@ -30,84 +29,25 @@ from salt.ext import six from salt.ext.six.moves.urllib.parse import urlparse # pylint: disable=import-error,no-name-in-module -# Let's define these custom logging levels before importing the salt.log.mixins -# since they will be used there -PROFILE = logging.PROFILE = 15 -TRACE = logging.TRACE = 5 -GARBAGE = logging.GARBAGE = 1 -QUIET = logging.QUIET = 1000 - # Import salt libs -from salt.textformat import TextFormat -from salt.log.handlers import (TemporaryLoggingHandler, - StreamHandler, - SysLogHandler, - FileHandler, - WatchedFileHandler, - RotatingFileHandler, - QueueHandler) -from salt.log.mixins import LoggingMixInMeta, NewStyleClassMixIn - -from salt.utils.ctx import RequestContext - -LOG_LEVELS = { - 'all': logging.NOTSET, - 'debug': logging.DEBUG, - 'error': logging.ERROR, - 'critical': logging.CRITICAL, - 'garbage': GARBAGE, - 'info': logging.INFO, - 'profile': PROFILE, - 'quiet': QUIET, - 'trace': TRACE, - 'warning': logging.WARNING, -} - -LOG_VALUES_TO_LEVELS = dict((v, k) for (k, v) in LOG_LEVELS.items()) - -LOG_COLORS = { - 'levels': { - 'QUIET': TextFormat('reset'), - 'CRITICAL': TextFormat('bold', 'red'), - 'ERROR': TextFormat('bold', 'red'), - 'WARNING': TextFormat('bold', 'yellow'), - 'INFO': TextFormat('bold', 'green'), - 'PROFILE': TextFormat('bold', 'cyan'), - 'DEBUG': TextFormat('bold', 'cyan'), - 'TRACE': TextFormat('bold', 'magenta'), - 'GARBAGE': TextFormat('bold', 'blue'), - 'NOTSET': TextFormat('reset'), - 'SUBDEBUG': TextFormat('bold', 'cyan'), # used by multiprocessing.log_to_stderr() - 'SUBWARNING': TextFormat('bold', 'yellow'), # used by multiprocessing.log_to_stderr() - }, - 'msgs': { - 'QUIET': TextFormat('reset'), - 'CRITICAL': TextFormat('bold', 'red'), - 'ERROR': TextFormat('red'), - 'WARNING': TextFormat('yellow'), - 'INFO': TextFormat('green'), - 'PROFILE': TextFormat('bold', 'cyan'), - 'DEBUG': TextFormat('cyan'), - 'TRACE': TextFormat('magenta'), - 'GARBAGE': TextFormat('blue'), - 'NOTSET': TextFormat('reset'), - 'SUBDEBUG': TextFormat('bold', 'cyan'), # used by multiprocessing.log_to_stderr() - 'SUBWARNING': TextFormat('bold', 'yellow'), # used by multiprocessing.log_to_stderr() - }, - 'name': TextFormat('bold', 'green'), - 'process': TextFormat('bold', 'blue'), -} - - -# Make a list of log level names sorted by log level -SORTED_LEVEL_NAMES = [ - l[0] for l in sorted(six.iteritems(LOG_LEVELS), key=lambda x: x[1]) -] - -# Store an instance of the current logging logger class -LOGGING_LOGGER_CLASS = logging.getLoggerClass() - -MODNAME_PATTERN = re.compile(r'(?P%%\(name\)(?:\-(?P[\d]+))?s)') +# pylint: disable=unused-import +from salt._logging import (LOG_COLORS, + LOG_LEVELS, + LOG_VALUES_TO_LEVELS, + SORTED_LEVEL_NAMES) +from salt._logging.impl import (SaltLogRecord, + SaltColorLogRecord, + LOGGING_NULL_HANDLER, + LOGGING_STORE_HANDLER, + LOGGING_TEMP_HANDLER) +from salt._logging.impl import set_log_record_factory as setLogRecordFactory +from salt._logging.handlers import (StreamHandler, + SysLogHandler, + FileHandler, + WatchedFileHandler, + RotatingFileHandler, + QueueHandler) +# pylint: enable=unused-import __CONSOLE_CONFIGURED = False __LOGGING_CONSOLE_HANDLER = None @@ -118,27 +58,13 @@ __MP_LOGGING_LISTENER_CONFIGURED = False __MP_LOGGING_CONFIGURED = False __MP_LOGGING_QUEUE = None -__MP_LOGGING_LEVEL = GARBAGE +__MP_LOGGING_LEVEL = logging.GARBAGE __MP_LOGGING_QUEUE_PROCESS = None __MP_LOGGING_QUEUE_HANDLER = None __MP_IN_MAINPROCESS = multiprocessing.current_process().name == 'MainProcess' __MP_MAINPROCESS_ID = None -class __NullLoggingHandler(TemporaryLoggingHandler): - ''' - This class exists just to better identify which temporary logging - handler is being used for what. - ''' - - -class __StoreLoggingHandler(TemporaryLoggingHandler): - ''' - This class exists just to better identify which temporary logging - handler is being used for what. - ''' - - def is_console_configured(): return __CONSOLE_CONFIGURED @@ -167,287 +93,12 @@ def is_extended_logging_configured(): return __EXTERNAL_LOGGERS_CONFIGURED -# Store a reference to the temporary queue logging handler -LOGGING_NULL_HANDLER = __NullLoggingHandler(logging.WARNING) - -# Store a reference to the temporary console logger -LOGGING_TEMP_HANDLER = StreamHandler(sys.stderr) - -# Store a reference to the "storing" logging handler -LOGGING_STORE_HANDLER = __StoreLoggingHandler() - - class SaltLogQueueHandler(QueueHandler): ''' Subclassed just to differentiate when debugging ''' -class SaltLogRecord(logging.LogRecord): - def __init__(self, *args, **kwargs): - logging.LogRecord.__init__(self, *args, **kwargs) - # pylint: disable=E1321 - self.bracketname = '[%-17s]' % self.name - self.bracketlevel = '[%-8s]' % self.levelname - self.bracketprocess = '[%5s]' % self.process - # pylint: enable=E1321 - - -class SaltColorLogRecord(SaltLogRecord): - def __init__(self, *args, **kwargs): - SaltLogRecord.__init__(self, *args, **kwargs) - - reset = TextFormat('reset') - clevel = LOG_COLORS['levels'].get(self.levelname, reset) - cmsg = LOG_COLORS['msgs'].get(self.levelname, reset) - - # pylint: disable=E1321 - self.colorname = '%s[%-17s]%s' % (LOG_COLORS['name'], - self.name, - reset) - self.colorlevel = '%s[%-8s]%s' % (clevel, - self.levelname, - reset) - self.colorprocess = '%s[%5s]%s' % (LOG_COLORS['process'], - self.process, - reset) - self.colormsg = '%s%s%s' % (cmsg, self.getMessage(), reset) - # pylint: enable=E1321 - - -_LOG_RECORD_FACTORY = SaltLogRecord - - -def setLogRecordFactory(factory): - ''' - Set the factory to be used when instantiating a log record. - - :param factory: A callable which will be called to instantiate - a log record. - ''' - global _LOG_RECORD_FACTORY - _LOG_RECORD_FACTORY = factory - - -def getLogRecordFactory(): - ''' - Return the factory to be used when instantiating a log record. - ''' - - return _LOG_RECORD_FACTORY - - -setLogRecordFactory(SaltLogRecord) - - -class SaltLoggingClass(six.with_metaclass(LoggingMixInMeta, LOGGING_LOGGER_CLASS, NewStyleClassMixIn)): - def __new__(cls, *args): # pylint: disable=W0613,E0012 - ''' - We override `__new__` in our logging logger class in order to provide - some additional features like expand the module name padding if length - is being used, and also some Unicode fixes. - - This code overhead will only be executed when the class is - instantiated, i.e.: - - logging.getLogger(__name__) - - ''' - instance = super(SaltLoggingClass, cls).__new__(cls) - - try: - max_logger_length = len(max( - list(logging.Logger.manager.loggerDict), key=len - )) - for handler in logging.root.handlers: - if handler in (LOGGING_NULL_HANDLER, - LOGGING_STORE_HANDLER, - LOGGING_TEMP_HANDLER): - continue - - formatter = handler.formatter - if not formatter: - continue - - if not handler.lock: - handler.createLock() - handler.acquire() - - fmt = formatter._fmt.replace('%', '%%') - - match = MODNAME_PATTERN.search(fmt) - if not match: - # Not matched. Release handler and return. - handler.release() - return instance - - if 'digits' not in match.groupdict(): - # No digits group. Release handler and return. - handler.release() - return instance - - digits = match.group('digits') - if not digits or not (digits and digits.isdigit()): - # No valid digits. Release handler and return. - handler.release() - return instance - - if int(digits) < max_logger_length: - # Formatter digits value is lower than current max, update. - fmt = fmt.replace(match.group('name'), '%%(name)-%ds') - formatter = logging.Formatter( - fmt % max_logger_length, - datefmt=formatter.datefmt - ) - handler.setFormatter(formatter) - handler.release() - except ValueError: - # There are no registered loggers yet - pass - return instance - - def _log(self, level, msg, args, exc_info=None, extra=None, # pylint: disable=arguments-differ - exc_info_on_loglevel=None): - # If both exc_info and exc_info_on_loglevel are both passed, let's fail - if extra is None: - extra = {} - - current_jid = RequestContext.current.get('data', {}).get('jid', None) - log_fmt_jid = RequestContext.current.get('opts', {}).get('log_fmt_jid', None) - - if current_jid is not None: - extra['jid'] = current_jid - - if log_fmt_jid is not None: - extra['log_fmt_jid'] = log_fmt_jid - - if exc_info and exc_info_on_loglevel: - raise RuntimeError( - 'Only one of \'exc_info\' and \'exc_info_on_loglevel\' is ' - 'permitted' - ) - if exc_info_on_loglevel is not None: - if isinstance(exc_info_on_loglevel, six.string_types): - exc_info_on_loglevel = LOG_LEVELS.get(exc_info_on_loglevel, - logging.ERROR) - elif not isinstance(exc_info_on_loglevel, int): - raise RuntimeError( - 'The value of \'exc_info_on_loglevel\' needs to be a ' - 'logging level or a logging level name, not \'{0}\'' - .format(exc_info_on_loglevel) - ) - if extra is None: - extra = {'exc_info_on_loglevel': exc_info_on_loglevel} - else: - extra['exc_info_on_loglevel'] = exc_info_on_loglevel - - LOGGING_LOGGER_CLASS._log( - self, level, msg, args, exc_info=exc_info, extra=extra - ) - - # pylint: disable=C0103 - # pylint: disable=W0221 - def makeRecord(self, name, level, fn, lno, msg, args, exc_info, - func=None, extra=None, sinfo=None): - # Let's remove exc_info_on_loglevel from extra - exc_info_on_loglevel = extra.pop('exc_info_on_loglevel') - - jid = extra.pop('jid', '') - if jid: - log_fmt_jid = extra.pop('log_fmt_jid') - jid = log_fmt_jid % {'jid': jid} - - if not extra: - # If nothing else is in extra, make it None - extra = None - - # Let's try to make every logging message unicode - try: - salt_system_encoding = __salt_system_encoding__ - if salt_system_encoding == 'ascii': - # Encoding detection most likely failed, let's use the utf-8 - # value which we defaulted before __salt_system_encoding__ was - # implemented - salt_system_encoding = 'utf-8' - except NameError: - salt_system_encoding = 'utf-8' - - if isinstance(msg, six.string_types) \ - and not isinstance(msg, six.text_type): - try: - _msg = msg.decode(salt_system_encoding, 'replace') - except UnicodeDecodeError: - _msg = msg.decode(salt_system_encoding, 'ignore') - else: - _msg = msg - - _args = [] - for item in args: - if isinstance(item, six.string_types) \ - and not isinstance(item, six.text_type): - try: - _args.append(item.decode(salt_system_encoding, 'replace')) - except UnicodeDecodeError: - _args.append(item.decode(salt_system_encoding, 'ignore')) - else: - _args.append(item) - _args = tuple(_args) - - if six.PY3: - logrecord = _LOG_RECORD_FACTORY(name, level, fn, lno, _msg, _args, - exc_info, func, sinfo) - else: - logrecord = _LOG_RECORD_FACTORY(name, level, fn, lno, _msg, _args, - exc_info, func) - - if extra is not None: - for key in extra: - if (key in ['message', 'asctime']) or (key in logrecord.__dict__): - raise KeyError( - 'Attempt to overwrite \'{0}\' in LogRecord'.format(key) - ) - logrecord.__dict__[key] = extra[key] - - if exc_info_on_loglevel is not None: - # Let's add some custom attributes to the LogRecord class in order - # to include the exc_info on a per handler basis. This will allow - # showing tracebacks on logfiles but not on console if the logfile - # handler is enabled for the log level "exc_info_on_loglevel" and - # console handler is not. - logrecord.exc_info_on_loglevel_instance = sys.exc_info() - logrecord.exc_info_on_loglevel_formatted = None - - logrecord.exc_info_on_loglevel = exc_info_on_loglevel - logrecord.jid = jid - return logrecord - - # pylint: enable=C0103 - - -# Override the python's logging logger class as soon as this module is imported -if logging.getLoggerClass() is not SaltLoggingClass: - - logging.setLoggerClass(SaltLoggingClass) - logging.addLevelName(QUIET, 'QUIET') - logging.addLevelName(PROFILE, 'PROFILE') - logging.addLevelName(TRACE, 'TRACE') - logging.addLevelName(GARBAGE, 'GARBAGE') - - if not logging.root.handlers: - # No configuration to the logging system has been done so far. - # Set the root logger at the lowest level possible - logging.root.setLevel(GARBAGE) - - # Add a Null logging handler until logging is configured(will be - # removed at a later stage) so we stop getting: - # No handlers could be found for logger 'foo' - logging.root.addHandler(LOGGING_NULL_HANDLER) - - # Add the queue logging handler so we can later sync all message records - # with the additional logging handlers - logging.root.addHandler(LOGGING_STORE_HANDLER) - - def getLogger(name): # pylint: disable=C0103 ''' This function is just a helper, an alias to: diff --git a/salt/master.py b/salt/master.py index abd913770858..7c91fb4fa4f9 100644 --- a/salt/master.py +++ b/salt/master.py @@ -138,7 +138,7 @@ def __prep_key(self): return salt.daemons.masterapi.access_keys(self.opts) -class Maintenance(salt.utils.process.SignalHandlingMultiprocessingProcess): +class Maintenance(salt.utils.process.SignalHandlingProcess): ''' A generalized maintenance process which performs maintenance routines. ''' @@ -161,7 +161,6 @@ def __init__(self, opts, **kwargs): # We do this so that __init__ will be invoked on Windows in the child # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): - self._is_child = True self.__init__( state['opts'], log_queue=state['log_queue'], @@ -353,7 +352,7 @@ def handle_presence(self, old_present): old_present.update(present) -class FileserverUpdate(salt.utils.process.SignalHandlingMultiprocessingProcess): +class FileserverUpdate(salt.utils.process.SignalHandlingProcess): ''' A process from which to update any dynamic fileserver backends ''' @@ -370,7 +369,6 @@ def __init__(self, opts, **kwargs): # We do this so that __init__ will be invoked on Windows in the child # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): - self._is_child = True self.__init__( state['opts'], log_queue=state['log_queue'], @@ -782,7 +780,7 @@ def _handle_signals(self, signum, sigframe): # pylint: disable=unused-argument sys.exit(0) -class Halite(salt.utils.process.SignalHandlingMultiprocessingProcess): +class Halite(salt.utils.process.SignalHandlingProcess): ''' Manage the Halite server ''' @@ -799,7 +797,6 @@ def __init__(self, hopts, **kwargs): # We do this so that __init__ will be invoked on Windows in the child # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): - self._is_child = True self.__init__( state['hopts'], log_queue=state['log_queue'], @@ -821,7 +818,7 @@ def run(self): halite.start(self.hopts) -class ReqServer(salt.utils.process.SignalHandlingMultiprocessingProcess): +class ReqServer(salt.utils.process.SignalHandlingProcess): ''' Starts up the master request server, minions send results to this interface. @@ -848,7 +845,6 @@ def __init__(self, opts, key, mkey, secrets=None, **kwargs): # We do this so that __init__ will be invoked on Windows in the child # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): - self._is_child = True self.__init__( state['opts'], state['key'], @@ -947,11 +943,13 @@ def destroy(self, signum=signal.SIGTERM): self.process_manager.send_signal_to_processes(signum) self.process_manager.kill_children() + # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 -class MWorker(salt.utils.process.SignalHandlingMultiprocessingProcess): +class MWorker(salt.utils.process.SignalHandlingProcess): ''' The worker multiprocess instance to manage the backend operations for the salt master. @@ -991,7 +989,6 @@ def __init__(self, # These methods are only used when pickling so will not be used on # non-Windows platforms. def __setstate__(self, state): - self._is_child = True super(MWorker, self).__init__( log_queue=state['log_queue'], log_queue_level=state['log_queue_level'] diff --git a/salt/metaproxy/proxy.py b/salt/metaproxy/proxy.py index d52809a91928..5e2cf3a35ca8 100644 --- a/salt/metaproxy/proxy.py +++ b/salt/metaproxy/proxy.py @@ -59,7 +59,7 @@ from salt.defaults import DEFAULT_TARGET_DELIM from salt.utils.process import (default_signals, - SignalHandlingMultiprocessingProcess) + SignalHandlingProcess) import tornado.gen # pylint: disable=F0401 @@ -725,8 +725,10 @@ def handle_decoded_payload(self, data): # running on windows instance = None with default_signals(signal.SIGINT, signal.SIGTERM): - process = SignalHandlingMultiprocessingProcess( - target=self._target, args=(instance, self.opts, data, self.connected) + process = SignalHandlingProcess( + target=self._target, + name='ProcessPayload', + args=(instance, self.opts, data, self.connected) ) else: process = threading.Thread( diff --git a/salt/minion.py b/salt/minion.py index 28b4956a49c4..52e52342ab0d 100644 --- a/salt/minion.py +++ b/salt/minion.py @@ -50,12 +50,6 @@ except ImportError: pass -try: - import zmq.utils.monitor - HAS_ZMQ_MONITOR = True -except ImportError: - HAS_ZMQ_MONITOR = False - try: import salt.utils.win_functions HAS_WIN_FUNCTIONS = True @@ -100,7 +94,7 @@ from salt.utils.event import tagify from salt.utils.odict import OrderedDict from salt.utils.process import (default_signals, - SignalHandlingMultiprocessingProcess, + SignalHandlingProcess, ProcessManager) from salt.exceptions import ( CommandExecutionError, @@ -423,7 +417,7 @@ def __init__(self, opts): self.opts = opts self.beacons_leader = opts.get('beacons_leader', True) - def gen_modules(self, initial_load=False): + def gen_modules(self, initial_load=False, context=None): ''' Tell the minion to reload the execution modules @@ -433,30 +427,35 @@ def gen_modules(self, initial_load=False): salt '*' sys.reload_modules ''' - self.opts['pillar'] = salt.pillar.get_pillar( - self.opts, - self.opts['grains'], - self.opts['id'], - self.opts['saltenv'], - pillarenv=self.opts.get('pillarenv'), - ).compile_pillar() + if initial_load: + self.opts['pillar'] = salt.pillar.get_pillar( + self.opts, + self.opts['grains'], + self.opts['id'], + self.opts['saltenv'], + pillarenv=self.opts.get('pillarenv'), + ).compile_pillar() - self.utils = salt.loader.utils(self.opts) - self.functions = salt.loader.minion_mods(self.opts, utils=self.utils) + self.utils = salt.loader.utils(self.opts, context=context) + self.functions = salt.loader.minion_mods(self.opts, utils=self.utils, context=context) self.serializers = salt.loader.serializers(self.opts) - self.returners = salt.loader.returners(self.opts, self.functions) - self.proxy = salt.loader.proxy(self.opts, self.functions, self.returners, None) + self.returners = salt.loader.returners(self.opts, functions=self.functions, context=context) + self.proxy = salt.loader.proxy(self.opts, functions=self.functions, returners=self.returners) # TODO: remove self.function_errors = {} # Keep the funcs clean self.states = salt.loader.states(self.opts, - self.functions, - self.utils, - self.serializers) - self.rend = salt.loader.render(self.opts, self.functions) + functions=self.functions, + utils=self.utils, + serializers=self.serializers, + context=context) + self.rend = salt.loader.render(self.opts, functions=self.functions, context=context) # self.matcher = Matcher(self.opts, self.functions) self.matchers = salt.loader.matchers(self.opts) self.functions['sys.reload_modules'] = self.gen_modules - self.executors = salt.loader.executors(self.opts, self.functions) + self.executors = salt.loader.executors(self.opts, + functions=self.functions, + proxy=self.proxy, + context=context) @staticmethod def process_schedule(minion, loop_interval): @@ -645,6 +644,7 @@ def eval_master(self, opts.update(prep_ip_port(opts)) opts['master_uri_list'].append(resolve_dns(opts)['master_uri']) + pub_channel = None while True: if attempts != 0: # Give up a little time between connection attempts @@ -690,6 +690,8 @@ def eval_master(self, msg = ('Master %s could not be reached, trying next ' 'next master (if any)', opts['master']) log.info(msg) + pub_channel.close() + pub_channel = None continue if not conn: @@ -701,6 +703,8 @@ def eval_master(self, 'No master could be reached or all masters ' 'denied the minion\'s connection attempt.' ) + if pub_channel: + pub_channel.close() # If the code reaches this point, 'last_exc' # should already be set. raise last_exc # pylint: disable=E0702 @@ -713,6 +717,7 @@ def eval_master(self, else: if opts['random_master']: log.warning('random_master is True but there is only one master specified. Ignoring.') + pub_channel = None while True: if attempts != 0: # Give up a little time between connection attempts @@ -750,11 +755,13 @@ def eval_master(self, self.tok = pub_channel.auth.gen_token(b'salt') self.connected = True raise tornado.gen.Return((opts['master'], pub_channel)) - except SaltClientError as exc: + except SaltClientError: if attempts == tries: # Exhausted all attempts. Return exception. self.connected = False - raise exc + if pub_channel: + pub_channel.close() + six.reraise(*sys.exc_info()) def _discover_masters(self): ''' @@ -828,7 +835,7 @@ class SMinion(MinionBase): generate all of the salt minion functions and present them with these functions for general use. ''' - def __init__(self, opts): + def __init__(self, opts, context=None): # Late setup of the opts grains, so we can log from the grains module import salt.loader opts['grains'] = salt.loader.grains(opts) @@ -842,7 +849,7 @@ def __init__(self, opts): io_loop.run_sync( lambda: self.eval_master(self.opts, failed=True) ) - self.gen_modules(initial_load=True) + self.gen_modules(initial_load=True, context=context or {}) # If configured, cache pillar data on the minion if self.opts['file_client'] == 'remote' and self.opts.get('minion_pillar_cache', False): @@ -944,8 +951,10 @@ def __init__(self, opts): self.process_manager = ProcessManager(name='MultiMinionProcessManager') self.io_loop.spawn_callback(self.process_manager.run, **{'asynchronous': True}) # Tornado backward compat + # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 def _bind(self): # start up the event publisher, so we can see events during startup @@ -1405,11 +1414,8 @@ def _send_req_sync(self, load, timeout): sig = salt.crypt.sign_message(minion_privkey_path, salt.serializers.msgpack.serialize(load)) load['sig'] = sig - channel = salt.transport.client.ReqChannel.factory(self.opts) - try: + with salt.transport.client.ReqChannel.factory(self.opts) as channel: return channel.send(load, timeout=timeout) - finally: - channel.close() @tornado.gen.coroutine def _send_req_async(self, load, timeout): @@ -1420,12 +1426,9 @@ def _send_req_async(self, load, timeout): sig = salt.crypt.sign_message(minion_privkey_path, salt.serializers.msgpack.serialize(load)) load['sig'] = sig - channel = salt.transport.client.AsyncReqChannel.factory(self.opts) - try: + with salt.transport.client.AsyncReqChannel.factory(self.opts) as channel: ret = yield channel.send(load, timeout=timeout) raise tornado.gen.Return(ret) - finally: - channel.close() def _fire_master(self, data=None, tag=None, events=None, pretag=None, timeout=60, sync=True, timeout_handler=None): ''' @@ -1523,8 +1526,10 @@ def _handle_decoded_payload(self, data): # running on windows instance = None with default_signals(signal.SIGINT, signal.SIGTERM): - process = SignalHandlingMultiprocessingProcess( - target=self._target, args=(instance, self.opts, data, self.connected) + process = SignalHandlingProcess( + target=self._target, + name='ProcessPayload', + args=(instance, self.opts, data, self.connected) ) else: process = threading.Thread( @@ -1633,8 +1638,8 @@ def _thread_return(cls, minion_instance, opts, data): minion_blackout_violation = True if minion_blackout_violation: raise SaltInvocationError('Minion in blackout mode. Set \'minion_blackout\' ' - 'to False in pillar or grains to resume operations. Only ' - 'saltutil.refresh_pillar allowed in blackout mode.') + 'to False in pillar or grains to resume operations. Only ' + 'saltutil.refresh_pillar allowed in blackout mode.') if function_name in minion_instance.functions: func = minion_instance.functions[function_name] @@ -2185,6 +2190,8 @@ def pillar_refresh(self, force_refresh=False): ''' Refresh the pillar ''' + self.module_refresh(force_refresh) + if self.connected: log.debug('Refreshing pillar') async_pillar = salt.pillar.get_async_pillar( @@ -2202,9 +2209,10 @@ def pillar_refresh(self, force_refresh=False): 'One or more masters may be down!') finally: async_pillar.destroy() - self.module_refresh(force_refresh) self.matchers_refresh() self.beacons_refresh() + evt = salt.utils.event.get_event('minion', opts=self.opts) + evt.fire_event({'complete': True}, tag='/salt/minion/minion_pillar_refresh_complete') def manage_schedule(self, tag, data): ''' @@ -2333,16 +2341,14 @@ def _mine_send(self, tag, data): ''' Send mine data to the master ''' - channel = salt.transport.client.ReqChannel.factory(self.opts) - data['tok'] = self.tok - try: - ret = channel.send(data) - return ret - except SaltReqTimeoutError: - log.warning('Unable to send mine data to master.') - return None - finally: - channel.close() + with salt.transport.client.ReqChannel.factory(self.opts) as channel: + data['tok'] = self.tok + try: + ret = channel.send(data) + return ret + except SaltReqTimeoutError: + log.warning('Unable to send mine data to master.') + return None @tornado.gen.coroutine def handle_event(self, package): @@ -2809,8 +2815,10 @@ def destroy(self): for cb in six.itervalues(self.periodic_callbacks): cb.stop() + # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 class Syndic(Minion): @@ -3383,7 +3391,7 @@ class SProxyMinion(SMinion): generate all of the salt minion functions and present them with these functions for general use. ''' - def gen_modules(self, initial_load=False): + def gen_modules(self, initial_load=False, context=None): ''' Tell the minion to reload the execution modules @@ -3418,13 +3426,23 @@ def gen_modules(self, initial_load=False): # Then load the proxy module self.proxy = salt.loader.proxy(self.opts) - self.utils = salt.loader.utils(self.opts, proxy=self.proxy) - - self.functions = salt.loader.minion_mods(self.opts, utils=self.utils, notify=False, proxy=self.proxy) - self.returners = salt.loader.returners(self.opts, self.functions, proxy=self.proxy) + self.utils = salt.loader.utils(self.opts, proxy=self.proxy, context=context) + + self.functions = salt.loader.minion_mods(self.opts, + utils=self.utils, + notify=False, + proxy=self.proxy, + context=context) + self.returners = salt.loader.returners(self.opts, + functions=self.functions, + proxy=self.proxy, + context=context) self.matchers = salt.loader.matchers(self.opts) self.functions['sys.reload_modules'] = self.gen_modules - self.executors = salt.loader.executors(self.opts, self.functions, proxy=self.proxy) + self.executors = salt.loader.executors(self.opts, + functions=self.functions, + proxy=self.proxy, + context=context) fq_proxyname = self.opts['proxy']['proxytype'] @@ -3439,7 +3457,7 @@ def gen_modules(self, initial_load=False): self.proxy.pack['__pillar__'] = self.opts['pillar'] # Reload utils as well (chicken and egg, __utils__ needs __proxy__ and __proxy__ needs __utils__ - self.utils = salt.loader.utils(self.opts, proxy=self.proxy) + self.utils = salt.loader.utils(self.opts, proxy=self.proxy, context=context) self.proxy.pack['__utils__'] = self.utils # Reload all modules so all dunder variables are injected diff --git a/salt/modules/acme.py b/salt/modules/acme.py index 74d48f8c2ca6..76552281f555 100644 --- a/salt/modules/acme.py +++ b/salt/modules/acme.py @@ -42,6 +42,7 @@ # Import salt libs import salt.utils.path +from salt.exceptions import SaltInvocationError log = logging.getLogger(__name__) @@ -69,12 +70,13 @@ def _expires(name): ''' Return the expiry date of a cert - :return datetime object of expiry date + :rtype: datetime + :return: Expiry date ''' cert_file = _cert_file(name, 'cert') # Use the salt module if available if 'tls.cert_info' in __salt__: - expiry = __salt__['tls.cert_info'](cert_file)['not_after'] + expiry = __salt__['tls.cert_info'](cert_file).get('not_after', 0) # Cobble it together using the openssl binary else: openssl_cmd = 'openssl x509 -in {0} -noout -enddate'.format(cert_file) @@ -82,7 +84,6 @@ def _expires(name): strptime_sux_cmd = 'date --date="$({0} | cut -d= -f2)" +%s'.format(openssl_cmd) expiry = float(__salt__['cmd.shell'](strptime_sux_cmd, output_loglevel='quiet')) # expiry = datetime.datetime.strptime(expiry.split('=', 1)[-1], '%b %e %H:%M:%S %Y %Z') - return datetime.datetime.fromtimestamp(expiry) @@ -90,9 +91,10 @@ def _renew_by(name, window=None): ''' Date before a certificate should be renewed - :param name: Common Name of the certificate (DNS name of certificate) - :param window: days before expiry date to renew - :return datetime object of first renewal date + :param str name: Common Name of the certificate (DNS name of certificate) + :param int window: days before expiry date to renew + :rtype: datetime + :return: First renewal date ''' expiry = _expires(name) if window is not None: @@ -127,8 +129,10 @@ def cert(name, :param aliases: subjectAltNames (Additional DNS names on certificate) :param email: e-mail address for interaction with ACME provider :param webroot: True or a full path to use to use webroot. Otherwise use standalone mode - :param test_cert: Request a certificate from the Happy Hacker Fake CA (mutually exclusive with 'server') - :param renew: True/'force' to force a renewal, or a window of renewal before expiry in days + :param test_cert: Request a certificate from the Happy Hacker Fake CA (mutually + exclusive with 'server') + :param renew: True/'force' to force a renewal, or a window of renewal before + expiry in days :param keysize: RSA key bits :param server: API endpoint to talk to :param owner: owner of the private key file @@ -136,26 +140,33 @@ def cert(name, :param mode: mode of the private key file :param certname: Name of the certificate to save :param preferred_challenges: A sorted, comma delimited list of the preferred - challenge to use during authorization with the - most preferred challenge listed first. + challenge to use during authorization with the most preferred challenge + listed first. :param tls_sni_01_port: Port used during tls-sni-01 challenge. This only affects - the port Certbot listens on. A conforming ACME server - will still attempt to connect on port 443. + the port Certbot listens on. A conforming ACME server will still attempt + to connect on port 443. :param tls_sni_01_address: The address the server listens to during tls-sni-01 - challenge. + challenge. :param http_01_port: Port used in the http-01 challenge. This only affects - the port Certbot listens on. A conforming ACME server - will still attempt to connect on port 80. + the port Certbot listens on. A conforming ACME server will still attempt + to connect on port 80. :param https_01_address: The address the server listens to during http-01 challenge. - :param dns_plugin: Name of a DNS plugin to use (currently only 'cloudflare') - :param dns_plugin_credentials: Path to the credentials file if required by the specified DNS plugin - :return: dict with 'result' True/False/None, 'comment' and certificate's expiry date ('not_after') + :param dns_plugin: Name of a DNS plugin to use (currently only 'cloudflare' + or 'digitalocean') + :param dns_plugin_credentials: Path to the credentials file if required by + the specified DNS plugin + :param dns_plugin_propagate_seconds: Number of seconds to wait for DNS propogations + before asking ACME servers to verify the DNS record. (default 10) + :rtype: dict + :return: Dictionary with 'result' True/False/None, 'comment' and certificate's + expiry date ('not_after') CLI example: .. code-block:: bash - salt 'gitlab.example.com' acme.cert dev.example.com "[gitlab.example.com]" test_cert=True renew=14 webroot=/opt/gitlab/embedded/service/gitlab-rails/public + salt 'gitlab.example.com' acme.cert dev.example.com "[gitlab.example.com]" test_cert=True \ + renew=14 webroot=/opt/gitlab/embedded/service/gitlab-rails/public ''' cmd = [LEA, 'certonly', '--non-interactive', '--agree-tos'] @@ -224,9 +235,13 @@ def cert(name, cmd.append('--expand') res = __salt__['cmd.run_all'](' '.join(cmd)) if res['retcode'] != 0: - return {'result': False, 'comment': 'Certificate {0} renewal failed with:\n{1}'.format(name, res['stderr'])} + return {'result': False, + 'comment': ('Certificate {0} renewal failed with:\n{1}' + ''.format(name, res['stderr']))} else: - return {'result': False, 'comment': 'Certificate {0} renewal failed with:\n{1}'.format(name, res['stderr'])} + return {'result': False, + 'comment': ('Certificate {0} renewal failed with:\n{1}' + ''.format(name, res['stderr']))} if 'no action taken' in res['stdout']: comment = 'Certificate {0} unchanged'.format(cert_file) @@ -257,17 +272,19 @@ def certs(): salt 'vhost.example.com' acme.certs ''' - return __salt__['file.readdir'](LE_LIVE)[2:] + return [item for item in __salt__['file.readdir'](LE_LIVE)[2:] if os.path.isdir(item)] def info(name): ''' Return information about a certificate - .. note:: - Will output tls.cert_info if that's available, or OpenSSL text if not - - :param name: CommonName of cert + :param str name: CommonName of certificate + :rtype: dict + :return: Dictionary with information about the certificate. + If neither the ``tls`` nor the ``x509`` module can be used to determine + the certificate information, the information will be retrieved as one + big text block under the key ``text`` using the openssl cli. CLI example: @@ -275,25 +292,32 @@ def info(name): salt 'gitlab.example.com' acme.info dev.example.com ''' + if not has(name): + return {} cert_file = _cert_file(name, 'cert') - # Use the salt module if available + # Use the tls salt module if available if 'tls.cert_info' in __salt__: cert_info = __salt__['tls.cert_info'](cert_file) # Strip out the extensions object contents; # these trip over our poor state output # and they serve no real purpose here anyway cert_info['extensions'] = cert_info['extensions'].keys() - return cert_info - # Cobble it together using the openssl binary - openssl_cmd = 'openssl x509 -in {0} -noout -text'.format(cert_file) - return __salt__['cmd.run'](openssl_cmd, output_loglevel='quiet') + elif 'x509.read_certificate' in __salt__: + cert_info = __salt__['x509.read_certificate'](cert_file) + else: + # Cobble it together using the openssl binary + openssl_cmd = 'openssl x509 -in {0} -noout -text'.format(cert_file) + cert_info = {'text': __salt__['cmd.run'](openssl_cmd, output_loglevel='quiet')} + return cert_info def expires(name): ''' The expiry date of a certificate in ISO format - :param name: CommonName of cert + :param str name: CommonName of certificate + :rtype: str + :return: Expiry date in ISO format. CLI example: @@ -308,7 +332,8 @@ def has(name): ''' Test if a certificate is in the Let's Encrypt Live directory - :param name: CommonName of cert + :param str name: CommonName of certificate + :rtype: bool Code example: @@ -324,8 +349,10 @@ def renew_by(name, window=None): ''' Date in ISO format when a certificate should first be renewed - :param name: CommonName of cert - :param window: number of days before expiry when renewal should take place + :param str name: CommonName of certificate + :param int window: number of days before expiry when renewal should take place + :rtype: str + :return: Date of certificate renewal in ISO format. ''' return _renew_by(name, window).isoformat() @@ -334,8 +361,10 @@ def needs_renewal(name, window=None): ''' Check if a certificate needs renewal - :param name: CommonName of cert - :param window: Window in days to renew earlier or True/force to just return True + :param str name: CommonName of certificate + :param bool/str/int window: Window in days to renew earlier or True/force to just return True + :rtype: bool + :return: Whether or not the certificate needs to be renewed. Code example: @@ -346,7 +375,14 @@ def needs_renewal(name, window=None): else: log.info('Your certificate is still good') ''' - if window is not None and window in ('force', 'Force', True): - return True + if window: + if str(window).lower in ('force', 'true'): + return True + if not (isinstance(window, int) or (hasattr(window, 'isdigit') and window.isdigit())): + raise SaltInvocationError( + 'The argument "window", if provided, must be one of the following : ' + 'True (boolean), "force" or "Force" (str) or a numerical value in days.' + ) + window = int(window) return _renew_by(name, window) <= datetime.datetime.today() diff --git a/salt/modules/aptpkg.py b/salt/modules/aptpkg.py index ead7c6391ec1..029d02f34e1e 100644 --- a/salt/modules/aptpkg.py +++ b/salt/modules/aptpkg.py @@ -1016,8 +1016,9 @@ def upgrade(refresh=True, dist_upgrade=False, **kwargs): Skip refreshing the package database if refresh has already occurred within seconds - download_only - Only download the packages, don't unpack or install them + download_only (or downloadonly) + Only download the packages, don't unpack or install them. Use + downloadonly to be in line with yum and zypper module. .. versionadded:: 2018.3.0 @@ -1048,7 +1049,7 @@ def upgrade(refresh=True, dist_upgrade=False, **kwargs): cmd.append('--force-yes') if kwargs.get('skip_verify', False): cmd.append('--allow-unauthenticated') - if kwargs.get('download_only', False): + if kwargs.get('download_only', False) or kwargs.get('downloadonly', False): cmd.append('--download-only') cmd.append('dist-upgrade' if dist_upgrade else 'upgrade') @@ -1156,7 +1157,7 @@ def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W06 salt '*' pkg.unhold pkgs - A list of packages to hold. Must be passed as a python list. + A list of packages to unhold. Must be passed as a python list. CLI Example: @@ -2791,6 +2792,8 @@ def info_installed(*names, **kwargs): ret = dict() for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names, failhard=failhard).items(): t_nfo = dict() + if pkg_nfo.get('status', 'ii')[1] != 'i': + continue # return only packages that are really installed # Translate dpkg-specific keys to a common structure for key, value in pkg_nfo.items(): if key == 'package': @@ -2803,6 +2806,8 @@ def info_installed(*names, **kwargs): t_nfo['packager'] = value elif key == 'homepage': t_nfo['url'] = value + elif key == 'status': + continue # only installed pkgs are returned, no need for status else: t_nfo[key] = value diff --git a/salt/modules/azurearm_dns.py b/salt/modules/azurearm_dns.py new file mode 100644 index 000000000000..7001215a8acb --- /dev/null +++ b/salt/modules/azurearm_dns.py @@ -0,0 +1,487 @@ +# -*- coding: utf-8 -*- +''' +Azure (ARM) DNS Execution Module + +.. versionadded:: Sodium + +:maintainer: +:maturity: new +:depends: + * `azure `_ >= 2.0.0 + * `azure-common `_ >= 1.1.8 + * `azure-mgmt `_ >= 1.0.0 + * `azure-mgmt-compute `_ >= 1.0.0 + * `azure-mgmt-dns `_ >= 2.0.0rc1 + * `azure-mgmt-network `_ >= 1.7.1 + * `azure-mgmt-resource `_ >= 1.1.0 + * `azure-mgmt-storage `_ >= 1.0.0 + * `azure-mgmt-web `_ >= 0.32.0 + * `azure-storage `_ >= 0.34.3 + * `msrestazure `_ >= 0.4.21 +:platform: linux + +:configuration: This module requires Azure Resource Manager credentials to be passed as keyword arguments +to every function in order to work properly. + + Required provider parameters: + + if using username and password: + * ``subscription_id`` + * ``username`` + * ``password`` + + if using a service principal: + * ``subscription_id`` + * ``tenant`` + * ``client_id`` + * ``secret`` + + Optional provider parameters: + + **cloud_environment**: Used to point the cloud driver to different API endpoints, such as Azure GovCloud. + Possible values: + * ``AZURE_PUBLIC_CLOUD`` (default) + * ``AZURE_CHINA_CLOUD`` + * ``AZURE_US_GOV_CLOUD`` + * ``AZURE_GERMAN_CLOUD`` + +''' + +# Python libs +from __future__ import absolute_import +import logging + +# Azure libs +HAS_LIBS = False +try: + import azure.mgmt.dns.models # pylint: disable=unused-import + from msrest.exceptions import SerializationError + from msrestazure.azure_exceptions import CloudError + HAS_LIBS = True +except ImportError: + pass + +__virtualname__ = 'azurearm_dns' + +log = logging.getLogger(__name__) + + +def __virtual__(): + if not HAS_LIBS: + return ( + False, + 'The following dependencies are required to use the AzureARM modules: ' + 'Microsoft Azure SDK for Python >= 2.0rc6, ' + 'MS REST Azure (msrestazure) >= 0.4' + ) + + return __virtualname__ + + +def record_set_create_or_update(name, zone_name, resource_group, record_type, **kwargs): + ''' + .. versionadded:: Sodium + + Creates or updates a record set within a DNS zone. + + :param name: The name of the record set, relative to the name of the zone. + + :param zone_name: The name of the DNS zone (without a terminating dot). + + :param resource_group: The name of the resource group. + + :param record_type: The type of DNS record in this record set. Record sets of type SOA can be + updated but not created (they are created when the DNS zone is created). + Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' + + CLI Example: + + .. code-block:: bash + + salt-call azurearm_dns.record_set_create_or_update myhost myzone testgroup A + arecords='[{ipv4_address: 10.0.0.1}]' ttl=300 + + ''' + dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + + try: + record_set_model = __utils__['azurearm.create_object_model']('dns', 'RecordSet', **kwargs) + except TypeError as exc: + result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + return result + + try: + record_set = dnsconn.record_sets.create_or_update( + relative_record_set_name=name, + zone_name=zone_name, + resource_group_name=resource_group, + record_type=record_type, + parameters=record_set_model, + if_match=kwargs.get('if_match'), + if_none_match=kwargs.get('if_none_match') + ) + result = record_set.as_dict() + except CloudError as exc: + __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + result = {'error': str(exc)} + except SerializationError as exc: + result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + + return result + + +def record_set_delete(name, zone_name, resource_group, record_type, **kwargs): + ''' + .. versionadded:: Sodium + + Deletes a record set from a DNS zone. This operation cannot be undone. + + :param name: The name of the record set, relative to the name of the zone. + + :param zone_name: The name of the DNS zone (without a terminating dot). + + :param resource_group: The name of the resource group. + + :param record_type: The type of DNS record in this record set. Record sets of type SOA cannot be + deleted (they are deleted when the DNS zone is deleted). + Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' + + CLI Example: + + .. code-block:: bash + + salt-call azurearm_dns.record_set_delete myhost myzone testgroup A + + ''' + result = False + dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + try: + record_set = dnsconn.record_sets.delete( + relative_record_set_name=name, + zone_name=zone_name, + resource_group_name=resource_group, + record_type=record_type, + if_match=kwargs.get('if_match') + ) + result = True + except CloudError as exc: + __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + + return result + + +def record_set_get(name, zone_name, resource_group, record_type, **kwargs): + ''' + .. versionadded:: Sodium + + Get a dictionary representing a record set's properties. + + :param name: The name of the record set, relative to the name of the zone. + + :param zone_name: The name of the DNS zone (without a terminating dot). + + :param resource_group: The name of the resource group. + + :param record_type: The type of DNS record in this record set. + Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' + + CLI Example: + + .. code-block:: bash + + salt-call azurearm_dns.record_set_get '@' myzone testgroup SOA + + ''' + dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + try: + record_set = dnsconn.record_sets.get( + relative_record_set_name=name, + zone_name=zone_name, + resource_group_name=resource_group, + record_type=record_type + ) + result = record_set.as_dict() + + except CloudError as exc: + __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + result = {'error': str(exc)} + + return result + + +def record_sets_list_by_type(zone_name, resource_group, record_type, top=None, recordsetnamesuffix=None, **kwargs): + ''' + .. versionadded:: Sodium + + Lists the record sets of a specified type in a DNS zone. + + :param zone_name: The name of the DNS zone (without a terminating dot). + + :param resource_group: The name of the resource group. + + :param record_type: The type of record sets to enumerate. + Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' + + :param top: The maximum number of record sets to return. If not specified, + returns up to 100 record sets. + + :param recordsetnamesuffix: The suffix label of the record set name that has + to be used to filter the record set enumerations. + + CLI Example: + + .. code-block:: bash + + salt-call azurearm_dns.record_sets_list_by_type myzone testgroup SOA + + ''' + result = {} + dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + try: + record_sets = __utils__['azurearm.paged_object_to_list']( + dnsconn.record_sets.list_by_type( + zone_name=zone_name, + resource_group_name=resource_group, + record_type=record_type, + top=top, + recordsetnamesuffix=recordsetnamesuffix + ) + ) + + for record_set in record_sets: + result[record_set['name']] = record_set + except CloudError as exc: + __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + result = {'error': str(exc)} + + return result + + +def record_sets_list_by_dns_zone(zone_name, resource_group, top=None, recordsetnamesuffix=None, **kwargs): + ''' + .. versionadded:: Sodium + + Lists all record sets in a DNS zone. + + :param zone_name: The name of the DNS zone (without a terminating dot). + + :param resource_group: The name of the resource group. + + :param top: The maximum number of record sets to return. If not specified, + returns up to 100 record sets. + + :param recordsetnamesuffix: The suffix label of the record set name that has + to be used to filter the record set enumerations. + + CLI Example: + + .. code-block:: bash + + salt-call azurearm_dns.record_sets_list_by_dns_zone myzone testgroup + + ''' + result = {} + dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + try: + record_sets = __utils__['azurearm.paged_object_to_list']( + dnsconn.record_sets.list_by_dns_zone( + zone_name=zone_name, + resource_group_name=resource_group, + top=top, + recordsetnamesuffix=recordsetnamesuffix + ) + ) + + for record_set in record_sets: + result[record_set['name']] = record_set + except CloudError as exc: + __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + result = {'error': str(exc)} + + return result + + +def zone_create_or_update(name, resource_group, **kwargs): + ''' + .. versionadded:: Sodium + + Creates or updates a DNS zone. Does not modify DNS records within the zone. + + :param name: The name of the DNS zone to create (without a terminating dot). + + :param resource_group: The name of the resource group. + + CLI Example: + + .. code-block:: bash + + salt-call azurearm_dns.zone_create_or_update myzone testgroup + + ''' + # DNS zones are global objects + kwargs['location'] = 'global' + + dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + + # Convert list of ID strings to list of dictionaries with id key. + if isinstance(kwargs.get('registration_virtual_networks'), list): + kwargs['registration_virtual_networks'] = [{'id': vnet} for vnet in kwargs['registration_virtual_networks']] + + if isinstance(kwargs.get('resolution_virtual_networks'), list): + kwargs['resolution_virtual_networks'] = [{'id': vnet} for vnet in kwargs['resolution_virtual_networks']] + + try: + zone_model = __utils__['azurearm.create_object_model']('dns', 'Zone', **kwargs) + except TypeError as exc: + result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + return result + + try: + zone = dnsconn.zones.create_or_update( + zone_name=name, + resource_group_name=resource_group, + parameters=zone_model, + if_match=kwargs.get('if_match'), + if_none_match=kwargs.get('if_none_match') + ) + result = zone.as_dict() + except CloudError as exc: + __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + result = {'error': str(exc)} + except SerializationError as exc: + result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + + return result + + +def zone_delete(name, resource_group, **kwargs): + ''' + .. versionadded:: Sodium + + Delete a DNS zone within a resource group. + + :param name: The name of the DNS zone to delete. + + :param resource_group: The name of the resource group. + + CLI Example: + + .. code-block:: bash + + salt-call azurearm_dns.zone_delete myzone testgroup + + ''' + result = False + dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + try: + zone = dnsconn.zones.delete( + zone_name=name, + resource_group_name=resource_group, + if_match=kwargs.get('if_match') + ) + zone.wait() + result = True + except CloudError as exc: + __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + + return result + + +def zone_get(name, resource_group, **kwargs): + ''' + .. versionadded:: Sodium + + Get a dictionary representing a DNS zone's properties, but not the + record sets within the zone. + + :param name: The DNS zone to get. + + :param resource_group: The name of the resource group. + + CLI Example: + + .. code-block:: bash + + salt-call azurearm_dns.zone_get myzone testgroup + + ''' + dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + try: + zone = dnsconn.zones.get( + zone_name=name, + resource_group_name=resource_group + ) + result = zone.as_dict() + + except CloudError as exc: + __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + result = {'error': str(exc)} + + return result + + +def zones_list_by_resource_group(resource_group, top=None, **kwargs): + ''' + .. versionadded:: Sodium + + Lists the DNS zones in a resource group. + + :param resource_group: The name of the resource group. + + :param top: The maximum number of DNS zones to return. If not specified, + returns up to 100 zones. + + CLI Example: + + .. code-block:: bash + + salt-call azurearm_dns.zones_list_by_resource_group testgroup + + ''' + result = {} + dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + try: + zones = __utils__['azurearm.paged_object_to_list']( + dnsconn.zones.list_by_resource_group( + resource_group_name=resource_group, + top=top + ) + ) + + for zone in zones: + result[zone['name']] = zone + except CloudError as exc: + __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + result = {'error': str(exc)} + + return result + + +def zones_list(top=None, **kwargs): + ''' + .. versionadded:: Sodium + + Lists the DNS zones in all resource groups in a subscription. + + :param top: The maximum number of DNS zones to return. If not specified, + returns up to 100 zones. + + CLI Example: + + .. code-block:: bash + + salt-call azurearm_dns.zones_list + + ''' + result = {} + dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + try: + zones = __utils__['azurearm.paged_object_to_list'](dnsconn.zones.list(top=top)) + + for zone in zones: + result[zone['name']] = zone + except CloudError as exc: + __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + result = {'error': str(exc)} + + return result diff --git a/salt/modules/azurearm_resource.py b/salt/modules/azurearm_resource.py index 28396b4552e0..6716d508d1ba 100644 --- a/salt/modules/azurearm_resource.py +++ b/salt/modules/azurearm_resource.py @@ -48,9 +48,11 @@ # Python libs from __future__ import absolute_import -from json import loads, dumps import logging +# Salt Libs +import salt.utils.json + # Azure libs HAS_LIBS = False try: @@ -1061,7 +1063,7 @@ def policy_definition_create_or_update(name, policy_rule, **kwargs): # pylint: polconn = __utils__['azurearm.get_client']('policy', **kwargs) # Convert OrderedDict to dict - prop_kwargs = {'policy_rule': loads(dumps(policy_rule))} + prop_kwargs = {'policy_rule': salt.utils.json.loads(salt.utils.json.dumps(policy_rule))} policy_kwargs = kwargs.copy() policy_kwargs.update(prop_kwargs) diff --git a/salt/modules/beacons.py b/salt/modules/beacons.py index 00a130de7f60..267102effd5b 100644 --- a/salt/modules/beacons.py +++ b/salt/modules/beacons.py @@ -57,18 +57,18 @@ def list_(return_yaml=True, beacons = None try: - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - res = __salt__['event.fire']({'func': 'list', - 'include_pillar': include_pillar, - 'include_opts': include_opts}, - 'manage_beacons') - if res: - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacons_list_complete', - wait=kwargs.get('timeout', default_event_wait)) - log.debug('event_ret %s', event_ret) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'func': 'list', + 'include_pillar': include_pillar, + 'include_opts': include_opts}, + 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacons_list_complete', + wait=kwargs.get('timeout', default_event_wait)) + log.debug('event_ret %s', event_ret) + if event_ret and event_ret['complete']: + beacons = event_ret['beacons'] except KeyError: # Effectively a no-op, since we can't really return without an event system ret = {} @@ -104,14 +104,14 @@ def list_available(return_yaml=True, **kwargs): beacons = None try: - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - res = __salt__['event.fire']({'func': 'list_available'}, 'manage_beacons') - if res: - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacons_list_available_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'func': 'list_available'}, 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacons_list_available_complete', + wait=kwargs.get('timeout', default_event_wait)) + if event_ret and event_ret['complete']: + beacons = event_ret['beacons'] except KeyError: # Effectively a no-op, since we can't really return without an event system ret = {} @@ -161,45 +161,50 @@ def add(name, beacon_data, **kwargs): else: try: # Attempt to load the beacon module so we have access to the validate function - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - res = __salt__['event.fire']({'name': name, - 'beacon_data': beacon_data, - 'func': 'validate_beacon'}, - 'manage_beacons') - if res: - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacon_validation_complete', - wait=kwargs.get('timeout', default_event_wait)) - valid = event_ret['valid'] - vcomment = event_ret['vcomment'] - - if not valid: - ret['result'] = False - ret['comment'] = ('Beacon {0} configuration invalid, ' - 'not adding.\n{1}'.format(name, vcomment)) - return ret - + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'name': name, + 'beacon_data': beacon_data, + 'func': 'validate_beacon'}, + 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacon_validation_complete', + wait=kwargs.get('timeout', default_event_wait)) + valid = event_ret['valid'] + vcomment = event_ret['vcomment'] + + if not valid: + ret['result'] = False + ret['comment'] = ('Beacon {0} configuration invalid, ' + 'not adding.\n{1}'.format(name, vcomment)) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Beacon add failed.' try: - res = __salt__['event.fire']({'name': name, - 'beacon_data': beacon_data, - 'func': 'add'}, 'manage_beacons') - if res: - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacon_add_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] - if name in beacons and beacons[name] == beacon_data: - ret['result'] = True - ret['comment'] = 'Added beacon: {0}.'.format(name) - else: - ret['result'] = False - ret['comment'] = event_ret['comment'] - return ret + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'name': name, + 'beacon_data': beacon_data, + 'func': 'add'}, 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacon_add_complete', + wait=kwargs.get('timeout', default_event_wait)) + if event_ret and event_ret['complete']: + beacons = event_ret['beacons'] + if name in beacons and beacons[name] == beacon_data: + ret['result'] = True + ret['comment'] = 'Added beacon: {0}.'.format(name) + elif event_ret: + ret['result'] = False + ret['comment'] = event_ret['comment'] + else: + ret['result'] = False + ret['comment'] = 'Did not receive the beacon add complete event before the timeout of {}s'.format( + kwargs.get('timeout', default_event_wait) + ) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Beacon add failed.' @@ -235,23 +240,23 @@ def modify(name, beacon_data, **kwargs): else: try: # Attempt to load the beacon module so we have access to the validate function - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - res = __salt__['event.fire']({'name': name, - 'beacon_data': beacon_data, - 'func': 'validate_beacon'}, - 'manage_beacons') - if res: - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacon_validation_complete', - wait=kwargs.get('timeout', default_event_wait)) - valid = event_ret['valid'] - vcomment = event_ret['vcomment'] - - if not valid: - ret['result'] = False - ret['comment'] = ('Beacon {0} configuration invalid, ' - 'not adding.\n{1}'.format(name, vcomment)) - return ret + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'name': name, + 'beacon_data': beacon_data, + 'func': 'validate_beacon'}, + 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacon_validation_complete', + wait=kwargs.get('timeout', default_event_wait)) + valid = event_ret['valid'] + vcomment = event_ret['vcomment'] + + if not valid: + ret['result'] = False + ret['comment'] = ('Beacon {0} configuration invalid, ' + 'not adding.\n{1}'.format(name, vcomment)) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system @@ -284,21 +289,26 @@ def modify(name, beacon_data, **kwargs): ret['changes']['diff'] = ''.join(_diff) try: - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - res = __salt__['event.fire']({'name': name, 'beacon_data': beacon_data, 'func': 'modify'}, 'manage_beacons') - if res: - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacon_modify_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] - if name in beacons and beacons[name] == beacon_data: - ret['result'] = True - ret['comment'] = 'Modified beacon: {0}.'.format(name) - else: - ret['result'] = False - ret['comment'] = event_ret['comment'] - return ret + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'name': name, 'beacon_data': beacon_data, 'func': 'modify'}, 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacon_modify_complete', + wait=kwargs.get('timeout', default_event_wait)) + if event_ret and event_ret['complete']: + beacons = event_ret['beacons'] + if name in beacons and beacons[name] == beacon_data: + ret['result'] = True + ret['comment'] = 'Modified beacon: {0}.'.format(name) + elif event_ret: + ret['result'] = False + ret['comment'] = event_ret['comment'] + else: + ret['result'] = False + ret['comment'] = 'Did not receive the beacon modify complete event before the timeout of {}s'.format( + kwargs.get('timeout', default_event_wait) + ) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Beacon add failed.' @@ -330,21 +340,26 @@ def delete(name, **kwargs): ret['comment'] = 'Beacon: {0} would be deleted.'.format(name) else: try: - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - res = __salt__['event.fire']({'name': name, 'func': 'delete'}, 'manage_beacons') - if res: - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacon_delete_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] - if name not in beacons: - ret['result'] = True - ret['comment'] = 'Deleted beacon: {0}.'.format(name) - return ret - else: - ret['result'] = False - ret['comment'] = event_ret['comment'] + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'name': name, 'func': 'delete'}, 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacon_delete_complete', + wait=kwargs.get('timeout', default_event_wait)) + if event_ret and event_ret['complete']: + beacons = event_ret['beacons'] + if name not in beacons: + ret['result'] = True + ret['comment'] = 'Deleted beacon: {0}.'.format(name) + return ret + elif event_ret: + ret['result'] = False + ret['comment'] = event_ret['comment'] + else: + ret['result'] = False + ret['comment'] = 'Did not receive the beacon delete complete event before the timeout of {}s'.format( + kwargs.get('timeout', default_event_wait) + ) except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Beacon add failed.' @@ -370,7 +385,7 @@ def save(**kwargs): beacons = list_(return_yaml=False, include_pillar=False, **kwargs) # move this file into an configurable opt - sfn = os.path.join(__opts__['config_dir'], + sfn = os.path.join(os.path.dirname(__opts__['conf_file']), os.path.dirname(__opts__['default_include']), 'beacons.conf') if beacons: @@ -411,21 +426,26 @@ def enable(**kwargs): ret['comment'] = 'Beacons would be enabled.' else: try: - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - res = __salt__['event.fire']({'func': 'enable'}, 'manage_beacons') - if res: - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacons_enabled_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] - if 'enabled' in beacons and beacons['enabled']: - ret['result'] = True - ret['comment'] = 'Enabled beacons on minion.' - else: - ret['result'] = False - ret['comment'] = 'Failed to enable beacons on minion.' - return ret + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'func': 'enable'}, 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacons_enabled_complete', + wait=kwargs.get('timeout', default_event_wait)) + if event_ret and event_ret['complete']: + beacons = event_ret['beacons'] + if 'enabled' in beacons and beacons['enabled']: + ret['result'] = True + ret['comment'] = 'Enabled beacons on minion.' + elif event_ret: + ret['result'] = False + ret['comment'] = 'Failed to enable beacons on minion.' + else: + ret['result'] = False + ret['comment'] = 'Did not receive the beacon enabled complete event before the timeout of {}s'.format( + kwargs.get('timeout', default_event_wait) + ) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Beacons enable job failed.' @@ -452,22 +472,27 @@ def disable(**kwargs): ret['comment'] = 'Beacons would be disabled.' else: try: - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - res = __salt__['event.fire']({'func': 'disable'}, 'manage_beacons') - if res: - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacons_disabled_complete', - wait=kwargs.get('timeout', default_event_wait)) - log.debug('event_ret %s', event_ret) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] - if 'enabled' in beacons and not beacons['enabled']: - ret['result'] = True - ret['comment'] = 'Disabled beacons on minion.' - else: - ret['result'] = False - ret['comment'] = 'Failed to disable beacons on minion.' - return ret + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'func': 'disable'}, 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacons_disabled_complete', + wait=kwargs.get('timeout', default_event_wait)) + log.debug('event_ret %s', event_ret) + if event_ret and event_ret['complete']: + beacons = event_ret['beacons'] + if 'enabled' in beacons and not beacons['enabled']: + ret['result'] = True + ret['comment'] = 'Disabled beacons on minion.' + elif event_ret: + ret['result'] = False + ret['comment'] = 'Failed to disable beacons on minion.' + else: + ret['result'] = False + ret['comment'] = 'Did not receive the beacon disabled complete event before the timeout of {}s'.format( + kwargs.get('timeout', default_event_wait) + ) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Beacons enable job failed.' @@ -516,26 +541,31 @@ def enable_beacon(name, **kwargs): return ret try: - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - res = __salt__['event.fire']({'func': 'enable_beacon', 'name': name}, 'manage_beacons') - if res: - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacon_enabled_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] - beacon_config_dict = _get_beacon_config_dict(beacons[name]) - - if 'enabled' in beacon_config_dict and beacon_config_dict['enabled']: - ret['result'] = True - ret['comment'] = 'Enabled beacon {0} on minion.'.format(name) + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'func': 'enable_beacon', 'name': name}, 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacon_enabled_complete', + wait=kwargs.get('timeout', default_event_wait)) + if event_ret and event_ret['complete']: + beacons = event_ret['beacons'] + beacon_config_dict = _get_beacon_config_dict(beacons[name]) + + if 'enabled' in beacon_config_dict and beacon_config_dict['enabled']: + ret['result'] = True + ret['comment'] = 'Enabled beacon {0} on minion.'.format(name) + else: + ret['result'] = False + ret['comment'] = 'Failed to enable beacon {0} on minion.'.format(name) + elif event_ret: + ret['result'] = False + ret['comment'] = event_ret['comment'] else: ret['result'] = False - ret['comment'] = 'Failed to enable beacon {0} on minion.'.format(name) - else: - ret['result'] = False - ret['comment'] = event_ret['comment'] - return ret + ret['comment'] = 'Did not receive the beacon enabled complete event before the timeout of {}s'.format( + kwargs.get('timeout', default_event_wait) + ) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Beacon enable job failed.' @@ -574,26 +604,31 @@ def disable_beacon(name, **kwargs): return ret try: - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - res = __salt__['event.fire']({'func': 'disable_beacon', 'name': name}, 'manage_beacons') - if res: - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacon_disabled_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] - beacon_config_dict = _get_beacon_config_dict(beacons[name]) - - if 'enabled' in beacon_config_dict and not beacon_config_dict['enabled']: - ret['result'] = True - ret['comment'] = 'Disabled beacon {0} on minion.'.format(name) + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'func': 'disable_beacon', 'name': name}, 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacon_disabled_complete', + wait=kwargs.get('timeout', default_event_wait)) + if event_ret and event_ret['complete']: + beacons = event_ret['beacons'] + beacon_config_dict = _get_beacon_config_dict(beacons[name]) + + if 'enabled' in beacon_config_dict and not beacon_config_dict['enabled']: + ret['result'] = True + ret['comment'] = 'Disabled beacon {0} on minion.'.format(name) + else: + ret['result'] = False + ret['comment'] = 'Failed to disable beacon on minion.' + elif event_ret: + ret['result'] = False + ret['comment'] = event_ret['comment'] else: ret['result'] = False - ret['comment'] = 'Failed to disable beacon on minion.' - else: - ret['result'] = False - ret['comment'] = event_ret['comment'] - return ret + ret['comment'] = 'Did not receive the beacon disabled complete event before the timeout of {}s'.format( + kwargs.get('timeout', default_event_wait) + ) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Beacon disable job failed.' @@ -618,26 +653,22 @@ def reset(**kwargs): ret['comment'] = 'Beacons would be reset.' else: try: - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - res = __salt__['event.fire']({'func': 'reset'}, 'manage_beacons') - if res: - wait = kwargs.get('timeout', default_event_wait) - event_ret = eventer.get_event( - tag='/salt/minion/minion_beacon_reset_complete', - wait=wait) - if event_ret and event_ret['complete']: - ret['result'] = True - ret['comment'] = 'Beacon configuration reset.' - else: - if event_ret is None: - ret['result'] = False - ret['comment'] = ( - 'minion reset event not recieved after {} seconds' - ).format(wait) + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + res = __salt__['event.fire']({'func': 'reset'}, 'manage_beacons') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_beacon_reset_complete', + wait=kwargs.get('timeout', default_event_wait)) + if event_ret and event_ret['complete']: + ret['result'] = True + ret['comment'] = 'Beacon configuration reset.' else: ret['result'] = False - ret['comment'] = event_ret['comment'] - return ret + if ret is not None: + ret['comment'] = event_ret['comment'] + else: + ret['comment'] = 'Beacon reset event never received' + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Beacon disable job failed.' diff --git a/salt/modules/boto3_route53.py b/salt/modules/boto3_route53.py index c5be14da6115..883fff20060f 100644 --- a/salt/modules/boto3_route53.py +++ b/salt/modules/boto3_route53.py @@ -121,7 +121,7 @@ def _wait_for_sync(change, conn, tries=10, sleep=20): if e.response.get('Error', {}).get('Code') == 'Throttling': log.debug('Throttled by AWS API.') else: - raise e + six.reraise(*sys.exc_info()) if status == 'INSYNC': return True time.sleep(sleep) @@ -898,7 +898,7 @@ def get_resource_records(HostedZoneId=None, Name=None, StartRecordName=None, log.debug('Throttled by AWS API.') time.sleep(3) continue - raise e + six.reraise(*sys.exc_info()) def change_resource_record_sets(HostedZoneId=None, Name=None, diff --git a/salt/modules/boto_cloudfront.py b/salt/modules/boto_cloudfront.py index 62018d86ba35..ea04e8d6b0ba 100644 --- a/salt/modules/boto_cloudfront.py +++ b/salt/modules/boto_cloudfront.py @@ -267,7 +267,7 @@ def export_distributions(region=None, key=None, keyid=None, profile=None): except botocore.exceptions.ClientError as err: # Raise an exception, as this is meant to be user-invoked at the CLI # as opposed to being called from execution or state modules - raise err + six.reraise(*sys.exc_info()) dumper = __utils__['yaml.get_dumper']('IndentedSafeOrderedDumper') return __utils__['yaml.dump']( diff --git a/salt/modules/boto_route53.py b/salt/modules/boto_route53.py index 4f2c5ae9b001..0e4c5f0982bc 100644 --- a/salt/modules/boto_route53.py +++ b/salt/modules/boto_route53.py @@ -279,7 +279,7 @@ def zone_exists(zone, region=None, key=None, keyid=None, profile=None, time.sleep(3) error_retries -= 1 continue - raise e + six.reraise(*sys.exc_info()) def create_zone(zone, private=False, vpc_id=None, vpc_region=None, region=None, @@ -528,7 +528,7 @@ def get_record(name, zone, record_type, fetch_all=False, region=None, key=None, time.sleep(3) error_retries -= 1 continue - raise e + six.reraise(*sys.exc_info()) if _record: ret['name'] = _decode_name(_record.name) @@ -603,7 +603,7 @@ def add_record(name, value, zone, record_type, identifier=None, ttl=None, time.sleep(3) error_retries -= 1 continue - raise e + six.reraise(*sys.exc_info()) _value = _munge_value(value, _type) while error_retries > 0: @@ -624,7 +624,7 @@ def add_record(name, value, zone, record_type, identifier=None, ttl=None, time.sleep(3) error_retries -= 1 continue - raise e + six.reraise(*sys.exc_info()) def update_record(name, value, zone, record_type, identifier=None, ttl=None, @@ -686,7 +686,7 @@ def update_record(name, value, zone, record_type, identifier=None, ttl=None, time.sleep(3) error_retries -= 1 continue - raise e + six.reraise(*sys.exc_info()) def delete_record(name, zone, record_type, identifier=None, all_records=False, @@ -747,7 +747,7 @@ def delete_record(name, zone, record_type, identifier=None, all_records=False, time.sleep(3) error_retries -= 1 continue - raise e + six.reraise(*sys.exc_info()) def _try_func(conn, func, **args): diff --git a/salt/modules/capirca_acl.py b/salt/modules/capirca_acl.py index 65726df1fa8e..277ea7b141dc 100644 --- a/salt/modules/capirca_acl.py +++ b/salt/modules/capirca_acl.py @@ -7,7 +7,7 @@ .. versionadded:: 2017.7.0 -:codeauthor: Mircea Ulinic & Robert Ankeny +:codeauthor: Mircea Ulinic & Robert Ankeny :maturity: new :depends: capirca :platform: unix diff --git a/salt/modules/celery.py b/salt/modules/celery.py index 9a7fd027d932..55fee4048aa4 100644 --- a/salt/modules/celery.py +++ b/salt/modules/celery.py @@ -12,9 +12,11 @@ # Import python libs import logging +import sys # Import salt libs from salt.exceptions import SaltInvocationError +from salt.ext import six log = logging.getLogger(__name__) @@ -106,5 +108,5 @@ def run_task(task_name, args=None, kwargs=None, broker=None, backend=None, wait_ except TimeoutError as ex: log.error('Waiting for the result of a celery task execution timed out.') if raise_timeout: - raise ex + six.reraise(*sys.exc_info()) return False diff --git a/salt/modules/chocolatey.py b/salt/modules/chocolatey.py index 9d98a47e8993..1b2c17674dba 100644 --- a/salt/modules/chocolatey.py +++ b/salt/modules/chocolatey.py @@ -54,13 +54,15 @@ def _clear_context(): Clear variables stored in __context__. Run this function when a new version of chocolatey is installed. ''' - for var in (x for x in __context__ if x.startswith('chocolatey.')): + choco_items = [x for x in __context__ if x.startswith('chocolatey.')] + for var in choco_items: __context__.pop(var) def _yes(): ''' Returns ['--yes'] if on v0.9.9.0 or later, otherwise returns an empty list + Confirm all prompts (--yes_ is available on v0.9.9.0 or later ''' if 'chocolatey._yes' in __context__: return __context__['chocolatey._yes'] @@ -487,7 +489,6 @@ def install(name, # Salt doesn't need to see the progress cmd.extend(_no_progress()) cmd.extend(_yes()) - result = __salt__['cmd.run_all'](cmd, python_shell=False) if result['retcode'] not in [0, 1641, 3010]: @@ -876,7 +877,6 @@ def update(name, source=None, pre_versions=False): # Salt doesn't need to see the progress cmd.extend(_no_progress()) cmd.extend(_yes()) - result = __salt__['cmd.run_all'](cmd, python_shell=False) if result['retcode'] not in [0, 1641, 3010]: diff --git a/salt/modules/chroot.py b/salt/modules/chroot.py new file mode 100644 index 000000000000..bc089ebf18ed --- /dev/null +++ b/salt/modules/chroot.py @@ -0,0 +1,357 @@ +# -*- coding: utf-8 -*- + +''' +:maintainer: Alberto Planas +:maturity: new +:depends: None +:platform: Linux +''' +from __future__ import absolute_import, print_function, unicode_literals +import copy +import logging +import os +import sys +import tempfile + + +import salt +import salt.client.ssh.state +import salt.client.ssh.wrapper.state +import salt.defaults.exitcodes +import salt.exceptions +import salt.ext.six as six +import salt.utils.args + + +__func_alias__ = { + 'apply_': 'apply' +} + +log = logging.getLogger(__name__) + + +def __virtual__(): + ''' + Chroot command is required. + ''' + if __utils__['path.which']('chroot') is not None: + return True + else: + return (False, 'Module chroot requires the command chroot') + + +def exist(root): + ''' + Return True if the chroot environment is present. + ''' + dev = os.path.join(root, 'dev') + proc = os.path.join(root, 'proc') + sys = os.path.join(root, 'sys') + return all(os.path.isdir(i) for i in (root, dev, proc, sys)) + + +def create(root): + ''' + Create a basic chroot environment. + + Note that this environment is not functional. The caller needs to + install the minimal required binaries, including Python if + chroot.call is called. + + root + Path to the chroot environment + + CLI Example: + + .. code-block:: bash + + salt myminion chroot.create /chroot + + ''' + if not exist(root): + dev = os.path.join(root, 'dev') + proc = os.path.join(root, 'proc') + sys = os.path.join(root, 'sys') + try: + os.makedirs(dev, mode=0o755) + os.makedirs(proc, mode=0o555) + os.makedirs(sys, mode=0o555) + except OSError as e: + log.error('Error when trying to create chroot directories: %s', e) + return False + return True + + +def call(root, function, *args, **kwargs): + ''' + Executes a Salt function inside a chroot environment. + + The chroot does not need to have Salt installed, but Python is + required. + + root + Path to the chroot environment + + function + Salt execution module function + + CLI Example: + + .. code-block:: bash + + salt myminion chroot.call /chroot test.ping + salt myminion chroot.call /chroot ssh.set_auth_key user key=mykey + + ''' + + if not function: + raise salt.exceptions.CommandExecutionError( + 'Missing function parameter') + + if not exist(root): + raise salt.exceptions.CommandExecutionError( + 'Chroot environment not found') + + # Create a temporary directory inside the chroot where we can + # untar salt-thin + thin_dest_path = tempfile.mkdtemp(dir=root) + thin_path = __utils__['thin.gen_thin']( + __opts__['cachedir'], + extra_mods=__salt__['config.option']('thin_extra_mods', ''), + so_mods=__salt__['config.option']('thin_so_mods', '') + ) + # Some bug in Salt is preventing us to use `archive.tar` here. A + # AsyncZeroMQReqChannel is not closed at the end os the salt-call, + # and makes the client never exit. + # + # stdout = __salt__['archive.tar']('xzf', thin_path, dest=thin_dest_path) + # + stdout = __salt__['cmd.run'](['tar', 'xzf', thin_path, + '-C', thin_dest_path]) + if stdout: + __utils__['files.rm_rf'](thin_dest_path) + return {'result': False, 'comment': stdout} + + chroot_path = os.path.join(os.path.sep, + os.path.relpath(thin_dest_path, root)) + try: + safe_kwargs = salt.utils.args.clean_kwargs(**kwargs) + salt_argv = [ + 'python{}'.format(sys.version_info[0]), + os.path.join(chroot_path, 'salt-call'), + '--metadata', + '--local', + '--log-file', os.path.join(chroot_path, 'log'), + '--cachedir', os.path.join(chroot_path, 'cache'), + '--out', 'json', + '-l', 'quiet', + '--', + function + ] + list(args) + [ + '{}={}'.format(k, v) for (k, v) in safe_kwargs.items() + ] + ret = __salt__['cmd.run_chroot'](root, [str(x) for x in salt_argv]) + + # Process "real" result in stdout + try: + data = __utils__['json.find_json'](ret['stdout']) + local = data.get('local', data) + if isinstance(local, dict) and 'retcode' in local: + __context__['retcode'] = local['retcode'] + return local.get('return', data) + except (KeyError, ValueError): + return { + 'result': False, + 'comment': "Can't parse container command output" + } + finally: + __utils__['files.rm_rf'](thin_dest_path) + + +def apply_(root, mods=None, **kwargs): + ''' + Apply an state inside a chroot. + + This function will call `chroot.highstate` or `chroot.sls` based + on the arguments passed to this function. It exists as a more + intuitive way of applying states. + + root + Path to the chroot environment + + For a formal description of the possible parameters accepted in + this function, check `state.apply_` documentation. + + CLI Example: + + .. code-block:: bash + + salt myminion chroot.apply /chroot + salt myminion chroot.apply /chroot stuff + salt myminion chroot.apply /chroot stuff pillar='{"foo": "bar"}' + + ''' + if mods: + return sls(root, mods, **kwargs) + return highstate(root, **kwargs) + + +def _create_and_execute_salt_state(root, chunks, file_refs, test, hash_type): + ''' + Create the salt_stage tarball, and execute in the chroot + ''' + # Create the tar containing the state pkg and relevant files. + salt.client.ssh.wrapper.state._cleanup_slsmod_low_data(chunks) + trans_tar = salt.client.ssh.state.prep_trans_tar( + salt.fileclient.get_file_client(__opts__), chunks, file_refs, + __pillar__, root) + trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, hash_type) + + ret = None + + # Create a temporary directory inside the chroot where we can move + # the salt_stage.tgz + salt_state_path = tempfile.mkdtemp(dir=root) + salt_state_path = os.path.join(salt_state_path, 'salt_state.tgz') + salt_state_path_in_chroot = salt_state_path.replace(root, '', 1) + try: + salt.utils.files.copyfile(trans_tar, salt_state_path) + ret = call(root, 'state.pkg', salt_state_path_in_chroot, + test=test, pkg_sum=trans_tar_sum, + hash_type=hash_type) + finally: + __utils__['files.rm_rf'](salt_state_path) + + return ret + + +def sls(root, mods, saltenv='base', test=None, exclude=None, **kwargs): + ''' + Execute the states in one or more SLS files inside the chroot. + + root + Path to the chroot environment + + saltenv + Specify a salt fileserver environment to be used when applying + states + + mods + List of states to execute + + test + Run states in test-only (dry-run) mode + + exclude + Exclude specific states from execution. Accepts a list of sls + names, a comma-separated string of sls names, or a list of + dictionaries containing ``sls`` or ``id`` keys. Glob-patterns + may be used to match multiple states. + + For a formal description of the possible parameters accepted in + this function, check `state.sls` documentation. + + CLI Example: + + .. code-block:: bash + + salt '*' chroot.sls /chroot stuff pillar='{"foo": "bar"}' + ''' + # Get a copy of the pillar data, to avoid overwriting the current + # pillar, instead the one delegated + pillar = copy.deepcopy(__pillar__) + pillar.update(kwargs.get('pillar', {})) + + # Clone the options data and apply some default values. May not be + # needed, as this module just delegate + opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) + st_ = salt.client.ssh.state.SSHHighState( + opts, pillar, __salt__, + salt.fileclient.get_file_client(__opts__)) + + if isinstance(mods, six.string_types): + mods = mods.split(',') + + high_data, errors = st_.render_highstate({saltenv: mods}) + if exclude: + if isinstance(exclude, six.string_types): + exclude = exclude.split(',') + if '__exclude__' in high_data: + high_data['__exclude__'].extend(exclude) + else: + high_data['__exclude__'] = exclude + + high_data, ext_errors = st_.state.reconcile_extend(high_data) + errors += ext_errors + errors += st_.state.verify_high(high_data) + if errors: + return errors + + high_data, req_in_errors = st_.state.requisite_in(high_data) + errors += req_in_errors + if errors: + return errors + + high_data = st_.state.apply_exclude(high_data) + + # Compile and verify the raw chunks + chunks = st_.state.compile_high_data(high_data) + file_refs = salt.client.ssh.state.lowstate_file_refs( + chunks, + salt.client.ssh.wrapper.state._merge_extra_filerefs( + kwargs.get('extra_filerefs', ''), + opts.get('extra_filerefs', ''))) + + hash_type = opts['hash_type'] + return _create_and_execute_salt_state(root, chunks, file_refs, test, + hash_type) + + +def highstate(root, **kwargs): + ''' + Retrieve the state data from the salt master for this minion and + execute it inside the chroot. + + root + Path to the chroot environment + + For a formal description of the possible parameters accepted in + this function, check `state.highstate` documentation. + + CLI Example: + + .. code-block:: bash + + salt myminion chroot.highstate /chroot + salt myminion chroot.highstate /chroot pillar='{"foo": "bar"}' + + ''' + # Get a copy of the pillar data, to avoid overwriting the current + # pillar, instead the one delegated + pillar = copy.deepcopy(__pillar__) + pillar.update(kwargs.get('pillar', {})) + + # Clone the options data and apply some default values. May not be + # needed, as this module just delegate + opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) + st_ = salt.client.ssh.state.SSHHighState( + opts, pillar, __salt__, + salt.fileclient.get_file_client(__opts__)) + + # Compile and verify the raw chunks + chunks = st_.compile_low_chunks() + file_refs = salt.client.ssh.state.lowstate_file_refs( + chunks, + salt.client.ssh.wrapper.state._merge_extra_filerefs( + kwargs.get('extra_filerefs', ''), + opts.get('extra_filerefs', ''))) + # Check for errors + for chunk in chunks: + if not isinstance(chunk, dict): + __context__['retcode'] = 1 + return chunks + + test = kwargs.pop('test', False) + hash_type = opts['hash_type'] + return _create_and_execute_salt_state(root, chunks, file_refs, test, + hash_type) diff --git a/salt/modules/cmdmod.py b/salt/modules/cmdmod.py index 723eddc67b04..b9d6863aa7c4 100644 --- a/salt/modules/cmdmod.py +++ b/salt/modules/cmdmod.py @@ -666,21 +666,12 @@ def _get_stripped(cmd): except (OSError, IOError) as exc: msg = ( 'Unable to run command \'{0}\' with the context \'{1}\', ' - 'reason: '.format( + 'reason: {2}'.format( cmd if output_loglevel is not None else 'REDACTED', - new_kwargs + new_kwargs, + exc ) ) - try: - if exc.filename is None: - msg += 'command not found' - else: - msg += '{0}: {1}'.format(exc, exc.filename) - except AttributeError: - # Both IOError and OSError have the filename attribute, so this - # is a precaution in case the exception classes in the previous - # try/except are changed. - msg += 'unknown' raise CommandExecutionError(msg) try: @@ -982,8 +973,8 @@ def run(cmd, .. warning:: For versions 2018.3.3 and above on macosx while using runas, - to pass special characters to the command you need to escape - the characters on the shell. + on linux while using run, to pass special characters to the + command you need to escape the characters on the shell. Example: @@ -2904,6 +2895,7 @@ def run_chroot(root, group=None, shell=DEFAULT_SHELL, python_shell=True, + binds=None, env=None, clean_env=False, template=None, @@ -2929,19 +2921,17 @@ def run_chroot(root, :param str root: Path to the root of the jail to use. - stdin - A string of standard input can be specified for the command to be run using - the ``stdin`` parameter. This can be useful in cases where sensitive - information must be read from standard input.: + :param str stdin: A string of standard input can be specified for + the command to be run using the ``stdin`` parameter. This can + be useful in cases where sensitive information must be read + from standard input.: - runas - User to run script as. + :param str runas: User to run script as. - group - Group to run script as. + :param str group: Group to run script as. - shell - Shell to execute under. Defaults to the system default shell. + :param str shell: Shell to execute under. Defaults to the system + default shell. :param str cmd: The command to run. ex: ``ls -lart /home`` @@ -2965,6 +2955,11 @@ def run_chroot(root, arguments. Set to True to use shell features, such as pipes or redirection. + :param list binds: List of directories that will be exported inside + the chroot with the bind option. + + .. versionadded:: Sodium + :param dict env: Environment variables to be set prior to execution. .. note:: @@ -2983,11 +2978,11 @@ def run_chroot(root, engine will be used to render the downloaded file. Currently jinja, mako, and wempy are supported. - :param bool rstrip: - Strip all whitespace off the end of output before it is returned. + :param bool rstrip: Strip all whitespace off the end of output + before it is returned. - :param str umask: - The umask (in octal) to use when running the command. + :param str umask: The umask (in octal) to use when running the + command. :param str output_encoding: Control the encoding used to decode the command's output. @@ -3061,6 +3056,15 @@ def run_chroot(root, 'sysfs', fstype='sysfs') + binds = binds if binds else [] + for bind_exported in binds: + bind_exported_to = os.path.relpath(bind_exported, os.path.sep) + bind_exported_to = os.path.join(root, bind_exported_to) + __salt__['mount.mount']( + bind_exported_to, + bind_exported, + opts='default,bind') + # Execute chroot routine sh_ = '/bin/sh' if os.path.isfile(os.path.join(root, 'bin/bash')): @@ -3111,6 +3115,11 @@ def run_chroot(root, log.error('Processes running in chroot could not be killed, ' 'filesystem will remain mounted') + for bind_exported in binds: + bind_exported_to = os.path.relpath(bind_exported, os.path.sep) + bind_exported_to = os.path.join(root, bind_exported_to) + __salt__['mount.umount'](bind_exported_to) + __salt__['mount.umount'](os.path.join(root, 'sys')) __salt__['mount.umount'](os.path.join(root, 'proc')) __salt__['mount.umount'](os.path.join(root, 'dev')) diff --git a/salt/modules/config.py b/salt/modules/config.py index dd2442c0bf13..ce490234e1b3 100644 --- a/salt/modules/config.py +++ b/salt/modules/config.py @@ -6,6 +6,7 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals import copy +import fnmatch import re import os import logging @@ -77,6 +78,12 @@ 'aliases.file': '/etc/aliases', 'virt': {'tunnel': False, 'images': os.path.join(syspaths.SRV_ROOT_DIR, 'salt-images')}, + + 'docker.exec_driver': 'docker-exec', + 'docker.compare_container_networks': { + 'static': ['Aliases', 'Links', 'IPAMConfig'], + 'automatic': ['IPAddress', 'Gateway', + 'GlobalIPv6Address', 'IPv6Gateway']}, } @@ -130,12 +137,60 @@ def valid_fileproto(uri): def option( value, - default='', + default=None, omit_opts=False, + omit_grains=False, + omit_pillar=False, omit_master=False, - omit_pillar=False): + omit_all=False, + wildcard=False): ''' - Pass in a generic option and receive the value that will be assigned + Returns the setting for the specified config value. The priority for + matches is the same as in :py:func:`config.get `, + only this function does not recurse into nested data structures. Another + difference between this function and :py:func:`config.get + ` is that it comes with a set of "sane defaults". + To view these, you can run the following command: + + .. code-block:: bash + + salt '*' config.option '*' omit_all=True wildcard=True + + default + The default value if no match is found. If not specified, then the + fallback default will be an empty string, unless ``wildcard=True``, in + which case the return will be an empty dictionary. + + omit_opts : False + Pass as ``True`` to exclude matches from the minion configuration file + + omit_grains : False + Pass as ``True`` to exclude matches from the grains + + omit_pillar : False + Pass as ``True`` to exclude matches from the pillar data + + omit_master : False + Pass as ``True`` to exclude matches from the master configuration file + + omit_all : True + Shorthand to omit all of the above and return matches only from the + "sane defaults". + + .. versionadded:: Neon + + wildcard : False + If used, this will perform pattern matching on keys. Note that this + will also significantly change the return data. Instead of only a value + being returned, a dictionary mapping the matched keys to their values + is returned. For example, using ``wildcard=True`` with a ``key`` of + ``'foo.ba*`` could return a dictionary like so: + + .. code-block:: python + + {'foo.bar': True, 'foo.baz': False} + + .. versionadded:: Neon CLI Example: @@ -143,18 +198,49 @@ def option( salt '*' config.option redis.host ''' - if not omit_opts: - if value in __opts__: - return __opts__[value] - if not omit_master: - if value in __pillar__.get('master', {}): - return __pillar__['master'][value] - if not omit_pillar: - if value in __pillar__: - return __pillar__[value] - if value in DEFAULTS: - return DEFAULTS[value] - return default + if omit_all: + omit_opts = omit_grains = omit_pillar = omit_master = True + + if default is None: + default = '' if not wildcard else {} + + if not wildcard: + if not omit_opts: + if value in __opts__: + return __opts__[value] + if not omit_grains: + if value in __grains__: + return __grains__[value] + if not omit_pillar: + if value in __pillar__: + return __pillar__[value] + if not omit_master: + if value in __pillar__.get('master', {}): + return __pillar__['master'][value] + if value in DEFAULTS: + return DEFAULTS[value] + + # No match + return default + else: + # We need to do the checks in the reverse order so that minion opts + # takes precedence + ret = {} + for omit, data in ((omit_master, __pillar__.get('master', {})), + (omit_pillar, __pillar__), + (omit_grains, __grains__), + (omit_opts, __opts__)): + if not omit: + ret.update( + {x: data[x] for x in fnmatch.filter(data, value)} + ) + # Check the DEFAULTS as well to see if the pattern matches it + for item in (x for x in fnmatch.filter(DEFAULTS, value) + if x not in ret): + ret[item] = DEFAULTS[item] + + # If no matches, return the default + return ret or default def merge(value, @@ -219,9 +305,9 @@ def get(key, default='', delimiter=':', merge=None, omit_opts=False, .. versionadded: 0.14.0 Attempt to retrieve the named value from the minion config file, pillar, - grains or the master config. If the named value is not available, return the - value specified by ``default``. If not specified, the default is an empty - string. + grains or the master config. If the named value is not available, return + the value specified by the ``default`` argument. If this argument is not + specified, ``default`` falls back to an empty string. Values can also be retrieved from nested dictionaries. Assume the below data structure: diff --git a/salt/modules/cp.py b/salt/modules/cp.py index e0fc3587a95e..d1deede8e9eb 100644 --- a/salt/modules/cp.py +++ b/salt/modules/cp.py @@ -423,6 +423,8 @@ def get_url(path, dest='', saltenv='base', makedirs=False, source_hash=None): log.error('Unable to fetch file %s from saltenv %s.', salt.utils.url.redact_http_basic_auth(path), saltenv) + if result: + return salt.utils.stringutils.to_unicode(result) return result @@ -443,7 +445,7 @@ def get_file_str(path, saltenv='base'): if isinstance(fn_, six.string_types): try: with salt.utils.files.fopen(fn_, 'r') as fp_: - return fp_.read() + return salt.utils.stringutils.to_unicode(fp_.read()) except IOError: return False return fn_ @@ -829,8 +831,8 @@ def push(path, keep_symlinks=False, upload_path=None, remove_source=False): 'path': load_path_list, 'size': os.path.getsize(path), 'tok': auth.gen_token(b'salt')} - channel = salt.transport.client.ReqChannel.factory(__opts__) - try: + + with salt.transport.client.ReqChannel.factory(__opts__) as channel: with salt.utils.files.fopen(path, 'rb') as fp_: init_send = False while True: @@ -853,8 +855,6 @@ def push(path, keep_symlinks=False, upload_path=None, remove_source=False): 'setting on the master.') return ret init_send = True - finally: - channel.close() def push_dir(path, glob=None, upload_path=None): diff --git a/salt/modules/cron.py b/salt/modules/cron.py index 7b3cfef4e4e0..a81c62552d4c 100644 --- a/salt/modules/cron.py +++ b/salt/modules/cron.py @@ -208,13 +208,24 @@ def write_cron_file(user, path): Some OS' do not support specifying user via the `crontab` command i.e. (Solaris, AIX) ''' - if _check_instance_uid_match(user) or __grains__.get('os_family') in ('Solaris', 'AIX'): + # Some OS' do not support specifying user via the `crontab` command + if __grains__.get('os_family') in ('Solaris', 'AIX'): return __salt__['cmd.retcode'](_get_cron_cmdstr(path), runas=user, python_shell=False) == 0 - else: + # If Salt is running from same user as requested in cron module we don't need any user switch + elif _check_instance_uid_match(user): + return __salt__['cmd.retcode'](_get_cron_cmdstr(path), + python_shell=False) == 0 + # If Salt is running from root user it could modify any user's crontab + elif _check_instance_uid_match('root'): return __salt__['cmd.retcode'](_get_cron_cmdstr(path, user), python_shell=False) == 0 + # Edge cases here, let's try do a runas + else: + return __salt__['cmd.retcode'](_get_cron_cmdstr(path), + runas=user, + python_shell=False) == 0 def write_cron_file_verbose(user, path): @@ -233,13 +244,24 @@ def write_cron_file_verbose(user, path): Some OS' do not support specifying user via the `crontab` command i.e. (Solaris, AIX) ''' - if _check_instance_uid_match(user) or __grains__.get('os_family') in ('Solaris', 'AIX'): + # Some OS' do not support specifying user via the `crontab` command + if __grains__.get('os_family') in ('Solaris', 'AIX'): return __salt__['cmd.run_all'](_get_cron_cmdstr(path), runas=user, python_shell=False) - else: + # If Salt is running from same user as requested in cron module we don't need any user switch + elif _check_instance_uid_match(user): + return __salt__['cmd.run_all'](_get_cron_cmdstr(path), + python_shell=False) + # If Salt is running from root user it could modify any user's crontab + elif _check_instance_uid_match('root'): return __salt__['cmd.run_all'](_get_cron_cmdstr(path, user), python_shell=False) + # Edge cases here, let's try do a runas + else: + return __salt__['cmd.run_all'](_get_cron_cmdstr(path), + runas=user, + python_shell=False) def _write_cron_lines(user, lines): @@ -248,7 +270,7 @@ def _write_cron_lines(user, lines): ''' lines = [salt.utils.stringutils.to_str(_l) for _l in lines] path = salt.utils.files.mkstemp() - if _check_instance_uid_match(user) or __grains__.get('os_family') in ('Solaris', 'AIX'): + if _check_instance_uid_match('root') or __grains__.get('os_family') in ('Solaris', 'AIX'): # In some cases crontab command should be executed as user rather than root with salt.utils.files.fpopen(path, 'w+', uid=__salt__['file.user_to_uid'](user), mode=0o600) as fp_: fp_.writelines(lines) @@ -284,25 +306,40 @@ def raw_cron(user): salt '*' cron.raw_cron root ''' - if _check_instance_uid_match(user) or __grains__.get('os_family') in ('Solaris', 'AIX'): + # Some OS' do not support specifying user via the `crontab` command + if __grains__.get('os_family') in ('Solaris', 'AIX'): cmd = 'crontab -l' # Preserve line endings - lines = salt.utils.data.decode( - __salt__['cmd.run_stdout'](cmd, - runas=user, - ignore_retcode=True, - rstrip=False, - python_shell=False) - ).splitlines(True) - else: + lines = salt.utils.data.decode(__salt__['cmd.run_stdout'](cmd, + runas=user, + ignore_retcode=True, + rstrip=False, + python_shell=False)).splitlines(True) + # If Salt is running from same user as requested in cron module we don't need any user switch + elif _check_instance_uid_match(user): + cmd = 'crontab -l' + # Preserve line endings + lines = salt.utils.data.decode(__salt__['cmd.run_stdout'](cmd, + ignore_retcode=True, + rstrip=False, + python_shell=False)).splitlines(True) + # If Salt is running from root user it could modify any user's crontab + elif _check_instance_uid_match('root'): cmd = 'crontab -u {0} -l'.format(user) # Preserve line endings - lines = salt.utils.data.decode( - __salt__['cmd.run_stdout'](cmd, - ignore_retcode=True, - rstrip=False, - python_shell=False) - ).splitlines(True) + lines = salt.utils.data.decode(__salt__['cmd.run_stdout'](cmd, + ignore_retcode=True, + rstrip=False, + python_shell=False)).splitlines(True) + # Edge cases here, let's try do a runas + else: + cmd = 'crontab -l' + # Preserve line endings + lines = salt.utils.data.decode(__salt__['cmd.run_stdout'](cmd, + runas=user, + ignore_retcode=True, + rstrip=False, + python_shell=False)).splitlines(True) if len(lines) != 0 and lines[0].startswith('# DO NOT EDIT THIS FILE - edit the master and reinstall.'): del lines[0:3] diff --git a/salt/modules/debian_ip.py b/salt/modules/debian_ip.py index 8017c9497170..c8bd587a1ec2 100644 --- a/salt/modules/debian_ip.py +++ b/salt/modules/debian_ip.py @@ -158,7 +158,7 @@ def _error_msg_iface(iface, option, expected): a list of expected values. ''' msg = 'Invalid option -- Interface: {0}, Option: {1}, Expected: [{2}]' - return msg.format(iface, option, '|'.join(expected)) + return msg.format(iface, option, '|'.join(str(e) for e in expected)) def _error_msg_routes(iface, option, expected): @@ -181,7 +181,7 @@ def _error_msg_network(option, expected): a list of expected values. ''' msg = 'Invalid network setting -- Setting: {0}, Expected: [{1}]' - return msg.format(option, '|'.join(expected)) + return msg.format(option, '|'.join(str(e) for e in expected)) def _log_default_network(opt, value): @@ -1808,7 +1808,8 @@ def down(iface, iface_type): # Slave devices are controlled by the master. # Source 'interfaces' aren't brought down. if iface_type not in ['slave', 'source']: - return __salt__['cmd.run'](['ifdown', iface]) + cmd = ['ip', 'link', 'set', iface, 'down'] + return __salt__['cmd.run'](cmd, python_shell=False) return None @@ -1869,7 +1870,8 @@ def up(iface, iface_type): # pylint: disable=C0103 # Slave devices are controlled by the master. # Source 'interfaces' aren't brought up. if iface_type not in ('slave', 'source'): - return __salt__['cmd.run'](['ifup', iface]) + cmd = ['ip', 'link', 'set', iface, 'up'] + return __salt__['cmd.run'](cmd, python_shell=False) return None diff --git a/salt/modules/debian_service.py b/salt/modules/debian_service.py index 03ee14827a16..109a752bdaca 100644 --- a/salt/modules/debian_service.py +++ b/salt/modules/debian_service.py @@ -328,7 +328,7 @@ def enabled(name, **kwargs): def disabled(name): ''' - Return True if the named service is enabled, false otherwise + Return True if the named service is disabled, false otherwise CLI Example: diff --git a/salt/modules/dockermod.py b/salt/modules/dockermod.py index 1bdc98dc6289..3fea97e99997 100644 --- a/salt/modules/dockermod.py +++ b/salt/modules/dockermod.py @@ -50,7 +50,23 @@ If you have previously performed a ``docker login`` from the minion, then the credentials saved in ``~/.docker/config.json`` will be used for any actions which require authentication. If not, then credentials can be configured in -Pillar data. The configuration schema is as follows: +any of the following locations: + +- Minion config file +- Grains +- Pillar data +- Master config file (requires :conf_minion:`pillar_opts` to be set to ``True`` + in Minion config file in order to work) + +.. important:: + Versions prior to Neon require that Docker credentials are configured in + Pillar data. Be advised that Pillar data is still recommended though, + because this keeps the configuration from being stored on the Minion. + + Also, keep in mind that if one gets your ``~/.docker/config.json``, the + password can be decoded from its contents. + +The configuration schema is as follows: .. code-block:: yaml @@ -346,7 +362,7 @@ def _get_client(timeout=NOTSET, **kwargs): client_kwargs['timeout'] = timeout for key, val in (('base_url', 'docker.url'), ('version', 'docker.version')): - param = __salt__['config.get'](val, NOTSET) + param = __salt__['config.option'](val, NOTSET) if param is not NOTSET: client_kwargs[key] = param @@ -359,7 +375,7 @@ def _get_client(timeout=NOTSET, **kwargs): # it's not defined by user. client_kwargs['version'] = 'auto' - docker_machine = __salt__['config.get']('docker.machine', NOTSET) + docker_machine = __salt__['config.option']('docker.machine', NOTSET) if docker_machine is not NOTSET: docker_machine_json = __salt__['cmd.run']( @@ -455,7 +471,8 @@ 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) + ret = __context__['docker.update_mine'] = __salt__[ + 'config.option']('docker.update_mine', default=True) return ret @@ -521,7 +538,7 @@ def _get_exec_driver(): ''' contextkey = 'docker.exec_driver' if contextkey not in __context__: - from_config = __salt__['config.get'](contextkey, None) + from_config = __salt__['config.option'](contextkey, None) # This if block can be removed once we make docker-exec a default # option, as it is part of the logic in the commented block above. if from_config is not None: @@ -1037,6 +1054,11 @@ def compare_container_networks(first, second): than waiting for a new Salt release one can just set :conf_minion:`docker.compare_container_networks`. + .. versionchanged:: Neon + This config option can now also be set in pillar data and grains. + Additionally, it can be set in the master config file, provided that + :conf_minion:`pillar_opts` is enabled on the minion. + .. note:: The checks for automatic IP configuration described above only apply if ``IPAMConfig`` is among the keys set for static IP checks in @@ -1058,7 +1080,8 @@ def compare_container_networks(first, second): def _get_nets(data): return data.get('NetworkSettings', {}).get('Networks', {}) - compare_keys = __opts__['docker.compare_container_networks'] + compare_keys = __salt__['config.option']('docker.compare_container_networks') + result1 = inspect_container(first) \ if not isinstance(first, dict) \ else first @@ -1215,12 +1238,20 @@ def _check_ipconfig(ret, net_name, **kwargs): old_val.remove(result1['Config']['Hostname']) except (AttributeError, ValueError): pass + try: + old_val.remove(result1['Id'][:12]) + except (AttributeError, ValueError): + pass if not old_val: old_val = None try: new_val.remove(result2['Config']['Hostname']) except (AttributeError, ValueError): pass + try: + new_val.remove(result2['Id'][:12]) + except (AttributeError, ValueError): + pass if not new_val: new_val = None elif key == 'IPAMConfig': @@ -1388,23 +1419,21 @@ def login(*registries): # NOTE: This function uses the "docker login" CLI command so that login # information is added to the config.json, since docker-py isn't designed # to do so. - registry_auth = __pillar__.get('docker-registries', {}) + registry_auth = __salt__['config.get']('docker-registries', {}) ret = {'retcode': 0} errors = ret.setdefault('Errors', []) if not isinstance(registry_auth, dict): errors.append('\'docker-registries\' Pillar value must be a dictionary') registry_auth = {} - for key, data in six.iteritems(__pillar__): + for reg_name, reg_conf in six.iteritems( + __salt__['config.option']('*-docker-registries', wildcard=True)): try: - if key.endswith('-docker-registries'): - try: - registry_auth.update(data) - except TypeError: - errors.append( - '\'{0}\' Pillar value must be a dictionary'.format(key) - ) - except AttributeError: - pass + registry_auth.update(reg_conf) + except TypeError: + errors.append( + 'Docker registry \'{0}\' was not specified as a ' + 'dictionary'.format(reg_name) + ) # If no registries passed, we will auth to all of them if not registries: @@ -1931,7 +1960,7 @@ def resolve_image_id(name): return False -def resolve_tag(name, tags=None, **kwargs): +def resolve_tag(name, **kwargs): ''' .. versionadded:: 2017.7.2 .. versionchanged:: 2018.3.0 @@ -1961,7 +1990,6 @@ def resolve_tag(name, tags=None, **kwargs): tags .. deprecated:: 2018.3.0 - Ignored if passed, will be removed in the Neon release. CLI Examples: @@ -1976,13 +2004,6 @@ def resolve_tag(name, tags=None, **kwargs): if kwargs: __utils__['args.invalid_kwargs'](kwargs) - if tags is not None: - __utils__['versions.warn_until']( - 'Neon', - 'The \'tags\' argument to docker.resolve_tag is deprecated. It no ' - 'longer is used, and will be removed in the Neon release.' - ) - try: inspect_result = inspect_image(name) tags = inspect_result['RepoTags'] @@ -3908,8 +3929,7 @@ def build(path=None, api_response=False, fileobj=None, dockerfile=None, - buildargs=None, - image=None): + buildargs=None): ''' .. versionchanged:: 2018.3.0 If the built image should be tagged, then the repository and tag must @@ -3997,14 +4017,6 @@ def build(path=None, ''' _prep_pull() - if image is not None: - __utils__['versions.warn_until']( - 'Neon', - 'The \'image\' argument to docker.build has been deprecated, ' - 'please use \'repository\' instead.' - ) - respository = image - if repository or tag: if not repository and tag: # Have to have both or neither @@ -4089,8 +4101,7 @@ def commit(name, repository, tag='latest', message=None, - author=None, - image=None): + author=None): ''' .. versionchanged:: 2018.3.0 The repository and tag must now be passed separately using the @@ -4139,14 +4150,6 @@ def commit(name, salt myminion docker.commit mycontainer myuser/myimage mytag ''' - if image is not None: - __utils__['versions.warn_until']( - 'Neon', - 'The \'image\' argument to docker.commit has been deprecated, ' - 'please use \'repository\' instead.' - ) - respository = image - if not isinstance(repository, six.string_types): repository = six.text_type(repository) if not isinstance(tag, six.string_types): @@ -4172,7 +4175,6 @@ def commit(name, if image_id is None: raise CommandExecutionError('No image ID was returned in API response') - ret['Image'] = image ret['Id'] = image_id return ret @@ -4237,8 +4239,7 @@ def dangling(prune=False, force=False): def import_(source, repository, tag='latest', - api_response=False, - image=None): + api_response=False): ''' .. versionchanged:: 2018.3.0 The repository and tag must now be passed separately using the @@ -4290,14 +4291,6 @@ def import_(source, salt myminion docker.import /tmp/cent7-minimal.tar.xz myuser/centos:7 salt myminion docker.import salt://dockerimages/cent7-minimal.tar.xz myuser/centos:7 ''' - if image is not None: - __utils__['versions.warn_until']( - 'Neon', - 'The \'image\' argument to docker.import has been deprecated, ' - 'please use \'repository\' instead.' - ) - respository = image - if not isinstance(repository, six.string_types): repository = six.text_type(repository) if not isinstance(tag, six.string_types): @@ -4346,7 +4339,7 @@ def import_(source, return ret -def load(path, repository=None, tag=None, image=None): +def load(path, repository=None, tag=None): ''' .. versionchanged:: 2018.3.0 If the loaded image should be tagged, then the repository and tag must @@ -4408,14 +4401,6 @@ def load(path, repository=None, tag=None, image=None): salt myminion docker.load /path/to/image.tar salt myminion docker.load salt://path/to/docker/saved/image.tar repository=myuser/myimage tag=mytag ''' - if image is not None: - __utils__['versions.warn_until']( - 'Neon', - 'The \'image\' argument to docker.load has been deprecated, ' - 'please use \'repository\' instead.' - ) - respository = image - if (repository or tag) and not (repository and tag): # Have to have both or neither raise SaltInvocationError( @@ -4974,7 +4959,7 @@ def save(name, return ret -def tag_(name, repository, tag='latest', force=False, image=None): +def tag_(name, repository, tag='latest', force=False): ''' .. versionchanged:: 2018.3.0 The repository and tag must now be passed separately using the @@ -5010,14 +4995,6 @@ def tag_(name, repository, tag='latest', force=False, image=None): salt myminion docker.tag 0123456789ab myrepo/mycontainer mytag ''' - if image is not None: - __utils__['versions.warn_until']( - 'Neon', - 'The \'image\' argument to docker.tag has been deprecated, ' - 'please use \'repository\' instead.' - ) - respository = image - if not isinstance(repository, six.string_types): repository = six.text_type(repository) if not isinstance(tag, six.string_types): @@ -5413,15 +5390,6 @@ def connect_container_to_network(container, net_id, **kwargs): salt myminion docker.connect_container_to_network web-1 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc ''' kwargs = __utils__['args.clean_kwargs'](**kwargs) - network_id = kwargs.pop('network_id', None) - if network_id is not None: - __utils__['versions.warn_until']( - 'Neon', - 'The \'network_id\' argument to docker.build has been deprecated, ' - 'please use \'net_id\' instead.' - ) - net_id = network_id - log.debug( 'Connecting container \'%s\' to network \'%s\' with the following ' 'configuration: %s', container, net_id, kwargs @@ -5432,7 +5400,7 @@ def connect_container_to_network(container, net_id, **kwargs): **kwargs) log.debug( 'Successfully connected container \'%s\' to network \'%s\'', - container, network_id + container, net_id ) _clear_context() return True @@ -6977,15 +6945,6 @@ def sls_build(repository, salt myminion docker.sls_build imgname base=mybase mods=rails,web ''' - name = kwargs.pop('name', None) - if name is not None: - __utils__['versions.warn_until']( - 'Neon', - 'The \'name\' argument to docker.sls_build has been deprecated, ' - 'please use \'repository\' instead.' - ) - repository = name - create_kwargs = __utils__['args.clean_kwargs'](**copy.deepcopy(kwargs)) for key in ('image', 'name', 'cmd', 'interactive', 'tty', 'extra_filerefs'): try: diff --git a/salt/modules/elasticsearch.py b/salt/modules/elasticsearch.py index 4a953154b1b8..87a9b1a5b1e6 100644 --- a/salt/modules/elasticsearch.py +++ b/salt/modules/elasticsearch.py @@ -52,6 +52,7 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals import logging +import sys # Import Salt Libs from salt.exceptions import CommandExecutionError, SaltInvocationError @@ -171,7 +172,7 @@ def ping(allow_failure=False, hosts=None, profile=None): _get_instance(hosts, profile) except CommandExecutionError as e: if allow_failure: - raise e + six.reraise(*sys.exc_info()) return False return True @@ -264,6 +265,59 @@ def cluster_stats(nodes=None, hosts=None, profile=None): raise CommandExecutionError("Cannot retrieve cluster stats, server returned code {0} with message {1}".format(e.status_code, e.error)) +def cluster_get_settings(flat_settings=False, include_defaults=False, hosts=None, profile=None): + ''' + .. versionadded:: Neon + + Return Elasticsearch cluster settings. + + flat_settings + Return settings in flat format. + + include_defaults + Whether to return all default clusters setting. + + CLI example:: + + salt myminion elasticsearch.cluster_get_settings + ''' + es = _get_instance(hosts, profile) + + try: + return es.cluster.get_settings(flat_settings=flat_settings, include_defaults=include_defaults) + except elasticsearch.TransportError as e: + raise CommandExecutionError("Cannot retrieve cluster settings, server returned code {0} with message {1}".format(e.status_code, e.error)) + + +def cluster_put_settings(body=None, flat_settings=False, hosts=None, profile=None): + ''' + .. versionadded:: Neon + + Set Elasticsearch cluster settings. + + body + The settings to be updated. Can be either 'transient' or 'persistent' (survives cluster restart) + http://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-update-settings.html + + flat_settings + Return settings in flat format. + + CLI example:: + + salt myminion elasticsearch.cluster_put_settings '{"persistent": {"indices.recovery.max_bytes_per_sec": "50mb"}}' + salt myminion elasticsearch.cluster_put_settings '{"transient": {"indices.recovery.max_bytes_per_sec": "50mb"}}' + ''' + if not body: + message = 'You must provide a body with settings' + raise SaltInvocationError(message) + es = _get_instance(hosts, profile) + + try: + return es.cluster.put_settings(body=body, flat_settings=flat_settings) + except elasticsearch.TransportError as e: + raise CommandExecutionError("Cannot update cluster settings, server returned code {0} with message {1}".format(e.status_code, e.error)) + + def alias_create(indices, alias, hosts=None, body=None, profile=None, source=None): ''' Create an alias for a specific index/indices @@ -1214,3 +1268,44 @@ def snapshot_delete(repository, snapshot, hosts=None, profile=None): return True except elasticsearch.TransportError as e: raise CommandExecutionError("Cannot delete snapshot {0} from repository {1}, server returned code {2} with message {3}".format(snapshot, repository, e.status_code, e.error)) + + +def flush_synced(hosts=None, profile=None, **kwargs): + ''' + .. versionadded:: Neon + + Perform a normal flush, then add a generated unique marker (sync_id) to all shards. + http://www.elastic.co/guide/en/elasticsearch/reference/current/indices-synced-flush.html + + index + (Optional, string) A comma-separated list of index names; use _all or empty string for all indices. Defaults to '_all'. + + ignore_unavailable + (Optional, boolean) If true, missing or closed indices are not included in the response. Defaults to false. + + allow_no_indices + (Optional, boolean) If true, the request does not return an error if a wildcard expression or _all value retrieves only missing or closed indices. + This parameter also applies to index aliases that point to a missing or closed index. + + expand_wildcards + (Optional, string) Controls what kind of indices that wildcard expressions can expand to. + + Valid values are:: + + all - Expand to open and closed indices. + open - Expand only to open indices. + closed - Expand only to closed indices. + none - Wildcard expressions are not accepted. + + The defaults settings for the above parameters depend on the API being used. + + CLI example:: + + salt myminion elasticsearch.flush_synced index='index1,index2' ignore_unavailable=True allow_no_indices=True expand_wildcards='all' + ''' + es = _get_instance(hosts, profile) + + try: + return es.indices.flush_synced(kwargs) + except elasticsearch.TransportError as e: + raise CommandExecutionError("Cannot flush synced, server returned code {} with message {}".format(e.status_code, e.error)) diff --git a/salt/modules/event.py b/salt/modules/event.py index f6a043923deb..bbdefe4fb09f 100644 --- a/salt/modules/event.py +++ b/salt/modules/event.py @@ -72,16 +72,15 @@ def fire_master(data, tag, preload=None): load.update(preload) for master in masters: - channel = salt.transport.client.ReqChannel.factory(__opts__, master_uri=master) - try: - channel.send(load) - # channel.send was successful. - # Ensure ret is True. - ret = True - except Exception: - ret = False - finally: - channel.close() + with salt.transport.client.ReqChannel.factory(__opts__, + master_uri=master) as channel: + try: + channel.send(load) + # channel.send was successful. + # Ensure ret is True. + ret = True + except Exception: # pylint: disable=bare-except + ret = False return ret else: # Usually, we can send the event via the minion, which is faster @@ -107,13 +106,12 @@ def fire(data, tag): salt '*' event.fire '{"data":"my event data"}' 'tag' ''' try: - event = salt.utils.event.get_event('minion', # was __opts__['id'] + with salt.utils.event.get_event('minion', # was __opts__['id'] sock_dir=__opts__['sock_dir'], transport=__opts__['transport'], opts=__opts__, - listen=False) - - return event.fire_event(data, tag) + listen=False) as event: + return event.fire_event(data, tag) except Exception: exc_type, exc_value, exc_traceback = sys.exc_info() lines = traceback.format_exception(exc_type, exc_value, exc_traceback) diff --git a/salt/modules/file.py b/salt/modules/file.py index d0847c669b76..c9c4e7d52d7f 100644 --- a/salt/modules/file.py +++ b/salt/modules/file.py @@ -3353,11 +3353,29 @@ def link(src, path): try: os.link(src, path) return True - except (OSError, IOError): - raise CommandExecutionError('Could not create \'{0}\''.format(path)) + except (OSError, IOError) as E: + raise CommandExecutionError('Could not create \'{0}\': {1}'.format(path, E)) return False +def is_hardlink(path): + ''' + Check if the path is a hard link by verifying that the number of links + is larger than 1 + + CLI Example: + + .. code-block:: bash + + salt '*' file.is_hardlink /path/to/link + ''' + + # Simply use lstat and count the st_nlink field to determine if this path + # is hardlinked to something. + res = lstat(os.path.expanduser(path)) + return res and res['st_nlink'] > 1 + + def is_link(path): ''' Check if the path is a symbolic link @@ -5336,11 +5354,11 @@ def manage_file(name, # Write the static contents to a temporary file tmp = salt.utils.files.mkstemp(prefix=salt.utils.files.TEMPFILE_PREFIX, text=True) - if salt.utils.platform.is_windows(): - contents = os.linesep.join( - _splitlines_preserving_trailing_newline(contents)) with salt.utils.files.fopen(tmp, 'wb') as tmp_: if encoding: + if salt.utils.platform.is_windows(): + contents = os.linesep.join( + _splitlines_preserving_trailing_newline(contents)) log.debug('File will be encoded with %s', encoding) tmp_.write(contents.encode(encoding=encoding, errors=encoding_errors)) else: diff --git a/salt/modules/gentoo_service.py b/salt/modules/gentoo_service.py index 2bbb11243336..08bb89d1eb4b 100644 --- a/salt/modules/gentoo_service.py +++ b/salt/modules/gentoo_service.py @@ -26,6 +26,11 @@ # Define the module's virtual name __virtualname__ = 'service' +# Define the module's function aliases +__func_alias__ = { + 'reload_': 'reload' +} + def __virtual__(): ''' diff --git a/salt/modules/glance.py b/salt/modules/glance.py index 16b90c907209..fc87c7800e02 100644 --- a/salt/modules/glance.py +++ b/salt/modules/glance.py @@ -104,7 +104,7 @@ def _auth(profile=None, api_version=2, **connection_args): Only intended to be used within glance-enabled modules ''' __utils__['versions.warn_until']( - 'Neon', + 'Aluminium', ( 'The glance module has been deprecated and will be removed in {version}. ' 'Please update to using the glanceng module' diff --git a/salt/modules/gpg.py b/salt/modules/gpg.py index d08a2edcb3bd..1198efd4a50b 100644 --- a/salt/modules/gpg.py +++ b/salt/modules/gpg.py @@ -1020,7 +1020,7 @@ def sign(user=None, else: signed_data = gpg.sign_file(_fp, keyid=keyid, passphrase=gpg_passphrase) if output: - with salt.utils.files.flopen(output, 'w') as fout: + with salt.utils.files.flopen(output, 'wb') as fout: fout.write(salt.utils.stringutils.to_bytes(signed_data.data)) else: raise SaltInvocationError('filename or text must be passed.') diff --git a/salt/modules/groupadd.py b/salt/modules/groupadd.py index e2e1560ab072..15dec6e89837 100644 --- a/salt/modules/groupadd.py +++ b/salt/modules/groupadd.py @@ -12,8 +12,12 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals import logging +import functools +import os from salt.ext import six +import salt.utils.files +import salt.utils.stringutils try: import grp except ImportError: @@ -40,6 +44,18 @@ def add(name, gid=None, system=False, root=None): ''' Add the specified group + name + Name of the new group + + gid + Use GID for the new group + + system + Create a system account + + root + Directory to chroot into + CLI Example: .. code-block:: bash @@ -51,11 +67,12 @@ def add(name, gid=None, system=False, root=None): cmd.append('-g {0}'.format(gid)) if system and __grains__['kernel'] != 'OpenBSD': cmd.append('-r') - cmd.append(name) if root is not None: cmd.extend(('-R', root)) + cmd.append(name) + ret = __salt__['cmd.run_all'](cmd, python_shell=False) return not ret['retcode'] @@ -65,34 +82,53 @@ def delete(name, root=None): ''' Remove the named group + name + Name group to delete + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' group.delete foo ''' - cmd = ['groupdel', name] + cmd = ['groupdel'] if root is not None: cmd.extend(('-R', root)) + cmd.append(name) + ret = __salt__['cmd.run_all'](cmd, python_shell=False) return not ret['retcode'] -def info(name): +def info(name, root=None): ''' Return information about a group + name + Name of the group + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' group.info foo ''' + if root is not None: + getgrnam = functools.partial(_getgrnam, root=root) + else: + getgrnam = functools.partial(grp.getgrnam) + try: - grinfo = grp.getgrnam(name) + grinfo = getgrnam(name) except KeyError: return {} else: @@ -109,10 +145,16 @@ def _format_info(data): 'members': data.gr_mem} -def getent(refresh=False): +def getent(refresh=False, root=None): ''' Return info on all groups + refresh + Force a refresh of group information + + root + Directory to chroot into + CLI Example: .. code-block:: bash @@ -123,41 +165,74 @@ def getent(refresh=False): return __context__['group.getent'] ret = [] - for grinfo in grp.getgrall(): + if root is not None: + getgrall = functools.partial(_getgrall, root=root) + else: + getgrall = functools.partial(grp.getgrall) + + for grinfo in getgrall(): ret.append(_format_info(grinfo)) __context__['group.getent'] = ret return ret +def _chattrib(name, key, value, param, root=None): + ''' + Change an attribute for a named user + ''' + pre_info = info(name, root=root) + if not pre_info: + return False + + if value == pre_info[key]: + return True + + cmd = ['groupmod'] + + if root is not None: + cmd.extend(('-R', root)) + + cmd.extend((param, value, name)) + + __salt__['cmd.run'](cmd, python_shell=False) + return info(name, root=root).get(key) == value + + def chgid(name, gid, root=None): ''' Change the gid for a named group + name + Name of the group to modify + + gid + Change the group ID to GID + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' group.chgid foo 4376 ''' - pre_gid = __salt__['file.group_to_gid'](name) - if gid == pre_gid: - return True - cmd = ['groupmod', '-g', gid, name] - - if root is not None: - cmd.extend(('-R', root)) - - __salt__['cmd.run'](cmd, python_shell=False) - post_gid = __salt__['file.group_to_gid'](name) - if post_gid != pre_gid: - return post_gid == gid - return False + return _chattrib(name, 'gid', gid, '-g', root=root) def adduser(name, username, root=None): ''' Add a user in the group. + name + Name of the group to modify + + username + Username to add to the group + + root + Directory to chroot into + CLI Example: .. code-block:: bash @@ -178,7 +253,7 @@ def adduser(name, username, root=None): else: cmd = ['gpasswd', '--add', username, name] if root is not None: - cmd.extend(('-Q', root)) + cmd.extend(('--root', root)) else: cmd = ['usermod', '-G', name, username] if root is not None: @@ -193,6 +268,15 @@ def deluser(name, username, root=None): ''' Remove a user from the group. + name + Name of the group to modify + + username + Username to delete from the group + + root + Directory to chroot into + CLI Example: .. code-block:: bash @@ -216,7 +300,7 @@ def deluser(name, username, root=None): else: cmd = ['gpasswd', '--del', username, name] if root is not None: - cmd.extend(('-R', root)) + cmd.extend(('--root', root)) retcode = __salt__['cmd.retcode'](cmd, python_shell=False) elif __grains__['kernel'] == 'OpenBSD': out = __salt__['cmd.run_stdout']('id -Gn {0}'.format(username), @@ -239,6 +323,15 @@ def members(name, members_list, root=None): ''' Replaces members of the group with a provided list. + name + Name of the group to modify + + members_list + Username list to set into the group + + root + Directory to chroot into + CLI Example: salt '*' group.members foo 'user1,user2,user3,...' @@ -259,7 +352,7 @@ def members(name, members_list, root=None): else: cmd = ['gpasswd', '--members', members_list, name] if root is not None: - cmd.extend(('-R', root)) + cmd.extend(('--root', root)) retcode = __salt__['cmd.retcode'](cmd, python_shell=False) elif __grains__['kernel'] == 'OpenBSD': retcode = 1 @@ -284,3 +377,43 @@ def members(name, members_list, root=None): return False return not retcode + + +def _getgrnam(name, root=None): + ''' + Alternative implementation for getgrnam, that use only /etc/group + ''' + root = root or '/' + passwd = os.path.join(root, 'etc/group') + with salt.utils.files.fopen(passwd) as fp_: + for line in fp_: + line = salt.utils.stringutils.to_unicode(line) + comps = line.strip().split(':') + if len(comps) < 4: + log.debug('Ignoring group line: %s', line) + continue + if comps[0] == name: + # Generate a getpwnam compatible output + comps[2] = int(comps[2]) + comps[3] = comps[3].split(',') if comps[3] else [] + return grp.struct_group(comps) + raise KeyError('getgrnam(): name not found: {}'.format(name)) + + +def _getgrall(root=None): + ''' + Alternative implemetantion for getgrall, that use only /etc/group + ''' + root = root or '/' + passwd = os.path.join(root, 'etc/group') + with salt.utils.files.fopen(passwd) as fp_: + for line in fp_: + line = salt.utils.stringutils.to_unicode(line) + comps = line.strip().split(':') + if len(comps) < 4: + log.debug('Ignoring group line: %s', line) + continue + # Generate a getgrall compatible output + comps[2] = int(comps[2]) + comps[3] = comps[3].split(',') if comps[3] else [] + yield grp.struct_group(comps) diff --git a/salt/modules/hashutil.py b/salt/modules/hashutil.py index 5123cc7cd7e6..3a05a902e8d3 100644 --- a/salt/modules/hashutil.py +++ b/salt/modules/hashutil.py @@ -115,13 +115,13 @@ def base64_b64decode(instr): def base64_encodestring(instr): ''' - Encode a string as base64 using the "legacy" Python interface. + Encode a byte-like object as base64 using the "modern" Python interface. - Among other possible differences, the "legacy" encoder includes + Among other possible differences, the "modern" encoder includes a newline ('\\n') character after every 76 characters and always - at the end of the encoded string. + at the end of the encoded byte-like object. - .. versionadded:: 2014.7.0 + .. versionadded:: Neon CLI Example: @@ -167,9 +167,9 @@ def base64_encodefile(fname): def base64_decodestring(instr): ''' - Decode a base64-encoded string using the "legacy" Python interface + Decode a base64-encoded byte-like object using the "modern" Python interface - .. versionadded:: 2014.7.0 + .. versionadded:: Neon CLI Example: @@ -263,6 +263,21 @@ def hmac_signature(string, shared_secret, challenge_hmac): return salt.utils.hashutils.hmac_signature(string, shared_secret, challenge_hmac) +def hmac_compute(string, shared_secret): + ''' + .. versionadded:: Sodium + + Compute a HMAC SHA256 digest using a string and secret. + + CLI Example: + + .. code-block:: bash + + salt '*' hashutil.hmac_compute 'get salted' 'shared secret' + ''' + return salt.utils.hashutils.hmac_compute(string, shared_secret) + + def github_signature(string, shared_secret, challenge_hmac): ''' Verify a challenging hmac signature against a string / shared-secret for diff --git a/salt/modules/hipchat.py b/salt/modules/hipchat.py deleted file mode 100644 index db7e5891279e..000000000000 --- a/salt/modules/hipchat.py +++ /dev/null @@ -1,356 +0,0 @@ -# -*- coding: utf-8 -*- -''' -Module for sending messages to hipchat. - -.. versionadded:: 2015.5.0 - -:configuration: This module can be used by either passing an api key and version - directly or by specifying both in a configuration profile in the salt - master/minion config. - - It is possible to use a different API than http://api.hipchat.com, - by specifying the API URL in config as api_url, or by passing the value directly. - - For example: - - .. code-block:: yaml - - hipchat: - api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - api_version: v1 - - Custom API Example: - - .. code-block:: yaml - - hipchat: - api_url: http://api.hipchat.myteam.com - api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - api_version: v2 -''' -# Import Python Libs -from __future__ import absolute_import, print_function, unicode_literals -import logging - -# Import Salt libs -import salt.utils.http -import salt.utils.json - -# Import 3rd-party Libs -# pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.ext.six.moves.urllib.parse import urljoin as _urljoin -from salt.ext.six.moves.urllib.parse import urlencode as _urlencode -from salt.ext.six.moves import range -import salt.ext.six.moves.http_client - -# pylint: enable=import-error,no-name-in-module,redefined-builtin - -log = logging.getLogger(__name__) - -__virtualname__ = 'hipchat' - - -def __virtual__(): - ''' - Return virtual name of the module. - - :return: The virtual name of the module. - ''' - return __virtualname__ - - -def _query(function, - api_url=None, - api_key=None, - api_version=None, - room_id=None, - method='GET', - data=None): - ''' - HipChat object method function to construct and execute on the API URL. - - :param api_url: The HipChat API URL. - :param api_key: The HipChat api key. - :param function: The HipChat api function to perform. - :param api_version: The HipChat api version (v1 or v2). - :param method: The HTTP method, e.g. GET or POST. - :param data: The data to be sent for POST method. - :return: The json response from the API call or False. - ''' - headers = {} - query_params = {} - - if not api_url: - try: - options = __salt__['config.option']('hipchat') - api_url = options.get('api_url') - except (NameError, KeyError, AttributeError): - pass # not mandatory, thus won't fail if not found - - if not api_key or not api_version: - try: - options = __salt__['config.option']('hipchat') - if not api_key: - api_key = options.get('api_key') - if not api_version: - api_version = options.get('api_version') - except (NameError, KeyError, AttributeError): - log.error("No HipChat api key or version found.") - return False - - if room_id: - room_id = 'room/{0}/notification'.format(room_id) - else: - room_id = 'room/0/notification' - - hipchat_functions = { - 'v1': { - 'rooms': { - 'request': 'rooms/list', - 'response': 'rooms', - }, - 'users': { - 'request': 'users/list', - 'response': 'users', - }, - 'message': { - 'request': 'rooms/message', - 'response': 'status', - }, - }, - 'v2': { - 'rooms': { - 'request': 'room', - 'response': 'items', - }, - 'users': { - 'request': 'user', - 'response': 'items', - }, - 'message': { - 'request': room_id, - 'response': None, - }, - }, - } - - use_api_url = 'https://api.hipchat.com' # default API URL - if api_url: - use_api_url = api_url - base_url = _urljoin(use_api_url, api_version + '/') - path = hipchat_functions.get(api_version).get(function).get('request') - url = _urljoin(base_url, path, False) - - if api_version == 'v1': - query_params['format'] = 'json' - query_params['auth_token'] = api_key - - if method == 'POST': - headers['Content-Type'] = 'application/x-www-form-urlencoded' - - if data: - if data.get('notify', None): - data['notify'] = 1 - data = _urlencode(data) - elif api_version == 'v2': - headers['Authorization'] = 'Bearer {0}'.format(api_key) - if data: - data = salt.utils.json.dumps(data) - - if method == 'POST': - headers['Content-Type'] = 'application/json' - else: - log.error('Unsupported HipChat API version') - return False - - result = salt.utils.http.query( - url, - method, - params=query_params, - data=data, - decode=True, - status=True, - header_dict=headers, - opts=__opts__, - ) - - if result.get('status', None) == salt.ext.six.moves.http_client.OK: - response = hipchat_functions.get(api_version).get(function).get('response') - return result.get('dict', {}).get(response, None) - elif result.get('status', None) == salt.ext.six.moves.http_client.NO_CONTENT and \ - api_version == 'v2': - return True - else: - log.debug(url) - log.debug(query_params) - log.debug(data) - log.debug(result) - if result.get('error'): - log.error(result) - return False - - -def list_rooms(api_url=None, - api_key=None, - api_version=None): - ''' - List all HipChat rooms. - - :param api_url: The HipChat API URL, if not specified in the configuration. - :param api_key: The HipChat admin api key. - :param api_version: The HipChat api version, if not specified in the configuration. - :return: The room list. - - CLI Example: - - .. code-block:: bash - - salt '*' hipchat.list_rooms - - salt '*' hipchat.list_rooms api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 api_version=v1 - ''' - foo = _query(function='rooms', - api_url=api_url, - api_key=api_key, - api_version=api_version) - log.debug('foo %s', foo) - return foo - - -def list_users(api_url=None, - api_key=None, - api_version=None): - ''' - List all HipChat users. - - :param api_url: The HipChat API URL, if not specified in the configuration. - :param api_key: The HipChat admin api key. - :param api_version: The HipChat api version, if not specified in the configuration. - :return: The user list. - - CLI Example: - - .. code-block:: bash - - salt '*' hipchat.list_users - - salt '*' hipchat.list_users api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 api_version=v1 - ''' - return _query(function='users', - api_url=api_url, - api_key=api_key, - api_version=api_version) - - -def find_room(name, - api_url=None, - api_key=None, - api_version=None): - ''' - Find a room by name and return it. - - :param name: The room name. - :param api_url: The HipChat API URL, if not specified in the configuration. - :param api_key: The HipChat admin api key. - :param api_version: The HipChat api version, if not specified in the configuration. - :return: The room object. - - CLI Example: - - .. code-block:: bash - - salt '*' hipchat.find_room name="Development Room" - - salt '*' hipchat.find_room name="Development Room" api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 api_version=v1 - ''' - rooms = list_rooms(api_url=api_url, - api_key=api_key, - api_version=api_version) - if rooms: - for x in range(0, len(rooms)): - if rooms[x]['name'] == name: - return rooms[x] - return False - - -def find_user(name, - api_url=None, - api_key=None, - api_version=None): - ''' - Find a user by name and return it. - - :param name: The user name. - :param api_url: The HipChat API URL, if not specified in the configuration. - :param api_key: The HipChat admin api key. - :param api_version: The HipChat api version, if not specified in the configuration. - :return: The user object. - - CLI Example: - - .. code-block:: bash - - salt '*' hipchat.find_user name="Thomas Hatch" - - salt '*' hipchat.find_user name="Thomas Hatch" api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 api_version=v1 - ''' - users = list_users(api_url=api_url, - api_key=api_key, - api_version=api_version) - if users: - for x in range(0, len(users)): - if users[x]['name'] == name: - return users[x] - return False - - -def send_message(room_id, - message, - from_name, - api_url=None, - api_key=None, - api_version=None, - color='yellow', - notify=False): - ''' - Send a message to a HipChat room. - - :param room_id: The room id or room name, either will work. - :param message: The message to send to the HipChat room. - :param from_name: Specify who the message is from. - :param api_url: The HipChat api URL, if not specified in the configuration. - :param api_key: The HipChat api key, if not specified in the configuration. - :param api_version: The HipChat api version, if not specified in the configuration. - :param color: The color for the message, default: yellow. - :param notify: Whether to notify the room, default: False. - :return: Boolean if message was sent successfully. - - CLI Example: - - .. code-block:: bash - - salt '*' hipchat.send_message room_id="Development Room" message="Build is done" from_name="Build Server" - - salt '*' hipchat.send_message room_id="Development Room" message="Build failed" from_name="Build Server" color="red" notify=True - ''' - - parameters = dict() - parameters['room_id'] = room_id - parameters['from'] = from_name[:15] - parameters['message'] = message[:10000] - parameters['message_format'] = 'text' - parameters['color'] = color - parameters['notify'] = notify - - result = _query(function='message', - api_url=api_url, - api_key=api_key, - api_version=api_version, - room_id=room_id, - method='POST', - data=parameters) - - if result: - return True - else: - return False diff --git a/salt/modules/http.py b/salt/modules/http.py index d11eff2f1385..b8af38113336 100644 --- a/salt/modules/http.py +++ b/salt/modules/http.py @@ -12,10 +12,16 @@ # Import Salt libs import salt.utils.http +from salt.exceptions import CommandExecutionError + +# Import 3rd-party libs +from salt.ext import six def query(url, **kwargs): ''' + .. versionadded:: 2015.5.0 + Query a resource, and decode the return data Passes through all the parameters described in the @@ -23,6 +29,10 @@ def query(url, **kwargs): .. autofunction:: salt.utils.http.query + raise_error : True + If ``False``, and if a connection cannot be made, the error will be + suppressed and the body of the return will simply be ``None``. + CLI Example: .. code-block:: bash @@ -38,7 +48,10 @@ def query(url, **kwargs): opts.update(kwargs['opts']) del kwargs['opts'] - return salt.utils.http.query(url=url, opts=opts, **kwargs) + try: + return salt.utils.http.query(url=url, opts=opts, **kwargs) + except Exception as exc: + raise CommandExecutionError(six.text_type(exc)) def wait_for_successful_query(url, wait_for=300, **kwargs): diff --git a/salt/modules/ilo.py b/salt/modules/ilo.py index 9f1361c2b548..d9a924fd2a00 100644 --- a/salt/modules/ilo.py +++ b/salt/modules/ilo.py @@ -42,6 +42,7 @@ def __execute_cmd(name, xml): with tempfile.NamedTemporaryFile(dir=tmp_dir, prefix=name + six.text_type(os.getpid()), suffix='.xml', + mode='w', delete=False) as fh: tmpfilename = fh.name fh.write(xml) diff --git a/salt/modules/jinja.py b/salt/modules/jinja.py new file mode 100644 index 000000000000..eb23991c379e --- /dev/null +++ b/salt/modules/jinja.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +''' +Module for checking jinja maps and verifying the result of loading JSON/YAML +files + +.. versionadded:: Neon +''' +from __future__ import absolute_import, print_function, unicode_literals + +# Import Python libs +import functools +import logging +import textwrap + +# Import Salt libs +import salt.loader +import salt.template +import salt.utils.json + +log = logging.getLogger(__name__) + + +def _strip_odict(wrapped): + ''' + dump to json and load it again, replaces OrderedDicts with regular ones + ''' + @functools.wraps(wrapped) + def strip(*args): + return salt.utils.json.loads(salt.utils.json.dumps(wrapped(*args))) + return strip + + +@_strip_odict +def load_map(path, value): + ''' + Loads the map at the specified path, and returns the specified value from + that map. + + CLI Example: + + .. code-block:: bash + + # Assuming the map is loaded in your formula SLS as follows: + # + # {% from "myformula/map.jinja" import myformula with context %} + # + # the following syntax can be used to load the map and check the + # results: + salt myminion jinja.load_map myformula/map.jinja myformula + ''' + tmplstr = textwrap.dedent('''\ + {{% from "{path}" import {value} with context %}} + {{{{ {value} | tojson }}}} + '''.format(path=path, value=value)) + return salt.template.compile_template_str( + tmplstr, + salt.loader.render(__opts__, __salt__), + __opts__['renderer'], + __opts__['renderer_blacklist'], + __opts__['renderer_whitelist']) + + +@_strip_odict +def import_yaml(path): + ''' + Loads YAML data from the specified path + + CLI Example: + + .. code-block:: bash + + salt myminion jinja.import_yaml myformula/foo.yaml + ''' + tmplstr = textwrap.dedent('''\ + {{% import_yaml "{path}" as imported %}} + {{{{ imported | tojson }}}} + '''.format(path=path)) + return salt.template.compile_template_str( + tmplstr, + salt.loader.render(__opts__, __salt__), + __opts__['renderer'], + __opts__['renderer_blacklist'], + __opts__['renderer_whitelist']) + + +@_strip_odict +def import_json(path): + ''' + Loads JSON data from the specified path + + CLI Example: + + .. code-block:: bash + + salt myminion jinja.import_JSON myformula/foo.json + ''' + tmplstr = textwrap.dedent('''\ + {{% import_json "{path}" as imported %}} + {{{{ imported | tojson }}}} + '''.format(path=path)) + return salt.template.compile_template_str( + tmplstr, + salt.loader.render(__opts__, __salt__), + __opts__['renderer'], + __opts__['renderer_blacklist'], + __opts__['renderer_whitelist']) diff --git a/salt/modules/keystone.py b/salt/modules/keystone.py index 2f39da5a63b7..b929be25ec5e 100644 --- a/salt/modules/keystone.py +++ b/salt/modules/keystone.py @@ -165,7 +165,7 @@ def auth(profile=None, **connection_args): salt '*' keystone.auth ''' __utils__['versions.warn_until']( - 'Neon', + 'Sodium', ( 'The keystone module has been deprecated and will be removed in {version}. ' 'Please update to using the keystoneng module' diff --git a/salt/modules/keystore.py b/salt/modules/keystore.py new file mode 100644 index 000000000000..27b59f7d5079 --- /dev/null +++ b/salt/modules/keystore.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +''' +Module to interact with keystores +''' + +# Import Python libs +from __future__ import absolute_import, unicode_literals, print_function +import logging +from datetime import datetime +import os + +log = logging.getLogger(__name__) + +__virtualname__ = 'keystore' + +# Import third party libs +from salt.exceptions import CommandExecutionError, SaltInvocationError + +try: + import jks + import OpenSSL + has_depends = True +except ImportError: + has_depends = False + + +def __virtual__(): + ''' + Check dependencies + ''' + if has_depends is False: + msg = 'jks unavailable: {0} execution module cant be loaded '.format(__virtualname__) + return False, msg + return __virtualname__ + + +def _parse_cert(alias, public_cert, return_cert=False): + ASN1 = OpenSSL.crypto.FILETYPE_ASN1 + PEM = OpenSSL.crypto.FILETYPE_PEM + cert_data = {} + sha1 = public_cert.digest(b'sha1') + + cert_pem = OpenSSL.crypto.dump_certificate(PEM, public_cert) + raw_until = public_cert.get_notAfter() + date_until = datetime.strptime(raw_until, '%Y%m%d%H%M%SZ') + string_until = date_until.strftime("%B %d %Y") + + raw_start = public_cert.get_notBefore() + date_start = datetime.strptime(raw_start, '%Y%m%d%H%M%SZ') + string_start = date_start.strftime("%B %d %Y") + + if return_cert: + cert_data['pem'] = cert_pem + cert_data['alias'] = alias + cert_data['sha1'] = sha1 + cert_data['valid_until'] = string_until + cert_data['valid_start'] = string_start + cert_data['expired'] = date_until < datetime.now() + + return cert_data + + +def list(keystore, passphrase, alias=None, return_cert=False): + ''' + Lists certificates in a keytool managed keystore. + + + :param keystore: The path to the keystore file to query + :param passphrase: The passphrase to use to decode the keystore + :param alias: (Optional) If found, displays details on only this key + :param return_certs: (Optional) Also return certificate PEM. + + .. warning:: + + There are security implications for using return_cert to return decrypted certificates. + + CLI Example: + + .. code-block:: bash + + salt '*' keystore.list /usr/lib/jvm/java-8/jre/lib/security/cacerts changeit + salt '*' keystore.list /usr/lib/jvm/java-8/jre/lib/security/cacerts changeit debian:verisign_-_g5.pem + + ''' + ASN1 = OpenSSL.crypto.FILETYPE_ASN1 + PEM = OpenSSL.crypto.FILETYPE_PEM + decoded_certs = [] + entries = [] + + keystore = jks.KeyStore.load(keystore, passphrase) + + if alias: + # If alias is given, look it up and build expected data structure + entry_value = keystore.entries.get(alias) + if entry_value: + entries = [(alias, entry_value)] + else: + entries = keystore.entries.items() + + if entries: + for entry_alias, cert_enc in entries: + entry_data = {} + if isinstance(cert_enc, jks.PrivateKeyEntry): + cert_result = cert_enc.cert_chain[0][1] + entry_data['type'] = 'PrivateKeyEntry' + elif isinstance(cert_enc, jks.TrustedCertEntry): + cert_result = cert_enc.cert + entry_data['type'] = 'TrustedCertEntry' + else: + raise CommandExecutionError('Unsupported EntryType detected in keystore') + + # Detect if ASN1 binary, otherwise assume PEM + if '\x30' in cert_result[0]: + public_cert = OpenSSL.crypto.load_certificate(ASN1, cert_result) + else: + public_cert = OpenSSL.crypto.load_certificate(PEM, cert_result) + + entry_data.update(_parse_cert(entry_alias, public_cert, return_cert)) + decoded_certs.append(entry_data) + + return decoded_certs + + +def add(name, keystore, passphrase, certificate, private_key=None): + ''' + Adds certificates to an existing keystore or creates a new one if necesssary. + + :param name: alias for the certificate + :param keystore: The path to the keystore file to query + :param passphrase: The passphrase to use to decode the keystore + :param certificate: The PEM public certificate to add to keystore. Can be a string for file. + :param private_key: (Optional for TrustedCert) The PEM private key to add to the keystore + + CLI Example: + + .. code-block:: bash + + salt '*' keystore.add aliasname /tmp/test.store changeit /tmp/testcert.crt + salt '*' keystore.add aliasname /tmp/test.store changeit certificate="-----BEGIN CERTIFICATE-----SIb...BM=-----END CERTIFICATE-----" + salt '*' keystore.add keyname /tmp/test.store changeit /tmp/512.cert private_key=/tmp/512.key + + ''' + ASN1 = OpenSSL.crypto.FILETYPE_ASN1 + PEM = OpenSSL.crypto.FILETYPE_PEM + certs_list = [] + if os.path.isfile(keystore): + keystore_object = jks.KeyStore.load(keystore, passphrase) + for alias, loaded_cert in keystore_object.entries.items(): + certs_list.append(loaded_cert) + + try: + cert_string = __salt__['x509.get_pem_entry'](certificate) + except SaltInvocationError: + raise SaltInvocationError('Invalid certificate file or string: {0}'.format(certificate)) + + if private_key: + # Accept PEM input format, but convert to DES for loading into new keystore + key_string = __salt__['x509.get_pem_entry'](private_key) + loaded_cert = OpenSSL.crypto.load_certificate(PEM, cert_string) + loaded_key = OpenSSL.crypto.load_privatekey(PEM, key_string) + dumped_cert = OpenSSL.crypto.dump_certificate(ASN1, loaded_cert) + dumped_key = OpenSSL.crypto.dump_privatekey(ASN1, loaded_key) + + new_entry = jks.PrivateKeyEntry.new(name, [dumped_cert], dumped_key, 'rsa_raw') + else: + new_entry = jks.TrustedCertEntry.new(name, cert_string) + + certs_list.append(new_entry) + + keystore_object = jks.KeyStore.new('jks', certs_list) + keystore_object.save(keystore, passphrase) + return True + + +def remove(name, keystore, passphrase): + ''' + Removes a certificate from an existing keystore. + Returns True if remove was successful, otherwise False + + :param name: alias for the certificate + :param keystore: The path to the keystore file to query + :param passphrase: The passphrase to use to decode the keystore + + CLI Example: + + .. code-block:: bash + + salt '*' keystore.remove aliasname /tmp/test.store changeit + ''' + certs_list = [] + keystore_object = jks.KeyStore.load(keystore, passphrase) + for alias, loaded_cert in keystore_object.entries.items(): + if name not in alias: + certs_list.append(loaded_cert) + + if len(keystore_object.entries) != len(certs_list): + # Entry has been removed, save keystore updates + keystore_object = jks.KeyStore.new('jks', certs_list) + keystore_object.save(keystore, passphrase) + return True + else: + # No alias found, notify user + return False diff --git a/salt/modules/linux_lvm.py b/salt/modules/linux_lvm.py index 003d6c0b066f..0a975324afa0 100644 --- a/salt/modules/linux_lvm.py +++ b/salt/modules/linux_lvm.py @@ -64,17 +64,21 @@ def fullversion(): return ret -def pvdisplay(pvname='', real=False): +def pvdisplay(pvname='', real=False, quiet=False): ''' Return information about the physical volume(s) pvname physical device name + real dereference any symlinks and report the real device .. versionadded:: 2015.8.7 + quiet + if the physical volume is not present, do not show any error + CLI Examples: @@ -87,7 +91,8 @@ def pvdisplay(pvname='', real=False): cmd = ['pvdisplay', '-c'] if pvname: cmd.append(pvname) - cmd_ret = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd_ret = __salt__['cmd.run_all'](cmd, python_shell=False, + ignore_retcode=quiet) if cmd_ret['retcode'] != 0: return {} @@ -118,10 +123,16 @@ def pvdisplay(pvname='', real=False): return ret -def vgdisplay(vgname=''): +def vgdisplay(vgname='', quiet=False): ''' Return information about the volume group(s) + vgname + volume group name + + quiet + if the volume group is not present, do not show any error + CLI Examples: .. code-block:: bash @@ -133,7 +144,8 @@ def vgdisplay(vgname=''): cmd = ['vgdisplay', '-c'] if vgname: cmd.append(vgname) - cmd_ret = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd_ret = __salt__['cmd.run_all'](cmd, python_shell=False, + ignore_retcode=quiet) if cmd_ret['retcode'] != 0: return {} @@ -167,6 +179,12 @@ def lvdisplay(lvname='', quiet=False): ''' Return information about the logical volume(s) + lvname + logical device name + + quiet + if the logical volume is not present, do not show any error + CLI Examples: .. code-block:: bash @@ -178,10 +196,8 @@ def lvdisplay(lvname='', quiet=False): cmd = ['lvdisplay', '-c'] if lvname: cmd.append(lvname) - if quiet: - cmd_ret = __salt__['cmd.run_all'](cmd, python_shell=False, output_loglevel='quiet') - else: - cmd_ret = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd_ret = __salt__['cmd.run_all'](cmd, python_shell=False, + ignore_retcode=quiet) if cmd_ret['retcode'] != 0: return {} @@ -230,7 +246,7 @@ def pvcreate(devices, override=True, **kwargs): for device in devices: if not os.path.exists(device): raise CommandExecutionError('{0} does not exist'.format(device)) - if not pvdisplay(device): + if not pvdisplay(device, quiet=True): cmd.append(device) elif not override: raise CommandExecutionError('Device "{0}" is already an LVM physical volume.'.format(device)) @@ -295,7 +311,7 @@ def pvremove(devices, override=True): # Verify pvcremove was successful for device in devices: - if pvdisplay(device): + if pvdisplay(device, quiet=True): raise CommandExecutionError('Device "{0}" was not affected.'.format(device)) return True diff --git a/salt/modules/lxd.py b/salt/modules/lxd.py index b2bf418184bd..d6c2d8d4b91e 100644 --- a/salt/modules/lxd.py +++ b/salt/modules/lxd.py @@ -4,12 +4,12 @@ .. versionadded:: 2019.2.0 -`LXD(1)`__ is a container "hypervisor". This execution module provides +`LXD(1)`_ is a container "hypervisor". This execution module provides several functions to help manage it and its containers. -.. note: +.. note:: - - `pylxd(2)`__ version >=2.2.5 is required to let this work, + - `pylxd(2)`_ version >=2.2.5 is required to let this work, currently only available via pip. To install on Ubuntu: @@ -23,8 +23,8 @@ - for the config_get() and config_get() methods you need to have lxd-client installed. -.. __: https://linuxcontainers.org/lxd/ -.. __: https://github.com/lxc/pylxd/blob/master/doc/source/installation.rst +.. _LXD(1): https://linuxcontainers.org/lxd/ +.. _pylxd(2): https://github.com/lxc/pylxd/blob/master/doc/source/installation.rst :maintainer: René Jochum :maturity: new @@ -65,7 +65,7 @@ _pylxd_minimal_version = "2.2.5" -# Keep in sync with: https://github.com/lxc/lxd/blob/master/shared/osarch/architectures.go # noqa +# Keep in sync with: https://github.com/lxc/lxd/blob/master/shared/osarch/architectures.go _architectures = { 'unknown': '0', 'i686': '1', @@ -78,7 +78,7 @@ 's390x': '8' } -# Keep in sync with: https://github.com/lxc/lxd/blob/master/shared/api/status_code.go # noqa +# Keep in sync with: https://github.com/lxc/lxd/blob/master/shared/api/status_code.go CONTAINER_STATUS_RUNNING = 103 __virtualname__ = 'lxd' @@ -328,7 +328,6 @@ def pylxd_client_get(remote_addr=None, cert=None, key=None, verify_cert=True): .. _requests-docs: http://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification - # noqa ''' pool_key = '|'.join((six.text_type(remote_addr), @@ -462,7 +461,6 @@ def authenticate(remote_addr, password, cert, key, verify_cert=True): .. _requests-docs: http://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification - # noqa ''' client = pylxd_client_get(remote_addr, cert, key, verify_cert) @@ -528,9 +526,10 @@ def container_list(list_names=False, remote_addr=None, salt '*' lxd.container_list true - # See: https://github.com/lxc/pylxd/blob/master/doc/source/containers.rst#container-attributes + See also `container-attributes`_. + + .. _container-attributes: https://github.com/lxc/pylxd/blob/master/doc/source/containers.rst#container-attributes - # noqa ''' client = pylxd_client_get(remote_addr, cert, key, verify_cert) @@ -555,21 +554,25 @@ def container_create(name, source, profiles=None, source : Can be either a string containing an image alias: "xenial/amd64" + or an dict with type "image" with alias: {"type": "image", "alias": "xenial/amd64"} + or image with "fingerprint": {"type": "image", "fingerprint": "SHA-256"} + or image with "properties": {"type": "image", "properties": { "os": "ubuntu", "release": "14.04", - "architecture": "x86_64" - }} + "architecture": "x86_64"}} + or none: {"type": "none"} + or copy: {"type": "copy", "source": "my-old-container"} @@ -636,7 +639,10 @@ def container_create(name, source, profiles=None, salt '*' lxd.container_create test xenial/amd64 - # See: https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1 + See also the `rest-api-docs`_. + + .. _rest-api-docs: https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1 + ''' if profiles is None: profiles = ['default'] @@ -1169,8 +1175,6 @@ def container_migrate(name, # Migrate phpmyadmin from srv01 to srv02 salt '*' lxd.container_migrate phpmyadmin stop_and_start=true remote_addr=https://srv02:8443 cert=~/.config/lxc/client.crt key=~/.config/lxc/client.key verify_cert=False src_remote_addr=https://srv01:8443 - - # noqa ''' if src_cert is None: src_cert = cert @@ -2020,8 +2024,6 @@ def profile_create(name, config=None, devices=None, description=None, See the `lxd-docs`_ for the details about the config and devices dicts. .. _lxd-docs: https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-10 - - # noqa ''' client = pylxd_client_get(remote_addr, cert, key, verify_cert) @@ -2412,8 +2414,6 @@ def profile_device_set(name, device_name, device_type='disk', .. code-block:: bash $ salt '*' lxd.profile_device_set autostart eth1 nic nictype=bridged parent=lxdbr0 - - # noqa ''' profile = profile_get( name, @@ -2475,8 +2475,6 @@ def profile_device_delete(name, device_name, remote_addr=None, $ salt '*' lxd.profile_device_delete autostart eth1 - # noqa - ''' profile = profile_get( name, @@ -2776,8 +2774,6 @@ def image_from_simplestreams(server, ..code-block:: bash $ salt '*' lxd.image_from_simplestreams "https://cloud-images.ubuntu.com/releases" "trusty/amd64" aliases='["t", "trusty/amd64"]' auto_update=True - - # noqa ''' if aliases is None: aliases = [] @@ -2857,8 +2853,6 @@ def image_from_url(url, ..code-block:: bash $ salt '*' lxd.image_from_url https://dl.stgraber.org/lxd aliases='["busybox-amd64"]' - - # noqa ''' if aliases is None: aliases = [] @@ -2938,8 +2932,6 @@ def image_from_file(filename, ..code-block:: bash $ salt '*' lxd.image_from_file salt://lxd/files/busybox.tar.xz aliases=["busybox-amd64"] - - # noqa ''' if aliases is None: aliases = [] @@ -3047,8 +3039,6 @@ def image_copy_lxd(source, .. code-block:: bash $ salt '*' lxd.image_copy_lxd xenial/amd64 https://srv01:8443 ~/.config/lxc/client.crt ~/.config/lxc/client.key false https://srv02:8443 ~/.config/lxc/client.crt ~/.config/lxc/client.key false aliases="['xenial/amd64']" - - # noqa ''' if aliases is None: aliases = [] @@ -3139,8 +3129,6 @@ def image_alias_add(image, .. code-block:: bash $ salt '*' lxd.image_alias_add xenial/amd64 x "Short version of xenial/amd64" - - # noqa ''' image = _verify_image(image, remote_addr, cert, key, verify_cert) @@ -3196,8 +3184,6 @@ def image_alias_delete(image, .. code-block:: bash $ salt '*' lxd.image_alias_add xenial/amd64 x "Short version of xenial/amd64" - - # noqa ''' image = _verify_image(image, remote_addr, cert, key, verify_cert) diff --git a/salt/modules/match.py b/salt/modules/match.py index ac16e1b23cc7..139f5aca9346 100644 --- a/salt/modules/match.py +++ b/salt/modules/match.py @@ -11,7 +11,6 @@ import copy # Import salt libs -import salt.minion import salt.loader from salt.defaults import DEFAULT_TARGET_DELIM from salt.ext import six @@ -47,7 +46,7 @@ def compound(tgt, minion_id=None): opts = __opts__ matchers = salt.loader.matchers(opts) try: - return matchers['compound_match.match'](tgt, opts=opts) + return matchers['compound_match.match'](tgt) except Exception as exc: log.exception(exc) return False diff --git a/salt/modules/mdadm_raid.py b/salt/modules/mdadm_raid.py index 829f4cdd245e..1581a558b66e 100644 --- a/salt/modules/mdadm_raid.py +++ b/salt/modules/mdadm_raid.py @@ -247,7 +247,7 @@ def create(name, '-v', '-l', six.text_type(level), ] + opts + [ - '-e', metadata, + '-e', six.text_type(metadata), '-n', six.text_type(raid_devices), ] + devices @@ -360,17 +360,25 @@ def assemble(name, return __salt__['cmd.run'](cmd, python_shell=False) -def examine(device): +def examine(device, quiet=False): ''' Show detail for a specified RAID component device + device + Device to examine, that is part of the RAID + + quiet + If the device is not part of the RAID, do not show any error + CLI Example: .. code-block:: bash salt '*' raid.examine '/dev/sda1' ''' - res = __salt__['cmd.run_stdout']('mdadm -Y -E {0}'.format(device), output_loglevel='trace', python_shell=False) + res = __salt__['cmd.run_stdout']('mdadm -Y -E {0}'.format(device), + python_shell=False, + ignore_retcode=quiet) ret = {} for line in res.splitlines(): diff --git a/salt/modules/mine.py b/salt/modules/mine.py index dc63a3984a35..8d78087cfbd2 100644 --- a/salt/modules/mine.py +++ b/salt/modules/mine.py @@ -75,12 +75,8 @@ def _mine_get(load, opts): 'Mine could not be retrieved.' ) return False - channel = salt.transport.client.ReqChannel.factory(opts) - try: - ret = channel.send(load) - finally: - channel.close() - return ret + with salt.transport.client.ReqChannel.factory(opts) as channel: + return channel.send(load) def update(clear=False, mine_functions=None): diff --git a/salt/modules/mongodb.py b/salt/modules/mongodb.py index bf330eb75d0c..61e22da4ceb8 100644 --- a/salt/modules/mongodb.py +++ b/salt/modules/mongodb.py @@ -18,6 +18,7 @@ # Import python libs import logging import re +import sys # Import salt libs import salt.utils.json @@ -83,7 +84,7 @@ def _to_dict(objects): objects = salt.utils.json.loads(objects) except ValueError as err: log.error("Could not parse objects: %s", err) - raise err + six.reraise(*sys.exc_info()) return objects diff --git a/salt/modules/mount.py b/salt/modules/mount.py index 97263252c758..541a2c9cc6ac 100644 --- a/salt/modules/mount.py +++ b/salt/modules/mount.py @@ -1218,6 +1218,8 @@ def mount(name, device, mkmnt=False, fstype='', opts='defaults', user=None, util # fstype in /etc/filesystems if 'AIX' in __grains__['os']: args += ' -v {0}'.format(fstype) + elif 'solaris' in __grains__['os'].lower(): + args += ' -F {0}'.format(fstype) else: args += ' -t {0}'.format(fstype) @@ -1248,7 +1250,7 @@ def remount(name, device, mkmnt=False, fstype='', opts='defaults', user=None): if 'AIX' in __grains__['os']: if opts == 'defaults': - opts = '' + opts = [] if isinstance(opts, six.string_types): opts = opts.split(',') @@ -1263,14 +1265,16 @@ def remount(name, device, mkmnt=False, fstype='', opts='defaults', user=None): lopts = ','.join(opts) args = '-o {0}'.format(lopts) - # use of fstype on AIX differs from typical Linux use of -t functionality - # AIX uses -v vfsname, -t fstype mounts all with fstype in /etc/filesystems - if 'AIX' in __grains__['os']: - if fstype: + if fstype: + # use of fstype on AIX differs from typical Linux use of + # -t functionality AIX uses -v vfsname, -t fstype mounts + # all with fstype in /etc/filesystems + if 'AIX' in __grains__['os']: args += ' -v {0}'.format(fstype) - args += ' -o remount' - else: - args += ' -t {0}'.format(fstype) + elif 'solaris' in __grains__['os'].lower(): + args += ' -F {0}'.format(fstype) + else: + args += ' -t {0}'.format(fstype) if __grains__['os'] not in ['OpenBSD', 'MacOS', 'Darwin'] or force_mount: cmd = 'mount {0} {1} {2} '.format(args, device, name) diff --git a/salt/modules/mssql.py b/salt/modules/mssql.py index ef1f2a5289be..6da237659b14 100644 --- a/salt/modules/mssql.py +++ b/salt/modules/mssql.py @@ -23,9 +23,10 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from json import JSONEncoder, loads +# Import Salt libs import salt.ext.six as six +import salt.utils.json try: @@ -64,7 +65,7 @@ def _get_connection(**kwargs): return pymssql.connect(**connection_args) -class _MssqlEncoder(JSONEncoder): +class _MssqlEncoder(salt.utils.json.JSONEncoder): # E0202: 68:_MssqlEncoder.default: An attribute inherited from JSONEncoder hide this method def default(self, o): # pylint: disable=E0202 return six.text_type(o) @@ -84,7 +85,7 @@ def tsql_query(query, **kwargs): cur = _get_connection(**kwargs).cursor() cur.execute(query) # Making sure the result is JSON serializable - return loads(_MssqlEncoder().encode({'resultset': cur.fetchall()}))['resultset'] + return salt.utils.json.loads(_MssqlEncoder().encode({'resultset': cur.fetchall()}))['resultset'] except Exception as err: # Trying to look like the output of cur.fetchall() return (('Could not run the query', ), (six.text_type(err), )) diff --git a/salt/modules/mysql.py b/salt/modules/mysql.py index 94f4b4acce1a..a87368a29c3d 100644 --- a/salt/modules/mysql.py +++ b/salt/modules/mysql.py @@ -1435,7 +1435,10 @@ def user_create(user, args['password'] = six.text_type(password) elif password_hash is not None: if salt.utils.versions.version_cmp(server_version, compare_version) >= 0: - qry += ' IDENTIFIED BY %(password)s' + if 'MariaDB' in server_version: + qry += ' IDENTIFIED BY PASSWORD %(password)s' + else: + qry += ' IDENTIFIED BY %(password)s' else: qry += ' IDENTIFIED BY PASSWORD %(password)s' args['password'] = password_hash @@ -1552,7 +1555,10 @@ def user_chpass(user, args['user'] = user args['host'] = host if salt.utils.versions.version_cmp(server_version, compare_version) >= 0: - qry = "ALTER USER %(user)s@%(host)s IDENTIFIED BY %(password)s;" + if 'MariaDB' in server_version and password_hash is not None: + qry = "ALTER USER %(user)s@%(host)s IDENTIFIED BY PASSWORD %(password)s;" + else: + qry = "ALTER USER %(user)s@%(host)s IDENTIFIED BY %(password)s;" else: qry = ('UPDATE mysql.user SET ' + password_column + '=' + password_sql + ' WHERE User=%(user)s AND Host = %(host)s;') diff --git a/salt/modules/napalm_bgp.py b/salt/modules/napalm_bgp.py index 89bf03e29294..d0964ea4402e 100644 --- a/salt/modules/napalm_bgp.py +++ b/salt/modules/napalm_bgp.py @@ -5,7 +5,7 @@ Manages BGP configuration on network devices and provides statistics. -:codeauthor: Mircea Ulinic & Jerome Fleury +:codeauthor: Mircea Ulinic & Jerome Fleury :maturity: new :depends: napalm :platform: unix diff --git a/salt/modules/napalm_netacl.py b/salt/modules/napalm_netacl.py index 8d0c2026586d..d70d360d73d3 100644 --- a/salt/modules/napalm_netacl.py +++ b/salt/modules/napalm_netacl.py @@ -7,7 +7,7 @@ .. versionadded:: 2017.7.0 -:codeauthor: Mircea Ulinic +:codeauthor: Mircea Ulinic :maturity: new :depends: capirca, napalm :platform: unix diff --git a/salt/modules/napalm_network.py b/salt/modules/napalm_network.py index 42ff688d2784..1a617d406f65 100644 --- a/salt/modules/napalm_network.py +++ b/salt/modules/napalm_network.py @@ -5,7 +5,7 @@ Basic methods for interaction with the network device through the virtual proxy 'napalm'. -:codeauthor: Mircea Ulinic & Jerome Fleury +:codeauthor: Mircea Ulinic & Jerome Fleury :maturity: new :depends: napalm :platform: unix diff --git a/salt/modules/napalm_ntp.py b/salt/modules/napalm_ntp.py index 3e00865e8429..98d3cb66f322 100644 --- a/salt/modules/napalm_ntp.py +++ b/salt/modules/napalm_ntp.py @@ -5,7 +5,7 @@ Manages NTP on network devices. -:codeauthor: Mircea Ulinic & Jerome Fleury +:codeauthor: Mircea Ulinic & Jerome Fleury :maturity: new :depends: napalm :platform: unix diff --git a/salt/modules/napalm_probes.py b/salt/modules/napalm_probes.py index 5198ab94f390..0578a2450603 100644 --- a/salt/modules/napalm_probes.py +++ b/salt/modules/napalm_probes.py @@ -5,7 +5,7 @@ Manages RPM/SLA probes on the network device. -:codeauthor: Mircea Ulinic & Jerome Fleury +:codeauthor: Mircea Ulinic & Jerome Fleury :maturity: new :depends: napalm :platform: unix diff --git a/salt/modules/napalm_route.py b/salt/modules/napalm_route.py index 9054e26f276d..a79805d04b3f 100644 --- a/salt/modules/napalm_route.py +++ b/salt/modules/napalm_route.py @@ -5,7 +5,7 @@ Retrieves route details from network devices. -:codeauthor: Mircea Ulinic +:codeauthor: Mircea Ulinic :maturity: new :depends: napalm :platform: unix diff --git a/salt/modules/napalm_snmp.py b/salt/modules/napalm_snmp.py index 8b3a76ecd37e..d7dd5935af64 100644 --- a/salt/modules/napalm_snmp.py +++ b/salt/modules/napalm_snmp.py @@ -5,7 +5,7 @@ Manages SNMP on network devices. -:codeauthor: Mircea Ulinic +:codeauthor: Mircea Ulinic :maturity: new :depends: napalm :platform: unix diff --git a/salt/modules/napalm_users.py b/salt/modules/napalm_users.py index 99fd7bd6f557..69296adb9786 100644 --- a/salt/modules/napalm_users.py +++ b/salt/modules/napalm_users.py @@ -5,7 +5,7 @@ Manages the configuration of the users on network devices. -:codeauthor: Mircea Ulinic +:codeauthor: Mircea Ulinic :maturity: new :depends: napalm :platform: unix diff --git a/salt/modules/npm.py b/salt/modules/npm.py index 50e88588841c..aaa3325191b1 100644 --- a/salt/modules/npm.py +++ b/salt/modules/npm.py @@ -49,10 +49,13 @@ def _check_valid_version(): Check the version of npm to ensure this module will work. Currently npm must be at least version 1.2. ''' + + # Locate the full path to npm + npm_path = salt.utils.path.which('npm') + # pylint: disable=no-member - npm_version = _LooseVersion( - salt.modules.cmdmod.run('npm --version', output_loglevel='quiet')) - valid_version = _LooseVersion('1.2') + res = salt.modules.cmdmod.run('{npm} --version'.format(npm=npm_path), output_loglevel='quiet') + npm_version, valid_version = _LooseVersion(res), _LooseVersion('1.2') # pylint: enable=no-member if npm_version < valid_version: raise CommandExecutionError( diff --git a/salt/modules/opkg.py b/salt/modules/opkg.py index eff54134858a..0aebc8fc183d 100644 --- a/salt/modules/opkg.py +++ b/salt/modules/opkg.py @@ -269,6 +269,14 @@ def refresh_db(failhard=False, **kwargs): # pylint: disable=unused-argument return ret +def _append_noaction_if_testmode(cmd, **kwargs): + ''' + Adds the --noaction flag to the command if it's running in the test mode. + ''' + if bool(kwargs.get('test') or __opts__.get('test')): + cmd.append('--noaction') + + def install(name=None, refresh=False, pkgs=None, @@ -366,6 +374,7 @@ def install(name=None, to_reinstall = [] to_downgrade = [] + _append_noaction_if_testmode(cmd_prefix, **kwargs) if pkg_params is None or len(pkg_params) == 0: return {} elif pkg_type == 'file': @@ -540,6 +549,7 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument if not targets: return {} cmd = ['opkg', 'remove'] + _append_noaction_if_testmode(cmd, **kwargs) if kwargs.get('remove_dependencies', False): cmd.append('--force-removal-of-dependent-packages') if kwargs.get('auto_remove_deps', False): diff --git a/salt/modules/pip.py b/salt/modules/pip.py index eac40c719cfa..9974f3aec99e 100644 --- a/salt/modules/pip.py +++ b/salt/modules/pip.py @@ -431,8 +431,10 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914 use_vt=False, trusted_host=None, no_cache_dir=False, + extra_args=None, cache_dir=None, no_binary=None, + disable_version_check=False, **kwargs): ''' Install packages with pip @@ -479,6 +481,11 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914 behind an authenticated proxy. If you provide ``user@proxy.server:port`` then you will be prompted for a password. + .. note:: + If the the Minion has a globaly configured proxy - it will be used + even if no proxy was set here. To explicitly disable proxy for pip + you should pass ``False`` as a value. + timeout Set the socket timeout (default 15 seconds) @@ -604,6 +611,29 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914 no_cache_dir Disable the cache. + extra_args + pip keyword and positional arguments not yet implemented in salt + + .. code-block:: yaml + + salt '*' pip.install pandas extra_args="[{'--latest-pip-kwarg':'param'}, '--latest-pip-arg']" + + .. warning:: + + If unsupported options are passed here that are not supported in a + minion's version of pip, a `No such option error` will be thrown. + + Will be translated into the following pip command: + + .. code-block:: bash + + pip install pandas --latest-pip-kwarg param --latest-pip-arg + + disable_version_check + Pip may periodically check PyPI to determine whether a new version of + pip is available to download. Passing True for this option disables + that check. + CLI Example: .. code-block:: bash @@ -686,8 +716,16 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914 cmd.extend(['--log', log]) + config = __opts__ if proxy: cmd.extend(['--proxy', proxy]) + # If proxy arg is set to False we won't use the global proxy even if it's set. + elif proxy is not False and config.get('proxy_host') and config.get('proxy_port'): + if config.get('proxy_username') and config.get('proxy_password'): + http_proxy_url = 'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}'.format(**config) + else: + http_proxy_url = 'http://{proxy_host}:{proxy_port}'.format(**config) + cmd.extend(['--proxy', http_proxy_url]) if timeout: try: @@ -756,6 +794,9 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914 ) cmd.extend(['--mirrors', mirror]) + if disable_version_check: + cmd.extend(['--disable-pip-version-check']) + if build: cmd.extend(['--build', build]) @@ -887,6 +928,24 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914 if trusted_host: cmd.extend(['--trusted-host', trusted_host]) + if extra_args: + # These are arguments from the latest version of pip that + # have not yet been implemented in salt + for arg in extra_args: + # It is a keyword argument + if isinstance(arg, dict): + # There will only ever be one item in this dictionary + key, val = arg.popitem() + # Don't allow any recursion into keyword arg definitions + # Don't allow multiple definitions of a keyword + if isinstance(val, (dict, list)): + raise TypeError("Too many levels in: {}".format(key)) + # This is a a normal one-to-one keyword argument + cmd.extend([key, val]) + # It is a positional argument, append it to the list + else: + cmd.append(arg) + cmd_kwargs = dict(saltenv=saltenv, use_vt=use_vt, runas=user) if kwargs: @@ -949,6 +1008,11 @@ def uninstall(pkgs=None, behind an authenticated proxy. If you provide ``user@proxy.server:port`` then you will be prompted for a password. + .. note:: + If the the Minion has a globaly configured proxy - it will be used + even if no proxy was set here. To explicitly disable proxy for pip + you should pass ``False`` as a value. + timeout Set the socket timeout (default 15 seconds) @@ -990,8 +1054,16 @@ def uninstall(pkgs=None, cmd.extend(['--log', log]) + config = __opts__ if proxy: cmd.extend(['--proxy', proxy]) + # If proxy arg is set to False we won't use the global proxy even if it's set. + elif proxy is not False and config.get('proxy_host') and config.get('proxy_port'): + if config.get('proxy_username') and config.get('proxy_password'): + http_proxy_url = 'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}'.format(**config) + else: + http_proxy_url = 'http://{proxy_host}:{proxy_port}'.format(**config) + cmd.extend(['--proxy', http_proxy_url]) if timeout: try: diff --git a/salt/modules/publish.py b/salt/modules/publish.py index 62e3e98f2f59..1550aa39a8b6 100644 --- a/salt/modules/publish.py +++ b/salt/modules/publish.py @@ -120,8 +120,7 @@ def _publish( 'id': __opts__['id'], 'no_parse': __opts__.get('no_parse', [])} - channel = salt.transport.client.ReqChannel.factory(__opts__, master_uri=master_uri) - try: + with salt.transport.client.ReqChannel.factory(__opts__, master_uri=master_uri) as channel: try: peer_data = channel.send(load) except SaltReqTimeoutError: @@ -175,8 +174,6 @@ def _publish( return cret else: return ret - finally: - channel.close() def publish(tgt, @@ -326,10 +323,8 @@ def runner(fun, arg=None, timeout=5): 'id': __opts__['id'], 'no_parse': __opts__.get('no_parse', [])} - channel = salt.transport.client.ReqChannel.factory(__opts__) - try: - return channel.send(load) - except SaltReqTimeoutError: - return '\'{0}\' runner publish timed out'.format(fun) - finally: - channel.close() + with salt.transport.client.ReqChannel.factory(__opts__) as channel: + try: + return channel.send(load) + except SaltReqTimeoutError: + return '\'{0}\' runner publish timed out'.format(fun) diff --git a/salt/modules/purefb.py b/salt/modules/purefb.py index 16a0ec5ca3ce..65fde5eb1b7e 100644 --- a/salt/modules/purefb.py +++ b/salt/modules/purefb.py @@ -44,7 +44,7 @@ :maintainer: Simon Dodsley (simon@purestorage.com) :maturity: new -:requires: purestorage +:requires: purity_fb :platform: all .. versionadded:: 2019.2.0 diff --git a/salt/modules/restartcheck.py b/salt/modules/restartcheck.py index 12812589d77b..426a90e9701e 100644 --- a/salt/modules/restartcheck.py +++ b/salt/modules/restartcheck.py @@ -552,7 +552,7 @@ def restartcheck(ignorelist=None, blacklist=None, excludepid=None, **kwargs): while True: _check_timeout(start_time, timeout) - line = paths.stdout.readline() + line = salt.utils.stringutils.to_unicode(paths.stdout.readline()) if not line: break pth = line[:-1] diff --git a/salt/modules/rh_ip.py b/salt/modules/rh_ip.py index 800a9d573876..1f09d0f7905b 100644 --- a/salt/modules/rh_ip.py +++ b/salt/modules/rh_ip.py @@ -81,7 +81,7 @@ def _error_msg_iface(iface, option, expected): a list of expected values. ''' msg = 'Invalid option -- Interface: {0}, Option: {1}, Expected: [{2}]' - return msg.format(iface, option, '|'.join(expected)) + return msg.format(iface, option, '|'.join(str(e) for e in expected)) def _error_msg_routes(iface, option, expected): @@ -104,7 +104,7 @@ def _error_msg_network(option, expected): a list of expected values. ''' msg = 'Invalid network setting -- Setting: {0}, Expected: [{1}]' - return msg.format(option, '|'.join(expected)) + return msg.format(option, '|'.join(str(e) for e in expected)) def _log_default_network(opt, value): diff --git a/salt/modules/salt_proxy.py b/salt/modules/salt_proxy.py index 9815e513dab4..13bcd2615505 100644 --- a/salt/modules/salt_proxy.py +++ b/salt/modules/salt_proxy.py @@ -14,6 +14,7 @@ # Import Salt libs import salt.utils.files +import salt.syspaths # Import 3rd-party libs import salt.ext.six.moves @@ -33,7 +34,7 @@ def _write_proxy_conf(proxyfile): if proxyfile: log.debug('Writing proxy conf file') with salt.utils.files.fopen(proxyfile, 'w') as proxy_conf: - proxy_conf.write(salt.utils.stringutils.to_str('master = {0}' + proxy_conf.write(salt.utils.stringutils.to_str('master: {0}' .format(__grains__['master']))) msg = 'Wrote proxy file {0}'.format(proxyfile) log.debug(msg) @@ -132,7 +133,7 @@ def configure_proxy(proxyname, start=True): test = __opts__['test'] # write the proxy file if necessary - proxyfile = '/etc/salt/proxy' + proxyfile = os.path.join(salt.syspaths.CONFIG_DIR, 'proxy') status_file, msg_new, msg_old = _proxy_conf_file(proxyfile, test) changes_new.extend(msg_new) changes_old.extend(msg_old) diff --git a/salt/modules/salt_version.py b/salt/modules/salt_version.py new file mode 100644 index 000000000000..f694209ec227 --- /dev/null +++ b/salt/modules/salt_version.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +''' +Access Salt's elemental release code-names. + +.. versionadded:: Neon + +Salt's feature release schedule is based on the Periodic Table, as described +in the :ref:`Version Numbers ` documentation. + +When a feature was added (or removed) in a specific release, it can be +difficult to build out future-proof functionality that is dependent on +a naming scheme that moves. + +For example, a state syntax needs to change to support an option that will be +removed in the future, but there are many Minion versions in use across an +infrastructure. It would be handy to use some Jinja syntax to check for these +instances to perform one state syntax over another. + +A simple example might be something like the following: + +.. code-block:: jinja + + {# a boolean check #} + {% set option_deprecated = salt['salt_version.less_than']("Sodium") %} + + {% if option_deprecated %} + + {% else %} + + {% endif %} + +''' + +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals +import logging + +# Import Salt libs +from salt.ext import six +import salt.version +import salt.utils.versions + + +log = logging.getLogger(__name__) + +__virtualname__ = 'salt_version' + + +def __virtual__(): + ''' + Only work on POSIX-like systems + ''' + return __virtualname__ + + +def get_release_number(name): + ''' + Returns the release number of a given release code name in a + ``MAJOR.PATCH`` format. + + If the release name has not been given an assigned release number, the + function returns a string. If the release cannot be found, it returns + ``None``. + + name + The release code name for which to find a release number. + + CLI Example: + + .. code-block:: bash + + salt '*' salt_version.get_release_number 'Oxygen' + ''' + name = name.lower() + version_map = salt.version.SaltStackVersion.LNAMES + version = version_map.get(name) + if version is None: + log.info('Version {} not found.'.format(name)) + return None + + if version[1] == 0: + log.info('Version {} found, but no release number has been assigned ' + 'yet.'.format(name)) + return 'No version assigned.' + + return '.'.join(str(item) for item in version) + + +def equal(name): + ''' + Returns a boolean (True) if the minion's current version + code name matches the named version. + + name + The release code name to check the version against. + + CLI Example: + + .. code-block:: bash + + salt '*' salt_version.equal 'Oxygen' + ''' + if _check_release_cmp(name) == 0: + log.info( + 'The minion\'s version code name matches \'{}\'.'.format(name) + ) + return True + + return False + + +def greater_than(name): + ''' + Returns a boolean (True) if the minion's current + version code name is greater than the named version. + + name + The release code name to check the version against. + + CLI Example: + + .. code-block:: bash + + salt '*' salt_version.greater_than 'Sodium' + ''' + if _check_release_cmp(name) == 1: + log.info( + 'The minion\'s version code name is greater than \'{}\'.'.format(name) + ) + return True + + return False + + +def less_than(name): + ''' + Returns a boolean (True) if the minion's current + version code name is less than the named version. + + name + The release code name to check the version against. + + CLI Example: + + .. code-block:: bash + + salt '*' salt_version.less_than 'Sodium' + ''' + if _check_release_cmp(name) == -1: + log.info( + 'The minion\'s version code name is less than \'{}\'.'.format(name) + ) + return True + + return False + + +def _check_release_cmp(name): + ''' + Helper function to compare the minion's current + Salt version to release code name versions. + + If release code name isn't found, the function returns None. Otherwise, it + returns the results of the version comparison as documented by the + ``versions_cmp`` function in ``salt.utils.versions.py``. + ''' + map_version = get_release_number(name) + if map_version is None: + log.info('Release code name {} was not found.'.format(name)) + return None + + current_version = six.text_type(salt.version.SaltStackVersion( + *salt.version.__version_info__)) + current_version = current_version.rsplit('.', 1)[0] + version_cmp = salt.utils.versions.version_cmp(current_version, map_version) + return version_cmp diff --git a/salt/modules/saltcheck.py b/salt/modules/saltcheck.py index e4801d6a4e91..989a535ac1b5 100644 --- a/salt/modules/saltcheck.py +++ b/salt/modules/saltcheck.py @@ -42,6 +42,101 @@ assertion: assertEqual expected-return: 'hello' +Example with jinja +------------------ + +.. code-block:: jinja + + {% for package in ["apache2", "openssh"] %} + {# or another example #} + {# for package in salt['pillar.get']("packages") #} + test_{{ package }}_latest: + module_and_function: pkg.upgrade_available + args: + - {{ package }} + assertion: assertFalse + {% endfor %} + +Example with setup state including pillar +----------------------------------------- + +.. code-block:: yaml + + setup_test_environment: + module_and_function: saltcheck.state_apply + args: + - common + pillar-data: + data: value + + verify_vim: + module_and_function: pkg.version + args: + - vim + assertion: assertNotEmpty + +Example with skip +----------------- + +.. code-block:: yaml + + package_latest: + module_and_function: pkg.upgrade_available + args: + - apache2 + assertion: assertFalse + skip: True + +Example with assertion_section +------------------------------ + +.. code-block:: yaml + + validate_shell: + module_and_function: user.info + args: + - root + assertion: assertEqual + expected-return: /bin/bash + assertion_section: shell + +Example suppressing print results +--------------------------------- + +.. code-block:: yaml + + validate_env_nameNode: + module_and_function: hadoop.dfs + args: + - text + - /oozie/common/env.properties + expected-return: nameNode = hdfs://nameservice2 + assertion: assertNotIn + print_result: False + +Supported assertions +==================== + +* assertEqual +* assertNotEqual +* assertTrue +* assertFalse +* assertIn +* assertNotIn +* assertGreater +* assertGreaterEqual +* assertLess +* assertLessEqual +* assertEmpty +* assertNotEmpty + +.. warning:: + + The saltcheck.state_apply function is an alias for + :py:func:`state.apply `. If using the + :ref:`ACL system ` ``saltcheck.*`` might provide more capability + than intended if only ``saltcheck.run_state_tests`` and + ``saltcheck.run_highstate_tests`` are needed. ''' # Import Python libs @@ -49,10 +144,10 @@ import logging import os import time -from json import loads, dumps # Import Salt libs import salt.utils.files +import salt.utils.json import salt.utils.path import salt.utils.yaml import salt.client @@ -604,7 +699,7 @@ def load_file_salt_rendered(self, filepath): # use the salt renderer module to interpret jinja and etc tests = _render_file(filepath) # use json as a convenient way to convert the OrderedDicts from salt renderer - mydict = loads(dumps(tests)) + mydict = salt.utils.json.loads(salt.utils.json.dumps(tests)) for key, value in mydict.items(): self.test_dict[key] = value return diff --git a/salt/modules/saltutil.py b/salt/modules/saltutil.py index fe1c0f8fd257..a82cfe8b69e7 100644 --- a/salt/modules/saltutil.py +++ b/salt/modules/saltutil.py @@ -383,9 +383,11 @@ def refresh_grains(**kwargs): # Modules and pillar need to be refreshed in case grains changes affected # them, and the module refresh process reloads the grains and assigns the # newly-reloaded grains to each execution module's __grains__ dunder. - refresh_modules() if _refresh_pillar: + # we don't need to call refresh_modules here because it's done by refresh_pillar refresh_pillar() + else: + refresh_modules() return True @@ -425,7 +427,7 @@ def sync_grains(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blackl ''' ret = _sync('grains', saltenv, extmod_whitelist, extmod_blacklist) if refresh: - refresh_modules() + # we don't need to call refresh_modules here because it's done by refresh_pillar refresh_pillar() return ret @@ -913,11 +915,50 @@ def sync_pillar(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blackl ) ret = _sync('pillar', saltenv, extmod_whitelist, extmod_blacklist) if refresh: - refresh_modules() + # we don't need to call refresh_modules here because it's done by refresh_pillar refresh_pillar() return ret +def sync_executors(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): + ''' + .. versionadded:: Neon + + Sync executors from ``salt://_executors`` to the minion + + saltenv + The fileserver environment from which to sync. To sync from more than + one environment, pass a comma-separated list. + + If not passed, then all environments configured in the :ref:`top files + ` will be checked for log handlers to sync. If no top files + are found, then the ``base`` environment will be synced. + + refresh : True + If ``True``, refresh the available execution modules on the minion. + This refresh will be performed even if no new log handlers are synced. + Set to ``False`` to prevent this refresh. + + extmod_whitelist : None + comma-seperated list of modules to sync + + extmod_blacklist : None + comma-seperated list of modules to blacklist based on type + + CLI Examples: + + .. code-block:: bash + + salt '*' saltutil.sync_executors + salt '*' saltutil.sync_executors saltenv=dev + salt '*' saltutil.sync_executors saltenv=base,dev + ''' + ret = _sync('executors', saltenv, extmod_whitelist, extmod_blacklist) + if refresh: + refresh_modules() + return ret + + def sync_all(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): ''' .. versionchanged:: 2015.8.11,2016.3.2 @@ -978,6 +1019,7 @@ def sync_all(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist ret['output'] = sync_output(saltenv, False, extmod_whitelist, extmod_blacklist) ret['utils'] = sync_utils(saltenv, False, extmod_whitelist, extmod_blacklist) ret['log_handlers'] = sync_log_handlers(saltenv, False, extmod_whitelist, extmod_blacklist) + ret['executors'] = sync_executors(saltenv, False, extmod_whitelist, extmod_blacklist) ret['proxymodules'] = sync_proxymodules(saltenv, False, extmod_whitelist, extmod_blacklist) ret['engines'] = sync_engines(saltenv, False, extmod_whitelist, extmod_blacklist) ret['thorium'] = sync_thorium(saltenv, False, extmod_whitelist, extmod_blacklist) @@ -986,7 +1028,7 @@ def sync_all(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist if __opts__['file_client'] == 'local': ret['pillar'] = sync_pillar(saltenv, False, extmod_whitelist, extmod_blacklist) if refresh: - refresh_modules() + # we don't need to call refresh_modules here because it's done by refresh_pillar refresh_pillar() return ret @@ -1027,10 +1069,16 @@ def refresh_matchers(): return ret -def refresh_pillar(): +def refresh_pillar(wait=False, timeout=30): ''' Signal the minion to refresh the pillar data. + :param wait: Wait for pillar refresh to complete, defaults to False. + :type wait: bool, optional + :param timeout: How long to wait in seconds, only used when wait is True, defaults to 30. + :type timeout: int, optional + :return: Boolean status, True when the pillar_refresh event was fired successfully. + CLI Example: .. code-block:: bash @@ -1042,6 +1090,13 @@ def refresh_pillar(): except KeyError: log.error('Event module not available. Module refresh failed.') ret = False # Effectively a no-op, since we can't really return without an event system + if wait: + eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) + event_ret = eventer.get_event( + tag='/salt/minion/minion_pillar_refresh_complete', + wait=timeout) + if not event_ret or event_ret['complete'] is False: + log.warn("Pillar refresh did not complete within timeout %s", timeout) return ret @@ -1068,12 +1123,12 @@ def refresh_modules(**kwargs): # If we're going to block, first setup a listener ret = __salt__['event.fire']({}, 'module_refresh') else: - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) - ret = __salt__['event.fire']({'notify': True}, 'module_refresh') - # Wait for the finish event to fire - log.trace('refresh_modules waiting for module refresh to complete') - # Blocks until we hear this event or until the timeout expires - eventer.get_event(tag='/salt/minion/minion_mod_complete', wait=30) + with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: + ret = __salt__['event.fire']({'notify': True}, 'module_refresh') + # Wait for the finish event to fire + log.trace('refresh_modules waiting for module refresh to complete') + # Blocks until we hear this event or until the timeout expires + event_bus.get_event(tag='/salt/minion/minion_mod_complete', wait=30) except KeyError: log.error('Event module not available. Module refresh failed.') ret = False # Effectively a no-op, since we can't really return without an event system @@ -1376,8 +1431,8 @@ def regen_keys(): pass # TODO: move this into a channel function? Or auth? # create a channel again, this will force the key regen - channel = salt.transport.client.ReqChannel.factory(__opts__) - channel.close() + with salt.transport.client.ReqChannel.factory(__opts__) as channel: + log.debug('Recreating channel to force key regen') def revoke_auth(preserve_minion_cache=False): @@ -1404,18 +1459,16 @@ def revoke_auth(preserve_minion_cache=False): masters.append(__opts__['master_uri']) for master in masters: - channel = salt.transport.client.ReqChannel.factory(__opts__, master_uri=master) - tok = channel.auth.gen_token(b'salt') - load = {'cmd': 'revoke_auth', - 'id': __opts__['id'], - 'tok': tok, - 'preserve_minion_cache': preserve_minion_cache} - try: - channel.send(load) - except SaltReqTimeoutError: - ret = False - finally: - channel.close() + with salt.transport.client.ReqChannel.factory(__opts__, master_uri=master) as channel: + tok = channel.auth.gen_token(b'salt') + load = {'cmd': 'revoke_auth', + 'id': __opts__['id'], + 'tok': tok, + 'preserve_minion_cache': preserve_minion_cache} + try: + channel.send(load) + except SaltReqTimeoutError: + ret = False return ret diff --git a/salt/modules/schedule.py b/salt/modules/schedule.py index 464097cd5a78..a0d813cada7f 100644 --- a/salt/modules/schedule.py +++ b/salt/modules/schedule.py @@ -95,13 +95,13 @@ def list_(show_all=False, schedule = {} try: - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire']({'func': 'list', - 'where': where}, 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_list_complete', wait=30) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire']({'func': 'list', + 'where': where}, 'manage_schedule') + if res: + event_ret = event_bus.get_event(tag='/salt/minion/minion_schedule_list_complete', wait=30) + if event_ret and event_ret['complete']: + schedule = event_ret['schedule'] except KeyError: # Effectively a no-op, since we can't really return without an event system ret = {} @@ -209,20 +209,20 @@ def purge(**kwargs): persist = kwargs['persist'] try: - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire']({'name': name, - 'func': 'delete', - 'persist': persist}, 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_delete_complete', wait=30) - if event_ret and event_ret['complete']: - _schedule_ret = event_ret['schedule'] - if name not in _schedule_ret: - ret['result'] = True - ret['comment'].append('Deleted job: {0} from schedule.'.format(name)) - else: - ret['comment'].append('Failed to delete job {0} from schedule.'.format(name)) - ret['result'] = True + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire']({'name': name, + 'func': 'delete', + 'persist': persist}, 'manage_schedule') + if res: + event_ret = event_bus.get_event(tag='/salt/minion/minion_schedule_delete_complete', wait=30) + if event_ret and event_ret['complete']: + _schedule_ret = event_ret['schedule'] + if name not in _schedule_ret: + ret['result'] = True + ret['comment'].append('Deleted job: {0} from schedule.'.format(name)) + else: + ret['comment'].append('Failed to delete job {0} from schedule.'.format(name)) + ret['result'] = True except KeyError: # Effectively a no-op, since we can't really return without an event system @@ -265,18 +265,21 @@ def delete(name, **kwargs): return ret try: - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire'](event_data, 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_delete_complete', wait=30) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] - if name not in schedule: - ret['result'] = True - ret['comment'] = 'Deleted Job {0} from schedule.'.format(name) - else: - ret['comment'] = 'Failed to delete job {0} from schedule.'.format(name) - return ret + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire'](event_data, 'manage_schedule') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_schedule_delete_complete', + wait=30, + ) + if event_ret and event_ret['complete']: + schedule = event_ret['schedule'] + if name not in schedule: + ret['result'] = True + ret['comment'] = 'Deleted Job {0} from schedule.'.format(name) + else: + ret['comment'] = 'Failed to delete job {0} from schedule.'.format(name) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Schedule add failed.' @@ -449,19 +452,22 @@ def add(name, **kwargs): ret['result'] = True else: try: - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire']({'name': name, - 'schedule': schedule_data, - 'func': 'add', - 'persist': persist}, 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_add_complete', wait=30) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] - if name in schedule: - ret['result'] = True - ret['comment'] = 'Added job: {0} to schedule.'.format(name) - return ret + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire']({'name': name, + 'schedule': schedule_data, + 'func': 'add', + 'persist': persist}, 'manage_schedule') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_schedule_add_complete', + wait=30, + ) + if event_ret and event_ret['complete']: + schedule = event_ret['schedule'] + if name in schedule: + ret['result'] = True + ret['comment'] = 'Added job: {0} to schedule.'.format(name) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Schedule add failed.' @@ -630,20 +636,23 @@ def enable_job(name, **kwargs): return ret try: - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire'](event_data, 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_enabled_job_complete', wait=30) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] - # check item exists in schedule and is enabled - if name in schedule and schedule[name]['enabled']: - ret['result'] = True - ret['comment'] = 'Enabled Job {0} in schedule.'.format(name) - else: - ret['result'] = False - ret['comment'] = 'Failed to enable job {0} in schedule.'.format(name) - return ret + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire'](event_data, 'manage_schedule') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_schedule_enabled_job_complete', + wait=30, + ) + if event_ret and event_ret['complete']: + schedule = event_ret['schedule'] + # check item exists in schedule and is enabled + if name in schedule and schedule[name]['enabled']: + ret['result'] = True + ret['comment'] = 'Enabled Job {0} in schedule.'.format(name) + else: + ret['result'] = False + ret['comment'] = 'Failed to enable job {0} in schedule.'.format(name) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Schedule enable job failed.' @@ -685,20 +694,23 @@ def disable_job(name, **kwargs): return ret try: - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire'](event_data, 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_disabled_job_complete', wait=30) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] - # check item exists in schedule and is enabled - if name in schedule and not schedule[name]['enabled']: - ret['result'] = True - ret['comment'] = 'Disabled Job {0} in schedule.'.format(name) - else: - ret['result'] = False - ret['comment'] = 'Failed to disable job {0} in schedule.'.format(name) - return ret + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire'](event_data, 'manage_schedule') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_schedule_disabled_job_complete', + wait=30, + ) + if event_ret and event_ret['complete']: + schedule = event_ret['schedule'] + # check item exists in schedule and is enabled + if name in schedule and not schedule[name]['enabled']: + ret['result'] = True + ret['comment'] = 'Disabled Job {0} in schedule.'.format(name) + else: + ret['result'] = False + ret['comment'] = 'Failed to disable job {0} in schedule.'.format(name) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Schedule enable job failed.' @@ -723,16 +735,19 @@ def save(**kwargs): ret['comment'] = 'Schedule would be saved.' else: try: - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire']({'func': 'save_schedule'}, 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_saved', wait=30) - if event_ret and event_ret['complete']: - ret['result'] = True - ret['comment'] = 'Schedule (non-pillar items) saved.' - else: - ret['result'] = False - ret['comment'] = 'Failed to save schedule.' + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire']({'func': 'save_schedule'}, 'manage_schedule') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_schedule_saved', + wait=30, + ) + if event_ret and event_ret['complete']: + ret['result'] = True + ret['comment'] = 'Schedule (non-pillar items) saved.' + else: + ret['result'] = False + ret['comment'] = 'Failed to save schedule.' except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Schedule save failed.' @@ -757,19 +772,22 @@ def enable(**kwargs): ret['comment'] = 'Schedule would be enabled.' else: try: - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire']({'func': 'enable'}, 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_enabled_complete', wait=30) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] - if 'enabled' in schedule and schedule['enabled']: - ret['result'] = True - ret['comment'] = 'Enabled schedule on minion.' - else: - ret['result'] = False - ret['comment'] = 'Failed to enable schedule on minion.' - return ret + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire']({'func': 'enable'}, 'manage_schedule') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_schedule_enabled_complete', + wait=30, + ) + if event_ret and event_ret['complete']: + schedule = event_ret['schedule'] + if 'enabled' in schedule and schedule['enabled']: + ret['result'] = True + ret['comment'] = 'Enabled schedule on minion.' + else: + ret['result'] = False + ret['comment'] = 'Failed to enable schedule on minion.' + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Schedule enable job failed.' @@ -794,19 +812,22 @@ def disable(**kwargs): ret['comment'] = 'Schedule would be disabled.' else: try: - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire']({'func': 'disable'}, 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_disabled_complete', wait=30) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] - if 'enabled' in schedule and not schedule['enabled']: - ret['result'] = True - ret['comment'] = 'Disabled schedule on minion.' - else: - ret['result'] = False - ret['comment'] = 'Failed to disable schedule on minion.' - return ret + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire']({'func': 'disable'}, 'manage_schedule') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_schedule_disabled_complete', + wait=30, + ) + if event_ret and event_ret['complete']: + schedule = event_ret['schedule'] + if 'enabled' in schedule and not schedule['enabled']: + ret['result'] = True + ret['comment'] = 'Disabled schedule on minion.' + else: + ret['result'] = False + ret['comment'] = 'Failed to disable schedule on minion.' + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Schedule disable job failed.' @@ -1076,20 +1097,23 @@ def postpone_job(name, return ret try: - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire'](event_data, 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_postpone_job_complete', wait=30) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] - # check item exists in schedule and is enabled - if name in schedule and schedule[name]['enabled']: - ret['result'] = True - ret['comment'] = 'Postponed Job {0} in schedule.'.format(name) - else: - ret['result'] = False - ret['comment'] = 'Failed to postpone job {0} in schedule.'.format(name) - return ret + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire'](event_data, 'manage_schedule') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_schedule_postpone_job_complete', + wait=30, + ) + if event_ret and event_ret['complete']: + schedule = event_ret['schedule'] + # check item exists in schedule and is enabled + if name in schedule and schedule[name]['enabled']: + ret['result'] = True + ret['comment'] = 'Postponed Job {0} in schedule.'.format(name) + else: + ret['result'] = False + ret['comment'] = 'Failed to postpone job {0} in schedule.'.format(name) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Schedule postpone job failed.' @@ -1156,20 +1180,23 @@ def skip_job(name, current_time, **kwargs): return ret try: - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire'](event_data, 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_skip_job_complete', wait=30) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] - # check item exists in schedule and is enabled - if name in schedule and schedule[name]['enabled']: - ret['result'] = True - ret['comment'] = 'Added Skip Job {0} in schedule.'.format(name) - else: - ret['result'] = False - ret['comment'] = 'Failed to skip job {0} in schedule.'.format(name) - return ret + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire'](event_data, 'manage_schedule') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_schedule_skip_job_complete', + wait=30, + ) + if event_ret and event_ret['complete']: + schedule = event_ret['schedule'] + # check item exists in schedule and is enabled + if name in schedule and schedule[name]['enabled']: + ret['result'] = True + ret['comment'] = 'Added Skip Job {0} in schedule.'.format(name) + else: + ret['result'] = False + ret['comment'] = 'Failed to skip job {0} in schedule.'.format(name) + return ret except KeyError: # Effectively a no-op, since we can't really return without an event system ret['comment'] = 'Event module not available. Schedule skip job failed.' @@ -1198,11 +1225,14 @@ def show_next_fire_time(name, **kwargs): try: event_data = {'name': name, 'func': 'get_next_fire_time'} - eventer = salt.utils.event.get_event('minion', opts=__opts__) - res = __salt__['event.fire'](event_data, - 'manage_schedule') - if res: - event_ret = eventer.get_event(tag='/salt/minion/minion_schedule_next_fire_time_complete', wait=30) + with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: + res = __salt__['event.fire'](event_data, + 'manage_schedule') + if res: + event_ret = event_bus.get_event( + tag='/salt/minion/minion_schedule_next_fire_time_complete', + wait=30, + ) except KeyError: # Effectively a no-op, since we can't really return without an event system ret = {} diff --git a/salt/modules/shadow.py b/salt/modules/shadow.py index 9659867f050c..98c7369c5ebf 100644 --- a/salt/modules/shadow.py +++ b/salt/modules/shadow.py @@ -13,6 +13,7 @@ # Import python libs import os import datetime +import functools try: import spwd except ImportError: @@ -24,6 +25,7 @@ import salt.utils.stringutils from salt.exceptions import CommandExecutionError from salt.ext import six +from salt.ext.six.moves import range try: import salt.utils.pycrypto HAS_CRYPT = True @@ -48,21 +50,32 @@ def default_hash(): return '!' -def info(name): +def info(name, root=None): ''' Return information for the specified user + name + User to get the information for + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' shadow.info root ''' + if root is not None: + getspnam = functools.partial(_getspnam, root=root) + else: + getspnam = functools.partial(spwd.getspnam) + try: - data = spwd.getspnam(name) + data = getspnam(name) ret = { - 'name': data.sp_nam, - 'passwd': data.sp_pwd, + 'name': data.sp_namp if hasattr(data, 'sp_namp') else data.sp_nam, + 'passwd': data.sp_pwdp if hasattr(data, 'sp_pwdp') else data.sp_pwd, 'lstchg': data.sp_lstchg, 'min': data.sp_min, 'max': data.sp_max, @@ -82,69 +95,99 @@ def info(name): return ret -def set_inactdays(name, inactdays): +def _set_attrib(name, key, value, param, root=None, validate=True): + ''' + Set a parameter in /etc/shadow + ''' + pre_info = info(name, root=root) + + # If the user is not present or the attribute is already present, + # we return early + if not pre_info['name']: + return False + + if value == pre_info[key]: + return True + + cmd = ['chage'] + + if root is not None: + cmd.extend(('-R', root)) + + cmd.extend((param, value, name)) + + ret = not __salt__['cmd.run'](cmd, python_shell=False) + if validate: + ret = info(name, root=root).get(key) == value + return ret + + +def set_inactdays(name, inactdays, root=None): ''' Set the number of days of inactivity after a password has expired before the account is locked. See man chage. + name + User to modify + + inactdays + Set password inactive after this number of days + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' shadow.set_inactdays username 7 ''' - pre_info = info(name) - if inactdays == pre_info['inact']: - return True - cmd = 'chage -I {0} {1}'.format(inactdays, name) - __salt__['cmd.run'](cmd, python_shell=False) - post_info = info(name) - if post_info['inact'] != pre_info['inact']: - return post_info['inact'] == inactdays - return False + return _set_attrib(name, 'inact', inactdays, '-I', root=root) -def set_maxdays(name, maxdays): +def set_maxdays(name, maxdays, root=None): ''' Set the maximum number of days during which a password is valid. See man chage. + name + User to modify + + maxdays + Maximum number of days during which a password is valid + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' shadow.set_maxdays username 90 ''' - pre_info = info(name) - if maxdays == pre_info['max']: - return True - cmd = 'chage -M {0} {1}'.format(maxdays, name) - __salt__['cmd.run'](cmd, python_shell=False) - post_info = info(name) - if post_info['max'] != pre_info['max']: - return post_info['max'] == maxdays - return False + return _set_attrib(name, 'max', maxdays, '-M', root=root) -def set_mindays(name, mindays): +def set_mindays(name, mindays, root=None): ''' Set the minimum number of days between password changes. See man chage. + name + User to modify + + mindays + Minimum number of days between password changes + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' shadow.set_mindays username 7 ''' - pre_info = info(name) - if mindays == pre_info['min']: - return True - cmd = 'chage -m {0} {1}'.format(mindays, name) - __salt__['cmd.run'](cmd, python_shell=False) - post_info = info(name) - if post_info['min'] != pre_info['min']: - return post_info['min'] == mindays - return False + return _set_attrib(name, 'min', mindays, '-m', root=root) def gen_password(password, crypt_salt=None, algorithm='sha512'): @@ -189,77 +232,107 @@ def gen_password(password, crypt_salt=None, algorithm='sha512'): return salt.utils.pycrypto.gen_hash(crypt_salt, password, algorithm) -def del_password(name): +def del_password(name, root=None): ''' .. versionadded:: 2014.7.0 Delete the password from name user + name + User to delete + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' shadow.del_password username ''' - cmd = 'passwd -d {0}'.format(name) + cmd = ['passwd'] + if root is not None: + cmd.extend(('-R', root)) + cmd.extend(('-d', name)) + __salt__['cmd.run'](cmd, python_shell=False, output_loglevel='quiet') - uinfo = info(name) + uinfo = info(name, root=root) return not uinfo['passwd'] and uinfo['name'] == name -def lock_password(name): +def lock_password(name, root=None): ''' .. versionadded:: 2016.11.0 Lock the password from specified user + name + User to lock + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' shadow.lock_password username ''' - pre_info = info(name) - if pre_info['name'] == '': + pre_info = info(name, root=root) + if not pre_info['name']: return False + if pre_info['passwd'].startswith('!'): return True - cmd = 'passwd -l {0}'.format(name) - __salt__['cmd.run'](cmd, python_shell=False) + cmd = ['passwd'] - post_info = info(name) + if root is not None: + cmd.extend(('-R', root)) - return post_info['passwd'].startswith('!') + cmd.extend(('-l', name)) + __salt__['cmd.run'](cmd, python_shell=False) + return info(name, root=root)['passwd'].startswith('!') -def unlock_password(name): + +def unlock_password(name, root=None): ''' .. versionadded:: 2016.11.0 Unlock the password from name user + name + User to unlock + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' shadow.unlock_password username ''' - pre_info = info(name) - if pre_info['name'] == '': + pre_info = info(name, root=root) + if not pre_info['name']: return False - if pre_info['passwd'][0] != '!': + + if not pre_info['passwd'].startswith('!'): return True - cmd = 'passwd -u {0}'.format(name) - __salt__['cmd.run'](cmd, python_shell=False) + cmd = ['passwd'] - post_info = info(name) + if root is not None: + cmd.extend(('-R', root)) - return post_info['passwd'][0] != '!' + cmd.extend(('-u', name)) + + __salt__['cmd.run'](cmd, python_shell=False) + return not info(name, root=root)['passwd'].startswith('!') -def set_password(name, password, use_usermod=False): +def set_password(name, password, use_usermod=False, root=None): ''' Set the password for a named user. The password must be a properly defined hash. The password hash can be generated with this command: @@ -273,6 +346,18 @@ def set_password(name, password, use_usermod=False): Keep in mind that the $6 represents a sha512 hash, if your OS is using a different hashing algorithm this needs to be changed accordingly + name + User to set the password + + password + Password already hashed + + use_usermod + Use usermod command to better compatibility + + root + Directory to chroot into + CLI Example: .. code-block:: bash @@ -287,6 +372,9 @@ def set_password(name, password, use_usermod=False): s_file = '/etc/tcb/{0}/shadow'.format(name) else: s_file = '/etc/shadow' + if root: + s_file = os.path.join(root, os.path.relpath(s_file, os.path.sep)) + ret = {} if not os.path.isfile(s_file): return ret @@ -306,54 +394,67 @@ def set_password(name, password, use_usermod=False): with salt.utils.files.fopen(s_file, 'w+') as fp_: lines = [salt.utils.stringutils.to_str(_l) for _l in lines] fp_.writelines(lines) - uinfo = info(name) + uinfo = info(name, root=root) return uinfo['passwd'] == password else: # Use usermod -p (less secure, but more feature-complete) - cmd = 'usermod -p {0} {1}'.format(password, name) + cmd = ['usermod'] + if root is not None: + cmd.extend(('-R', root)) + cmd.extend(('-p', password, name)) + __salt__['cmd.run'](cmd, python_shell=False, output_loglevel='quiet') - uinfo = info(name) + uinfo = info(name, root=root) return uinfo['passwd'] == password -def set_warndays(name, warndays): +def set_warndays(name, warndays, root=None): ''' Set the number of days of warning before a password change is required. See man chage. + name + User to modify + + warndays + Number of days of warning before a password change is required + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' shadow.set_warndays username 7 ''' - pre_info = info(name) - if warndays == pre_info['warn']: - return True - cmd = 'chage -W {0} {1}'.format(warndays, name) - __salt__['cmd.run'](cmd, python_shell=False) - post_info = info(name) - if post_info['warn'] != pre_info['warn']: - return post_info['warn'] == warndays - return False + return _set_attrib(name, 'warn', warndays, '-W', root=root) -def set_date(name, date): +def set_date(name, date, root=None): ''' Sets the value for the date the password was last changed to days since the epoch (January 1, 1970). See man chage. + name + User to modify + + date + Date the password was last changed + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' shadow.set_date username 0 ''' - cmd = ['chage', '-d', date, name] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + return _set_attrib(name, 'lstchg', date, '-d', root=root, validate=False) -def set_expire(name, expire): +def set_expire(name, expire, root=None): ''' .. versionchanged:: 2014.7.0 @@ -361,26 +462,77 @@ def set_expire(name, expire): (January 1, 1970). Using a value of -1 will clear expiration. See man chage. + name + User to modify + + date + Date the account expires + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' shadow.set_expire username -1 ''' - cmd = ['chage', '-E', expire, name] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + return _set_attrib(name, 'expire', expire, '-E', root=root, validate=False) -def list_users(): +def list_users(root=None): ''' .. versionadded:: 2018.3.0 Return a list of all shadow users + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' shadow.list_users ''' - return sorted([user.sp_nam for user in spwd.getspall()]) + if root is not None: + getspall = functools.partial(_getspall, root=root) + else: + getspall = functools.partial(spwd.getspall) + + return sorted([user.sp_namp if hasattr(user, 'sp_namp') else user.sp_nam + for user in getspall()]) + + +def _getspnam(name, root=None): + ''' + Alternative implementation for getspnam, that use only /etc/shadow + ''' + root = '/' if not root else root + passwd = os.path.join(root, 'etc/shadow') + with salt.utils.files.fopen(passwd) as fp_: + for line in fp_: + line = salt.utils.stringutils.to_unicode(line) + comps = line.strip().split(':') + if comps[0] == name: + # Generate a getspnam compatible output + for i in range(2, 9): + comps[i] = int(comps[i]) if comps[i] else -1 + return spwd.struct_spwd(comps) + raise KeyError + + +def _getspall(root=None): + ''' + Alternative implementation for getspnam, that use only /etc/shadow + ''' + root = '/' if not root else root + passwd = os.path.join(root, 'etc/shadow') + with salt.utils.files.fopen(passwd) as fp_: + for line in fp_: + line = salt.utils.stringutils.to_unicode(line) + comps = line.strip().split(':') + # Generate a getspall compatible output + for i in range(2, 9): + comps[i] = int(comps[i]) if comps[i] else -1 + yield spwd.struct_spwd(comps) diff --git a/salt/modules/smartos_imgadm.py b/salt/modules/smartos_imgadm.py index 7730678a5515..a16f4c3de74f 100644 --- a/salt/modules/smartos_imgadm.py +++ b/salt/modules/smartos_imgadm.py @@ -56,7 +56,7 @@ def _parse_image_meta(image=None, detail=False): if image and 'Error' in image: ret = image - elif image and 'manifest' in image: + elif image and 'manifest' in image and 'name' in image['manifest']: name = image['manifest']['name'] version = image['manifest']['version'] os = image['manifest']['os'] @@ -97,6 +97,11 @@ def _parse_image_meta(image=None, detail=False): version=version, published=published, ) + else: + log.debug("smartos_image - encountered invalid image payload: {}".format(image)) + ret = { + 'Error': 'This looks like an orphaned image, image payload was invalid.' + } return ret @@ -110,8 +115,7 @@ def _split_docker_uuid(uuid): if len(uuid) == 2: tag = uuid[1] repo = uuid[0] - if len(repo.split('/')) == 2: - return repo, tag + return repo, tag return None, None diff --git a/salt/modules/state.py b/salt/modules/state.py index b51c775f5bb7..d6c9db1b5d08 100644 --- a/salt/modules/state.py +++ b/salt/modules/state.py @@ -33,6 +33,7 @@ import salt.utils.hashutils import salt.utils.jid import salt.utils.json +import salt.utils.msgpack import salt.utils.platform import salt.utils.state import salt.utils.stringutils @@ -45,7 +46,6 @@ # Import 3rd-party libs from salt.ext import six -import msgpack __proxyenabled__ = ['*'] @@ -185,7 +185,7 @@ def _get_pause(jid, state_id=None): data[state_id] = {} if os.path.exists(pause_path): with salt.utils.files.fopen(pause_path, 'rb') as fp_: - data = msgpack.loads(fp_.read()) + data = salt.utils.msgpack.loads(fp_.read()) return data, pause_path @@ -256,7 +256,7 @@ def soft_kill(jid, state_id=None): data, pause_path = _get_pause(jid, state_id) data[state_id]['kill'] = True with salt.utils.files.fopen(pause_path, 'wb') as fp_: - fp_.write(msgpack.dumps(data)) + fp_.write(salt.utils.msgpack.dumps(data)) def pause(jid, state_id=None, duration=None): @@ -291,7 +291,7 @@ def pause(jid, state_id=None, duration=None): if duration: data[state_id]['duration'] = int(duration) with salt.utils.files.fopen(pause_path, 'wb') as fp_: - fp_.write(msgpack.dumps(data)) + fp_.write(salt.utils.msgpack.dumps(data)) def resume(jid, state_id=None): @@ -325,7 +325,7 @@ def resume(jid, state_id=None): if state_id == '__all__': data = {} with salt.utils.files.fopen(pause_path, 'wb') as fp_: - fp_.write(msgpack.dumps(data)) + fp_.write(salt.utils.msgpack.dumps(data)) def orchestrate(mods, @@ -2400,37 +2400,37 @@ def event(tagmatch='*', salt-call --local state.event pretty=True ''' - sevent = salt.utils.event.get_event( - node, - sock_dir or __opts__['sock_dir'], - __opts__['transport'], - opts=__opts__, - listen=True) + with salt.utils.event.get_event( + node, + sock_dir or __opts__['sock_dir'], + __opts__['transport'], + opts=__opts__, + listen=True) as sevent: - while True: - ret = sevent.get_event(full=True, auto_reconnect=True) - if ret is None: - continue + while True: + ret = sevent.get_event(full=True, auto_reconnect=True) + if ret is None: + continue - if salt.utils.stringutils.expr_match(ret['tag'], tagmatch): - if not quiet: - salt.utils.stringutils.print_cli( - str('{0}\t{1}').format( # future lint: blacklisted-function - salt.utils.stringutils.to_str(ret['tag']), - salt.utils.json.dumps( - ret['data'], - sort_keys=pretty, - indent=None if not pretty else 4) + if salt.utils.stringutils.expr_match(ret['tag'], tagmatch): + if not quiet: + salt.utils.stringutils.print_cli( + str('{0}\t{1}').format( # future lint: blacklisted-function + salt.utils.stringutils.to_str(ret['tag']), + salt.utils.json.dumps( + ret['data'], + sort_keys=pretty, + indent=None if not pretty else 4) + ) ) - ) - sys.stdout.flush() + sys.stdout.flush() - if count > 0: - count -= 1 - log.debug('Remaining event matches: %s', count) + if count > 0: + count -= 1 + log.debug('Remaining event matches: %s', count) - if count == 0: - break - else: - log.debug('Skipping event tag: %s', ret['tag']) - continue + if count == 0: + break + else: + log.debug('Skipping event tag: %s', ret['tag']) + continue diff --git a/salt/modules/status.py b/salt/modules/status.py index 94c929569804..6377196bc217 100644 --- a/salt/modules/status.py +++ b/salt/modules/status.py @@ -1630,11 +1630,11 @@ def master(master=None, connected=True): # Connection to master is not as expected if master_connection_status is not connected: - event = salt.utils.event.get_event('minion', opts=__opts__, listen=False) - if master_connection_status: - event.fire_event({'master': master}, salt.minion.master_event(type='connected')) - else: - event.fire_event({'master': master}, salt.minion.master_event(type='disconnected')) + with salt.utils.event.get_event('minion', opts=__opts__, listen=False) as event: + if master_connection_status: + event.fire_event({'master': master}, salt.minion.master_event(type='connected')) + else: + event.fire_event({'master': master}, salt.minion.master_event(type='disconnected')) return master_connection_status @@ -1669,18 +1669,18 @@ def ping_master(master): load = {'cmd': 'ping'} result = False - channel = salt.transport.client.ReqChannel.factory(opts, crypt='clear') - try: - payload = channel.send(load, tries=0, timeout=timeout) - result = True - except Exception as e: - pass + with salt.transport.client.ReqChannel.factory(opts, crypt='clear') as channel: + try: + payload = channel.send(load, tries=0, timeout=timeout) + result = True + except Exception as e: + pass - if result: - event = salt.utils.event.get_event('minion', opts=__opts__, listen=False) - event.fire_event({'master': master}, salt.minion.master_event(type='failback')) + if result: + with salt.utils.event.get_event('minion', opts=__opts__, listen=False) as event: + event.fire_event({'master': master}, salt.minion.master_event(type='failback')) - return result + return result def proxy_reconnect(proxy_name, opts=None): diff --git a/salt/modules/sysfs.py b/salt/modules/sysfs.py index 8ef3c4fe7b25..62e0ade86d76 100644 --- a/salt/modules/sysfs.py +++ b/salt/modules/sysfs.py @@ -159,7 +159,7 @@ def target(key, full=True): key = os.path.realpath(key) if not os.path.exists(key): - log.debug('Unkown SysFS key %s', key) + log.debug('Unknown SysFS key %s', key) return False elif full: return key diff --git a/salt/modules/system.py b/salt/modules/system.py index 37e5f4823511..c834fd52564c 100644 --- a/salt/modules/system.py +++ b/salt/modules/system.py @@ -122,7 +122,15 @@ def shutdown(at_time=None): salt '*' system.shutdown 5 ''' - cmd = ['shutdown', '-h', ('{0}'.format(at_time) if at_time else 'now')] + if (salt.utils.platform.is_freebsd() or + salt.utils.platform.is_netbsd() or + salt.utils.platform.is_openbsd()): + # these platforms don't power off by default when halted + flag = '-p' + else: + flag = '-h' + + cmd = ['shutdown', flag, ('{0}'.format(at_time) if at_time else 'now')] ret = __salt__['cmd.run'](cmd, python_shell=False) return ret diff --git a/salt/modules/systemd_service.py b/salt/modules/systemd_service.py index fb349d30e6c7..3efd455ba689 100644 --- a/salt/modules/systemd_service.py +++ b/salt/modules/systemd_service.py @@ -56,7 +56,7 @@ def __virtual__(): Only work on systems that have been booted with systemd ''' if __grains__['kernel'] == 'Linux' \ - and salt.utils.systemd.booted(__context__): + and salt.utils.systemd.booted(__context__): return __virtualname__ return ( False, @@ -65,6 +65,16 @@ def __virtual__(): ) +def _root(path, root): + ''' + Relocate an absolute path to a new root directory. + ''' + if root: + return os.path.join(root, os.path.relpath(path, os.path.sep)) + else: + return path + + def _canonical_unit_name(name): ''' Build a canonical unit name treating unit names without one @@ -123,15 +133,15 @@ def _check_for_unit_changes(name): __context__[contextkey] = True -def _check_unmask(name, unmask, unmask_runtime): +def _check_unmask(name, unmask, unmask_runtime, root=None): ''' Common code for conditionally removing masks before making changes to a service's state. ''' if unmask: - unmask_(name, runtime=False) + unmask_(name, runtime=False, root=root) if unmask_runtime: - unmask_(name, runtime=True) + unmask_(name, runtime=True, root=root) def _clear_context(): @@ -193,15 +203,16 @@ def _default_runlevel(): return runlevel -def _get_systemd_services(): +def _get_systemd_services(root): ''' Use os.listdir() to get all the unit files ''' ret = set() for path in SYSTEM_CONFIG_PATHS + (LOCAL_CONFIG_PATH,): - # Make sure user has access to the path, and if the path is a link - # it's likely that another entry in SYSTEM_CONFIG_PATHS or LOCAL_CONFIG_PATH - # points to it, so we can ignore it. + # Make sure user has access to the path, and if the path is a + # link it's likely that another entry in SYSTEM_CONFIG_PATHS + # or LOCAL_CONFIG_PATH points to it, so we can ignore it. + path = _root(path, root) if os.access(path, os.R_OK) and not os.path.islink(path): for fullname in os.listdir(path): try: @@ -213,19 +224,20 @@ def _get_systemd_services(): return ret -def _get_sysv_services(systemd_services=None): +def _get_sysv_services(root, systemd_services=None): ''' Use os.listdir() and os.access() to get all the initscripts ''' + initscript_path = _root(INITSCRIPT_PATH, root) try: - sysv_services = os.listdir(INITSCRIPT_PATH) + sysv_services = os.listdir(initscript_path) except OSError as exc: if exc.errno == errno.ENOENT: pass elif exc.errno == errno.EACCES: log.error( 'Unable to check sysvinit scripts, permission denied to %s', - INITSCRIPT_PATH + initscript_path ) else: log.error( @@ -236,11 +248,11 @@ def _get_sysv_services(systemd_services=None): return [] if systemd_services is None: - systemd_services = _get_systemd_services() + systemd_services = _get_systemd_services(root) ret = [] for sysv_service in sysv_services: - if os.access(os.path.join(INITSCRIPT_PATH, sysv_service), os.X_OK): + if os.access(os.path.join(initscript_path, sysv_service), os.X_OK): if sysv_service in systemd_services: log.debug( 'sysvinit script \'%s\' found, but systemd unit ' @@ -303,7 +315,8 @@ def _strip_scope(msg): return '\n'.join(ret).strip() -def _systemctl_cmd(action, name=None, systemd_scope=False, no_block=False): +def _systemctl_cmd(action, name=None, systemd_scope=False, no_block=False, + root=None): ''' Build a systemctl command line. Treat unit names without one of the valid suffixes as a service. @@ -316,6 +329,8 @@ def _systemctl_cmd(action, name=None, systemd_scope=False, no_block=False): ret.append('systemctl') if no_block: ret.append('--no-block') + if root: + ret.extend(['--root', root]) if isinstance(action, six.string_types): action = shlex.split(action) ret.extend(action) @@ -343,26 +358,27 @@ def _systemctl_status(name): return __context__[contextkey] -def _sysv_enabled(name): +def _sysv_enabled(name, root): ''' A System-V style service is assumed disabled if the "startup" symlink (starts with "S") to its script is found in /etc/init.d in the current runlevel. ''' # Find exact match (disambiguate matches like "S01anacron" for cron) - for match in glob.glob('/etc/rc%s.d/S*%s' % (_runlevel(), name)): + rc = _root('/etc/rc{}.d/S*{}'.format(_runlevel(), name), root) + for match in glob.glob(rc): if re.match(r'S\d{,2}%s' % name, os.path.basename(match)): return True return False -def _untracked_custom_unit_found(name): +def _untracked_custom_unit_found(name, root=None): ''' If the passed service name is not available, but a unit file exist in /etc/systemd/system, return True. Otherwise, return False. ''' - unit_path = os.path.join('/etc/systemd/system', - _canonical_unit_name(name)) + system = _root('/etc/systemd/system', root) + unit_path = os.path.join(system, _canonical_unit_name(name)) return os.access(unit_path, os.R_OK) and not _check_available(name) @@ -371,7 +387,8 @@ def _unit_file_changed(name): Returns True if systemctl reports that the unit file has changed, otherwise returns False. ''' - return "'systemctl daemon-reload'" in _systemctl_status(name)['stdout'].lower() + status = _systemctl_status(name)['stdout'].lower() + return "'systemctl daemon-reload'" in status def systemctl_reload(): @@ -389,8 +406,7 @@ def systemctl_reload(): out = __salt__['cmd.run_all']( _systemctl_cmd('--system daemon-reload'), python_shell=False, - redirect_stderr=True - ) + redirect_stderr=True) if out['retcode'] != 0: raise CommandExecutionError( 'Problem performing systemctl daemon-reload: %s' % out['stdout'] @@ -414,8 +430,7 @@ def get_running(): out = __salt__['cmd.run']( _systemctl_cmd('--full --no-legend --no-pager'), python_shell=False, - ignore_retcode=True, - ) + ignore_retcode=True) for line in salt.utils.itertools.split(out, '\n'): try: comps = line.strip().split() @@ -438,10 +453,13 @@ def get_running(): return sorted(ret) -def get_enabled(): +def get_enabled(root=None): ''' Return a list of all enabled services + root + Enable/disable/mask unit files in the specified root directory + CLI Example: .. code-block:: bash @@ -452,10 +470,10 @@ def get_enabled(): # Get enabled systemd units. Can't use --state=enabled here because it's # not present until systemd 216. out = __salt__['cmd.run']( - _systemctl_cmd('--full --no-legend --no-pager list-unit-files'), + _systemctl_cmd('--full --no-legend --no-pager list-unit-files', + root=root), python_shell=False, - ignore_retcode=True, - ) + ignore_retcode=True) for line in salt.utils.itertools.split(out, '\n'): try: fullname, unit_state = line.strip().split(None, 1) @@ -473,15 +491,18 @@ def get_enabled(): # Add in any sysvinit services that are enabled ret.update(set( - [x for x in _get_sysv_services() if _sysv_enabled(x)] + [x for x in _get_sysv_services(root) if _sysv_enabled(x, root)] )) return sorted(ret) -def get_disabled(): +def get_disabled(root=None): ''' Return a list of all disabled services + root + Enable/disable/mask unit files in the specified root directory + CLI Example: .. code-block:: bash @@ -492,10 +513,10 @@ def get_disabled(): # Get disabled systemd units. Can't use --state=disabled here because it's # not present until systemd 216. out = __salt__['cmd.run']( - _systemctl_cmd('--full --no-legend --no-pager list-unit-files'), + _systemctl_cmd('--full --no-legend --no-pager list-unit-files', + root=root), python_shell=False, - ignore_retcode=True, - ) + ignore_retcode=True) for line in salt.utils.itertools.split(out, '\n'): try: fullname, unit_state = line.strip().split(None, 1) @@ -513,17 +534,20 @@ def get_disabled(): # Add in any sysvinit services that are disabled ret.update(set( - [x for x in _get_sysv_services() if not _sysv_enabled(x)] + [x for x in _get_sysv_services(root) if not _sysv_enabled(x, root)] )) return sorted(ret) -def get_static(): +def get_static(root=None): ''' .. versionadded:: 2015.8.5 Return a list of all static services + root + Enable/disable/mask unit files in the specified root directory + CLI Example: .. code-block:: bash @@ -534,10 +558,10 @@ def get_static(): # Get static systemd units. Can't use --state=static here because it's # not present until systemd 216. out = __salt__['cmd.run']( - _systemctl_cmd('--full --no-legend --no-pager list-unit-files'), + _systemctl_cmd('--full --no-legend --no-pager list-unit-files', + root=root), python_shell=False, - ignore_retcode=True, - ) + ignore_retcode=True) for line in salt.utils.itertools.split(out, '\n'): try: fullname, unit_state = line.strip().split(None, 1) @@ -557,18 +581,21 @@ def get_static(): return sorted(ret) -def get_all(): +def get_all(root=None): ''' Return a list of all available services + root + Enable/disable/mask unit files in the specified root directory + CLI Example: .. code-block:: bash salt '*' service.get_all ''' - ret = _get_systemd_services() - ret.update(set(_get_sysv_services(systemd_services=ret))) + ret = _get_systemd_services(root) + ret.update(set(_get_sysv_services(root, systemd_services=ret))) return sorted(ret) @@ -606,7 +633,7 @@ def missing(name): return not available(name) -def unmask_(name, runtime=False): +def unmask_(name, runtime=False, root=None): ''' .. versionadded:: 2015.5.0 .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 @@ -633,6 +660,9 @@ def unmask_(name, runtime=False): removes a runtime mask only when this argument is set to ``True``, and otherwise removes an indefinite mask. + root + Enable/disable/mask unit files in the specified root directory + CLI Example: .. code-block:: bash @@ -641,15 +671,16 @@ def unmask_(name, runtime=False): salt '*' service.unmask foo runtime=True ''' _check_for_unit_changes(name) - if not masked(name, runtime): + if not masked(name, runtime, root=root): log.debug('Service \'%s\' is not %smasked', name, 'runtime-' if runtime else '') return True cmd = 'unmask --runtime' if runtime else 'unmask' - out = __salt__['cmd.run_all'](_systemctl_cmd(cmd, name, systemd_scope=True), - python_shell=False, - redirect_stderr=True) + out = __salt__['cmd.run_all']( + _systemctl_cmd(cmd, name, systemd_scope=True, root=root), + python_shell=False, + redirect_stderr=True) if out['retcode'] != 0: raise CommandExecutionError('Failed to unmask service \'%s\'' % name) @@ -657,7 +688,7 @@ def unmask_(name, runtime=False): return True -def mask(name, runtime=False): +def mask(name, runtime=False, root=None): ''' .. versionadded:: 2015.5.0 .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 @@ -678,6 +709,9 @@ def mask(name, runtime=False): .. versionadded:: 2015.8.5 + root + Enable/disable/mask unit files in the specified root directory + CLI Example: .. code-block:: bash @@ -688,9 +722,10 @@ def mask(name, runtime=False): _check_for_unit_changes(name) cmd = 'mask --runtime' if runtime else 'mask' - out = __salt__['cmd.run_all'](_systemctl_cmd(cmd, name, systemd_scope=True), - python_shell=False, - redirect_stderr=True) + out = __salt__['cmd.run_all']( + _systemctl_cmd(cmd, name, systemd_scope=True, root=root), + python_shell=False, + redirect_stderr=True) if out['retcode'] != 0: raise CommandExecutionError( @@ -701,7 +736,7 @@ def mask(name, runtime=False): return True -def masked(name, runtime=False): +def masked(name, runtime=False, root=None): ''' .. versionadded:: 2015.8.0 .. versionchanged:: 2015.8.5 @@ -731,6 +766,9 @@ def masked(name, runtime=False): only checks for runtime masks if this argument is set to ``True``. Otherwise, it will check for an indefinite mask. + root + Enable/disable/mask unit files in the specified root directory + CLI Examples: .. code-block:: bash @@ -739,7 +777,7 @@ def masked(name, runtime=False): salt '*' service.masked foo runtime=True ''' _check_for_unit_changes(name) - root_dir = '/run' if runtime else '/etc' + root_dir = _root('/run' if runtime else '/etc', root) link_path = os.path.join(root_dir, 'systemd', 'system', @@ -1055,9 +1093,10 @@ def status(name, sig=None): # pylint: disable=unused-argument results = {} for service in services: _check_for_unit_changes(service) - results[service] = __salt__['cmd.retcode'](_systemctl_cmd('is-active', service), - python_shell=False, - ignore_retcode=True) == 0 + results[service] = __salt__['cmd.retcode']( + _systemctl_cmd('is-active', service), + python_shell=False, + ignore_retcode=True) == 0 if contains_globbing: return results return results[name] @@ -1065,7 +1104,8 @@ def status(name, sig=None): # pylint: disable=unused-argument # **kwargs is required to maintain consistency with the API established by # Salt's service management states. -def enable(name, no_block=False, unmask=False, unmask_runtime=False, **kwargs): # pylint: disable=unused-argument +def enable(name, no_block=False, unmask=False, unmask_runtime=False, + root=None, **kwargs): # pylint: disable=unused-argument ''' .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to @@ -1101,6 +1141,9 @@ def enable(name, no_block=False, unmask=False, unmask_runtime=False, **kwargs): In previous releases, Salt would simply unmask a service before enabling. This behavior is no longer the default. + root + Enable/disable/mask unit files in the specified root directory + CLI Example: .. code-block:: bash @@ -1108,8 +1151,8 @@ def enable(name, no_block=False, unmask=False, unmask_runtime=False, **kwargs): salt '*' service.enable ''' _check_for_unit_changes(name) - _check_unmask(name, unmask, unmask_runtime) - if name in _get_sysv_services(): + _check_unmask(name, unmask, unmask_runtime, root) + if name in _get_sysv_services(root): cmd = [] if salt.utils.systemd.has_scope(__context__) \ and __salt__['config.get']('systemd.scope', True): @@ -1123,7 +1166,8 @@ def enable(name, no_block=False, unmask=False, unmask_runtime=False, **kwargs): python_shell=False, ignore_retcode=True) == 0 ret = __salt__['cmd.run_all']( - _systemctl_cmd('enable', name, systemd_scope=True, no_block=no_block), + _systemctl_cmd('enable', name, systemd_scope=True, no_block=no_block, + root=root), python_shell=False, ignore_retcode=True) @@ -1137,7 +1181,7 @@ def enable(name, no_block=False, unmask=False, unmask_runtime=False, **kwargs): # The unused kwargs argument is required to maintain consistency with the API # established by Salt's service management states. -def disable(name, no_block=False, **kwargs): # pylint: disable=unused-argument +def disable(name, no_block=False, root=None, **kwargs): # pylint: disable=unused-argument ''' .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to @@ -1157,6 +1201,9 @@ def disable(name, no_block=False, **kwargs): # pylint: disable=unused-argument .. versionadded:: 2017.7.0 + root + Enable/disable/mask unit files in the specified root directory + CLI Example: .. code-block:: bash @@ -1164,7 +1211,7 @@ def disable(name, no_block=False, **kwargs): # pylint: disable=unused-argument salt '*' service.disable ''' _check_for_unit_changes(name) - if name in _get_sysv_services(): + if name in _get_sysv_services(root): cmd = [] if salt.utils.systemd.has_scope(__context__) \ and __salt__['config.get']('systemd.scope', True): @@ -1179,17 +1226,21 @@ def disable(name, no_block=False, **kwargs): # pylint: disable=unused-argument ignore_retcode=True) == 0 # Using cmd.run_all instead of cmd.retcode here to make unit tests easier return __salt__['cmd.run_all']( - _systemctl_cmd('disable', name, systemd_scope=True, no_block=no_block), + _systemctl_cmd('disable', name, systemd_scope=True, no_block=no_block, + root=root), python_shell=False, ignore_retcode=True)['retcode'] == 0 # The unused kwargs argument is required to maintain consistency with the API # established by Salt's service management states. -def enabled(name, **kwargs): # pylint: disable=unused-argument +def enabled(name, root=None, **kwargs): # pylint: disable=unused-argument ''' Return if the named service is enabled to start on boot + root + Enable/disable/mask unit files in the specified root directory + CLI Example: .. code-block:: bash @@ -1199,7 +1250,7 @@ def enabled(name, **kwargs): # pylint: disable=unused-argument # Try 'systemctl is-enabled' first, then look for a symlink created by # systemctl (older systemd releases did not support using is-enabled to # check templated services), and lastly check for a sysvinit service. - if __salt__['cmd.retcode'](_systemctl_cmd('is-enabled', name), + if __salt__['cmd.retcode'](_systemctl_cmd('is-enabled', name, root=root), python_shell=False, ignore_retcode=True) == 0: return True @@ -1207,43 +1258,50 @@ def enabled(name, **kwargs): # pylint: disable=unused-argument # On older systemd releases, templated services could not be checked # with ``systemctl is-enabled``. As a fallback, look for the symlinks # created by systemctl when enabling templated services. - cmd = ['find', LOCAL_CONFIG_PATH, '-name', name, + local_config_path = _root(LOCAL_CONFIG_PATH, '/') + cmd = ['find', local_config_path, '-name', name, '-type', 'l', '-print', '-quit'] # If the find command returns any matches, there will be output and the # string will be non-empty. if bool(__salt__['cmd.run'](cmd, python_shell=False)): return True - elif name in _get_sysv_services(): - return _sysv_enabled(name) + elif name in _get_sysv_services(root): + return _sysv_enabled(name, root) return False -def disabled(name): +def disabled(name, root=None): ''' Return if the named service is disabled from starting on boot + root + Enable/disable/mask unit files in the specified root directory + CLI Example: .. code-block:: bash salt '*' service.disabled ''' - return not enabled(name) + return not enabled(name, root=root) -def show(name): +def show(name, root=None): ''' .. versionadded:: 2014.7.0 Show properties of one or more units/jobs or the manager + root + Enable/disable/mask unit files in the specified root directory + CLI Example: salt '*' service.show ''' ret = {} - out = __salt__['cmd.run'](_systemctl_cmd('show', name), + out = __salt__['cmd.run'](_systemctl_cmd('show', name, root=root), python_shell=False) for line in salt.utils.itertools.split(out, '\n'): comps = line.split('=') @@ -1263,19 +1321,22 @@ def show(name): return ret -def execs(): +def execs(root=None): ''' .. versionadded:: 2014.7.0 Return a list of all files specified as ``ExecStart`` for all services. + root + Enable/disable/mask unit files in the specified root directory + CLI Example: salt '*' service.execs ''' ret = {} - for service in get_all(): - data = show(service) + for service in get_all(root=root): + data = show(service, root=root) if 'ExecStart' not in data: continue ret[service] = data['ExecStart']['path'] diff --git a/salt/modules/twilio_notify.py b/salt/modules/twilio_notify.py index dfdb512580a3..93951e188c82 100644 --- a/salt/modules/twilio_notify.py +++ b/salt/modules/twilio_notify.py @@ -6,7 +6,10 @@ :depends: - twilio python module :configuration: Configure this module by specifying the name of a configuration - profile in the minion config, minion pillar, or master config. + profile in the minion config, minion pillar, or master config (with :conf_master:`pillar_opts` set to True). + + .. warning: Setting pillar_opts to True in the master config may be considered + unsafe as it copies the master config to pillar For example: @@ -73,7 +76,7 @@ def send_sms(profile, body, to, from_): CLI Example: - twilio.send_sms twilio-account 'Test sms' '+18019999999' '+18011111111' + twilio.send_sms my-twilio-account 'Test sms' '+18019999999' '+18011111111' ''' ret = {} ret['message'] = {} diff --git a/salt/modules/upstart_service.py b/salt/modules/upstart_service.py index eb6362b2d9d3..751507c4530f 100644 --- a/salt/modules/upstart_service.py +++ b/salt/modules/upstart_service.py @@ -50,7 +50,6 @@ import glob import os import re -import itertools import fnmatch # Import salt libs @@ -58,6 +57,7 @@ import salt.utils.files import salt.utils.path import salt.utils.systemd +from salt.ext.six.moves import filter # pylint: disable=import-error,redefined-builtin __func_alias__ = { 'reload_': 'reload' @@ -190,7 +190,7 @@ def _upstart_is_disabled(name): in /etc/init/[name].conf. ''' files = ['/etc/init/{0}.conf'.format(name), '/etc/init/{0}.override'.format(name)] - for file_name in itertools.ifilter(os.path.isfile, files): + for file_name in filter(os.path.isfile, files): with salt.utils.files.fopen(file_name) as fp_: if re.search(r'^\s*manual', salt.utils.stringutils.to_unicode(fp_.read()), @@ -516,7 +516,7 @@ def _upstart_enable(name): return _upstart_is_enabled(name) override = '/etc/init/{0}.override'.format(name) files = ['/etc/init/{0}.conf'.format(name), override] - for file_name in itertools.ifilter(os.path.isfile, files): + for file_name in filter(os.path.isfile, files): with salt.utils.files.fopen(file_name, 'r+') as fp_: new_text = re.sub(r'^\s*manual\n?', '', diff --git a/salt/modules/useradd.py b/salt/modules/useradd.py index e370dd4bb31a..e38a094ed28c 100644 --- a/salt/modules/useradd.py +++ b/salt/modules/useradd.py @@ -17,6 +17,8 @@ HAS_PWD = False import logging import copy +import functools +import os # Import salt libs import salt.utils.data @@ -55,12 +57,17 @@ def _quote_username(name): return salt.utils.stringutils.to_str(name) -def _get_gecos(name): +def _get_gecos(name, root=None): ''' Retrieve GECOS field info and return it in dictionary form ''' + if root is not None and __grains__['kernel'] != 'AIX': + getpwnam = functools.partial(_getpwnam, root=root) + else: + getpwnam = functools.partial(pwd.getpwnam) gecos_field = salt.utils.stringutils.to_unicode( - pwd.getpwnam(_quote_username(name)).pw_gecos).split(',', 4) + getpwnam(_quote_username(name)).pw_gecos).split(',', 4) + if not gecos_field: return {} else: @@ -96,7 +103,7 @@ def _update_gecos(name, key, value, root=None): value = six.text_type(value) else: value = salt.utils.stringutils.to_unicode(value) - pre_info = _get_gecos(name) + pre_info = _get_gecos(name, root=root) if not pre_info: return False if value == pre_info[key]: @@ -104,14 +111,13 @@ def _update_gecos(name, key, value, root=None): gecos_data = copy.deepcopy(pre_info) gecos_data[key] = value - cmd = ['usermod', '-c', _build_gecos(gecos_data), name] - + cmd = ['usermod'] if root is not None and __grains__['kernel'] != 'AIX': cmd.extend(('-R', root)) + cmd.extend(('-c', _build_gecos(gecos_data), name)) __salt__['cmd.run'](cmd, python_shell=False) - post_info = info(name) - return _get_gecos(name).get(key) == value + return _get_gecos(name, root=root).get(key) == value def add(name, @@ -129,11 +135,62 @@ def add(name, other='', createhome=True, loginclass=None, - root=None, - nologinit=False): + nologinit=False, + root=None): ''' Add a user to the minion + name + Username LOGIN to add + + uid + User ID of the new account + + gid + Name or ID of the primary group of the new accoun + + groups + List of supplementary groups of the new account + + home + Home directory of the new account + + shell + Login shell of the new account + + unique + Allow to create users with duplicate + + system + Create a system account + + fullname + GECOS field for the full name + + roomnumber + GECOS field for the room number + + workphone + GECOS field for the work phone + + homephone + GECOS field for the home phone + + other + GECOS field for other information + + createhome + Create the user's home directory + + loginclass + Login class for the new account (OpenBSD) + + nologinit + Do not add the user to the lastlog and faillog databases + + root + Directory to chroot into + CLI Example: .. code-block:: bash @@ -231,17 +288,17 @@ def add(name, # user does exist, and B) running useradd again would result in a # nonzero exit status and be interpreted as a False result. if groups: - chgroups(name, groups) + chgroups(name, groups, root=root) if fullname: - chfullname(name, fullname) + chfullname(name, fullname, root=root) if roomnumber: - chroomnumber(name, roomnumber) + chroomnumber(name, roomnumber, root=root) if workphone: - chworkphone(name, workphone) + chworkphone(name, workphone, root=root) if homephone: - chhomephone(name, homephone) + chhomephone(name, homephone, root=root) if other: - chother(name, other) + chother(name, other, root=root) return True @@ -249,6 +306,18 @@ def delete(name, remove=False, force=False, root=None): ''' Remove a user from the minion + name + Username to delete + + remove + Remove home directory and mail spool + + force + Force some actions that would fail otherwise + + root + Directory to chroot into + CLI Example: .. code-block:: bash @@ -292,10 +361,16 @@ def delete(name, remove=False, force=False, root=None): return False -def getent(refresh=False): +def getent(refresh=False, root=None): ''' Return the list of all info for all users + refresh + Force a refresh of user information + + root + Directory to chroot into + CLI Example: .. code-block:: bash @@ -306,72 +381,106 @@ def getent(refresh=False): return __context__['user.getent'] ret = [] - for data in pwd.getpwall(): + if root is not None and __grains__['kernel'] != 'AIX': + getpwall = functools.partial(_getpwall, root=root) + else: + getpwall = functools.partial(pwd.getpwall) + + for data in getpwall(): ret.append(_format_info(data)) __context__['user.getent'] = ret return ret -def chuid(name, uid): +def _chattrib(name, key, value, param, persist=False, root=None): + ''' + Change an attribute for a named user + ''' + pre_info = info(name, root=root) + if not pre_info: + raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) + + if value == pre_info[key]: + return True + + cmd = ['usermod'] + + if root is not None and __grains__['kernel'] != 'AIX': + cmd.extend(('-R', root)) + + if persist and __grains__['kernel'] != 'OpenBSD': + cmd.append('-m') + + cmd.extend((param, value, name)) + + __salt__['cmd.run'](cmd, python_shell=False) + return info(name, root=root).get(key) == value + + +def chuid(name, uid, root=None): ''' Change the uid for a named user + name + User to modify + + uid + New UID for the user account + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' user.chuid foo 4376 ''' - pre_info = info(name) - if uid == pre_info['uid']: - return True - cmd = ['usermod', '-u', uid, name] - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('uid') == uid + return _chattrib(name, 'uid', uid, '-u', root=root) def chgid(name, gid, root=None): ''' Change the default group of the user + name + User to modify + + gid + Force use GID as new primary group + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' user.chgid foo 4376 ''' - pre_info = info(name) - if gid == pre_info['gid']: - return True - cmd = ['usermod', '-g', gid, name] - - if root is not None and __grains__['kernel'] != 'AIX': - cmd.extend(('-R', root)) - - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('gid') == gid + return _chattrib(name, 'gid', gid, '-g', root=root) def chshell(name, shell, root=None): ''' Change the default shell of the user + name + User to modify + + shell + New login shell for the user account + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' user.chshell foo /bin/zsh ''' - pre_info = info(name) - if shell == pre_info['shell']: - return True - cmd = ['usermod', '-s', shell, name] - - if root is not None and __grains__['kernel'] != 'AIX': - cmd.extend(('-R', root)) - - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('shell') == shell + return _chattrib(name, 'shell', shell, '-s', root=root) def chhome(name, home, persist=False, root=None): @@ -379,25 +488,25 @@ def chhome(name, home, persist=False, root=None): Change the home directory of the user, pass True for persist to move files to the new home directory if the old home directory exist. + name + User to modify + + home + New home directory for the user account + + presist + Move contents of the home directory to the new location + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' user.chhome foo /home/users/foo True ''' - pre_info = info(name) - if home == pre_info['home']: - return True - cmd = ['usermod', '-d', home] - - if root is not None and __grains__['kernel'] != 'AIX': - cmd.extend(('-R', root)) - - if persist and __grains__['kernel'] != 'OpenBSD': - cmd.append('-m') - cmd.append(name) - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('home') == home + return _chattrib(name, 'home', home, '-d', persist=persist, root=root) def chgroups(name, groups, append=False, root=None): @@ -414,6 +523,9 @@ def chgroups(name, groups, append=False, root=None): If ``True``, append the specified group(s). Otherwise, this function will replace the user's groups with the specified group(s). + root + Directory to chroot into + CLI Examples: .. code-block:: bash @@ -460,20 +572,29 @@ def chgroups(name, groups, append=False, root=None): return result['retcode'] == 0 -def chfullname(name, fullname): +def chfullname(name, fullname, root=None): ''' Change the user's Full Name + name + User to modify + + fullname + GECOS field for the full name + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' user.chfullname foo "Foo Bar" ''' - return _update_gecos(name, 'fullname', fullname) + return _update_gecos(name, 'fullname', fullname, root=root) -def chroomnumber(name, roomnumber): +def chroomnumber(name, roomnumber, root=None): ''' Change the user's Room Number @@ -483,52 +604,88 @@ def chroomnumber(name, roomnumber): salt '*' user.chroomnumber foo 123 ''' - return _update_gecos(name, 'roomnumber', roomnumber) + return _update_gecos(name, 'roomnumber', roomnumber, root=root) -def chworkphone(name, workphone): +def chworkphone(name, workphone, root=None): ''' Change the user's Work Phone + name + User to modify + + workphone + GECOS field for the work phone + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' user.chworkphone foo 7735550123 ''' - return _update_gecos(name, 'workphone', workphone) + return _update_gecos(name, 'workphone', workphone, root=root) -def chhomephone(name, homephone): +def chhomephone(name, homephone, root=None): ''' Change the user's Home Phone + name + User to modify + + homephone + GECOS field for the home phone + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' user.chhomephone foo 7735551234 ''' - return _update_gecos(name, 'homephone', homephone) + return _update_gecos(name, 'homephone', homephone, root=root) -def chother(name, other): +def chother(name, other, root=None): ''' Change the user's other GECOS attribute + name + User to modify + + other + GECOS field for other information + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' user.chother foobar ''' - return _update_gecos(name, 'other', other) + return _update_gecos(name, 'other', other, root=root) def chloginclass(name, loginclass, root=None): ''' Change the default login class of the user + name + User to modify + + loginclass + Login class for the new account + + root + Directory to chroot into + .. note:: This function only applies to OpenBSD systems. @@ -546,25 +703,43 @@ def chloginclass(name, loginclass, root=None): cmd = ['usermod', '-L', loginclass, name] - if root is not None: + if root is not None and __grains__['kernel'] != 'AIX': cmd.extend(('-R', root)) __salt__['cmd.run'](cmd, python_shell=False) return get_loginclass(name) == loginclass -def info(name): +def info(name, root=None): ''' Return user information + name + User to get the information + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' user.info root ''' + # If root is provided, we use a less portable solution that + # depends on analyzing /etc/passwd manually. Of course we cannot + # find users from NIS nor LDAP, but in those cases do not makes + # sense to provide a root parameter. + # + # Please, note that if the non-root /etc/passwd file is long the + # iteration can be slow. + if root is not None and __grains__['kernel'] != 'AIX': + getpwnam = functools.partial(_getpwnam, root=root) + else: + getpwnam = functools.partial(pwd.getpwnam) + try: - data = pwd.getpwnam(_quote_username(name)) + data = getpwnam(_quote_username(name)) except KeyError: return {} else: @@ -575,6 +750,9 @@ def get_loginclass(name): ''' Get the login class of the user + name + User to get the information + .. note:: This function only applies to OpenBSD systems. @@ -632,6 +810,9 @@ def primary_group(name): .. versionadded:: 2016.3.0 + name + User to get the information + CLI Example: .. code-block:: bash @@ -645,6 +826,9 @@ def list_groups(name): ''' Return a list of groups the named user belongs to + name + User to get the information + CLI Example: .. code-block:: bash @@ -654,43 +838,79 @@ def list_groups(name): return salt.utils.user.get_group_list(name) -def list_users(): +def list_users(root=None): ''' Return a list of all users + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' user.list_users ''' - return sorted([user.pw_name for user in pwd.getpwall()]) + if root is not None and __grains__['kernel'] != 'AIX': + getpwall = functools.partial(_getpwall, root=root) + else: + getpwall = functools.partial(pwd.getpwall) + + return sorted([user.pw_name for user in getpwall()]) def rename(name, new_name, root=None): ''' Change the username for a named user + name + User to modify + + new_name + New value of the login name + + root + Directory to chroot into + CLI Example: .. code-block:: bash salt '*' user.rename name new_name ''' - current_info = info(name) - if not current_info: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) + if info(new_name, root=root): + raise CommandExecutionError('User \'{0}\' already exists'.format(new_name)) - new_info = info(new_name) - if new_info: - raise CommandExecutionError( - 'User \'{0}\' already exists'.format(new_name) - ) + return _chattrib(name, 'name', new_name, '-l', root=root) - cmd = ['usermod', '-l', new_name, name] - if root is not None and __grains__['kernel'] != 'AIX': - cmd.extend(('-R', root)) +def _getpwnam(name, root=None): + ''' + Alternative implementation for getpwnam, that use only /etc/passwd + ''' + root = '/' if not root else root + passwd = os.path.join(root, 'etc/passwd') + with salt.utils.files.fopen(passwd) as fp_: + for line in fp_: + line = salt.utils.stringutils.to_unicode(line) + comps = line.strip().split(':') + if comps[0] == name: + # Generate a getpwnam compatible output + comps[2], comps[3] = int(comps[2]), int(comps[3]) + return pwd.struct_passwd(comps) + raise KeyError - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('name') == new_name + +def _getpwall(root=None): + ''' + Alternative implemetantion for getpwall, that use only /etc/passwd + ''' + root = '/' if not root else root + passwd = os.path.join(root, 'etc/passwd') + with salt.utils.files.fopen(passwd) as fp_: + for line in fp_: + line = salt.utils.stringutils.to_unicode(line) + comps = line.strip().split(':') + # Generate a getpwall compatible output + comps[2], comps[3] = int(comps[2]), int(comps[3]) + yield pwd.struct_passwd(comps) diff --git a/salt/modules/virt.py b/salt/modules/virt.py index 8d63b6ff4c8b..f670703aa667 100644 --- a/salt/modules/virt.py +++ b/salt/modules/virt.py @@ -2435,7 +2435,9 @@ def get_xml(vm_, **kwargs): salt '*' virt.get_xml ''' conn = __get_conn(**kwargs) - xml_desc = _get_domain(conn, vm_).XMLDesc(0) + xml_desc = vm_.XMLDesc(0) if isinstance( + vm_, libvirt.virDomain + ) else _get_domain(conn, vm_).XMLDesc(0) conn.close() return xml_desc @@ -2725,7 +2727,7 @@ def ctrl_alt_del(vm_, **kwargs): def create_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name ''' - Start a domain based on the XML passed to the function + Start a transient domain based on the XML passed to the function :param xml: libvirt XML definition of the domain :param connection: libvirt connection URI, overriding defaults @@ -2752,7 +2754,7 @@ def create_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name def create_xml_path(path, **kwargs): ''' - Start a domain based on the XML-file path passed to the function + Start a transient domain based on the XML-file path passed to the function :param path: path to a file containing the libvirt XML definition of the domain :param connection: libvirt connection URI, overriding defaults @@ -2783,7 +2785,7 @@ def create_xml_path(path, **kwargs): def define_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name ''' - Define a domain based on the XML passed to the function + Define a persistent domain based on the XML passed to the function :param xml: libvirt XML definition of the domain :param connection: libvirt connection URI, overriding defaults @@ -2810,7 +2812,7 @@ def define_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name def define_xml_path(path, **kwargs): ''' - Define a domain based on the XML-file path passed to the function + Define a persistent domain based on the XML-file path passed to the function :param path: path to a file containing the libvirt XML definition of the domain :param connection: libvirt connection URI, overriding defaults @@ -3136,9 +3138,9 @@ def undefine(vm_, **kwargs): def purge(vm_, dirs=False, removables=None, **kwargs): ''' - Recursively destroy and delete a virtual machine, pass True for dir's to - also delete the directories containing the virtual machine disk images - - USE WITH EXTREME CAUTION! + Recursively destroy and delete a persistent virtual machine, pass True for + dir's to also delete the directories containing the virtual machine disk + images - USE WITH EXTREME CAUTION! Pass removables=False to avoid deleting cdrom and floppy images. To avoid disruption, the default but dangerous value is True. This will be changed @@ -3307,7 +3309,7 @@ def get_hypervisor(): # To add a new 'foo' hypervisor, add the _is_foo_hyper function, # add 'foo' to the list below and add it to the docstring with a .. versionadded:: hypervisors = ['kvm', 'xen'] - result = [hyper for hyper in hypervisors if getattr(sys.modules[__name__], '_is_{}_hyper').format(hyper)()] + result = [hyper for hyper in hypervisors if getattr(sys.modules[__name__], '_is_{}_hyper'.format(hyper))()] return result[0] if result else None @@ -4836,13 +4838,11 @@ def pool_undefine(name, **kwargs): conn.close() -def pool_delete(name, fast=True, **kwargs): +def pool_delete(name, **kwargs): ''' Delete the resources of a defined libvirt storage pool. :param name: libvirt storage pool name - :param fast: if set to False, zeroes out all the data. - Default value is True. :param connection: libvirt connection URI, overriding defaults :param username: username to connect with, overriding defaults :param password: password to connect with, overriding defaults @@ -4858,10 +4858,7 @@ def pool_delete(name, fast=True, **kwargs): conn = __get_conn(**kwargs) try: pool = conn.storagePoolLookupByName(name) - flags = libvirt.VIR_STORAGE_POOL_DELETE_NORMAL - if fast: - flags = libvirt.VIR_STORAGE_POOL_DELETE_ZEROED - return not bool(pool.delete(flags)) + return not bool(pool.delete(libvirt.VIR_STORAGE_POOL_DELETE_NORMAL)) finally: conn.close() diff --git a/salt/modules/vsphere.py b/salt/modules/vsphere.py index 96254e0591ad..edc7480532bd 100644 --- a/salt/modules/vsphere.py +++ b/salt/modules/vsphere.py @@ -42,6 +42,27 @@ The 5.5.0.2014.1.1 is a known stable version that this original vSphere Execution Module was developed against. +vSphere Automation SDK +---------------------- + +vSphere Automation SDK can be installed via pip: + +.. code-block:: bash + + pip install --upgrade pip setuptools + pip install --upgrade git+https://github.com/vmware/vsphere-automation-sdk-python.git + +.. note:: + + The SDK also requires OpenSSL 1.0.1+ if you want to connect to vSphere 6.5+ in order to support + TLS1.1 & 1.2. + + In order to use the tagging functions in this module, vSphere Automation SDK is necessary to + install. + +The module is currently in version 1.0.3 +(as of 8/26/2019) + ESXCLI ------ @@ -194,7 +215,6 @@ from salt.config.schemas.esxvm import ESXVirtualMachineDeleteSchema, \ ESXVirtualMachineUnregisterSchema - log = logging.getLogger(__name__) # Import Third Party Libs @@ -218,6 +238,27 @@ except ImportError: HAS_PYVMOMI = False +# vSphere SDK Automation +# pylint: disable=unused-import +try: + from com.vmware.cis.tagging_client import Category, CategoryModel + from com.vmware.cis.tagging_client import Tag, TagModel, TagAssociation + from com.vmware.vcenter_client import Cluster + from com.vmware.vapi.std_client import DynamicID + + # Error Handling + from com.vmware.vapi.std.errors_client import ( + AlreadyExists, InvalidArgument, + NotFound, Unauthenticated, Unauthorized + ) + vsphere_errors = (AlreadyExists, InvalidArgument, + NotFound, Unauthenticated, Unauthorized,) + HAS_VSPHERE_SDK = True +except ImportError: + HAS_VSPHERE_SDK = False +# pylint: enable=unused-import + +# ESXI esx_cli = salt.utils.path.which('esxcli') if esx_cli: HAS_ESX_CLI = True @@ -4167,7 +4208,7 @@ def _get_dvportgroup_teaming(pg_name, pg_default_port_config): def _get_dvportgroup_dict(pg_ref): ''' - Returns a dictionary with a distributed virutal portgroup data + Returns a dictionary with a distributed virtual portgroup data pg_ref @@ -9050,6 +9091,443 @@ def _delete_device(device): return device_spec +def _get_client(server, username, password): + ''' + Establish client through proxy or with user provided credentials. + + :param basestring server: + Target DNS or IP of vCenter center. + :param basestring username: + Username associated with the vCenter center. + :param basestring password: + Password associated with the vCenter center. + :returns: + vSphere Client instance. + :rtype: + vSphere.Client + ''' + # Get salted vSphere Client + if not (server and username and password): + # User didn't provide CLI args so use proxy information + details = __salt__['vcenter.get_details']() + server = details['vcenter'] + username = details['username'] + password = details['password'] + + # Establish connection with client + client = salt.utils.vmware.get_vsphere_client(server=server, + username=username, + password=password) + # Will return None if utility function causes Unauthenticated error + return client + + +@depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) +@supports_proxies('vcenter') +@gets_service_instance_via_proxy +def list_tag_categories(server=None, username=None, password=None, + service_instance=None): + ''' + List existing categories a user has access to. + + CLI Example: + + .. code-block:: bash + + salt vm_minion vsphere.list_tag_categories + + :param basestring server: + Target DNS or IP of vCenter center. + :param basestring username: + Username associated with the vCenter center. + :param basestring password: + Password associated with the vCenter center. + :returns: + Value(s) of category_id. + :rtype: + list of str + ''' + categories = None + client = _get_client(server, username, password) + + if client: + categories = client.tagging.Category.list() + return {'Categories': categories} + + +@depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) +@supports_proxies('vcenter') +@gets_service_instance_via_proxy +def list_tags(server=None, username=None, password=None, + service_instance=None): + ''' + List existing tags a user has access to. + + CLI Example: + + .. code-block:: bash + + salt vm_minion vsphere.list_tags + + :param basestring server: + Target DNS or IP of vCenter center. + :param basestring username: + Username associated with the vCenter center. + :param basestring password: + Password associated with the vCenter center. + :return: + Value(s) of tag_id. + :rtype: + list of str + ''' + tags = None + client = _get_client(server, username, password) + + if client: + tags = client.tagging.Tag.list() + return {'Tags': tags} + + +@depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) +@supports_proxies('vcenter') +@gets_service_instance_via_proxy +def attach_tag(object_id, tag_id, + managed_obj='ClusterComputeResource', + server=None, username=None, password=None, + service_instance=None): + ''' + Attach an existing tag to an input object. + + The tag needs to meet the cardinality (`CategoryModel.cardinality`) and + associability (`CategoryModel.associable_types`) criteria in order to be + eligible for attachment. If the tag is already attached to the object, + then this method is a no-op and an error will not be thrown. To invoke + this method, you need the attach tag privilege on the tag and the read + privilege on the object. + + CLI Example: + + .. code-block:: bash + + salt vm_minion vsphere.attach_tag domain-c2283 \ + urn:vmomi:InventoryServiceTag:b55ecc77-f4a5-49f8-ab52-38865467cfbe:GLOBAL + + :param str object_id: + The identifier of the input object. + :param str tag_id: + The identifier of the tag object. + :param str managed_obj: + Classes that contain methods for creating and deleting resources + typically contain a class attribute specifying the resource type + for the resources being created and deleted. + :param basestring server: + Target DNS or IP of vCenter center. + :param basestring username: + Username associated with the vCenter center. + :param basestring password: + Password associated with the vCenter center. + :return: + The list of all tag identifiers that correspond to the + tags attached to the given object. + :rtype: + list of tags + :raise: Unauthorized + if you do not have the privilege to read the object. + :raise: Unauthenticated + if the user can not be authenticated. + ''' + tag_attached = None + client = _get_client(server, username, password) + + if client: + # Create dynamic id object associated with a type and an id. + # Note, here the default is ClusterComputeResource, which + # infers a lazy loaded vim.ClusterComputerResource. + + # The ClusterComputeResource data object aggregates the compute + # resources of associated HostSystem objects into a single compute + # resource for use by virtual machines. + dynamic_id = DynamicID(type=managed_obj, id=object_id) + try: + tag_attached = client.tagging.TagAssociation.attach( + tag_id=tag_id, object_id=dynamic_id) + except vsphere_errors: + log.warning('Unable to attach tag. Check user privileges and' + ' object_id (must be a string).') + return {'Tag attached': tag_attached} + + +@depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) +@supports_proxies('vcenter') +@gets_service_instance_via_proxy +def list_attached_tags(object_id, + managed_obj='ClusterComputeResource', + server=None, username=None, password=None, + service_instance=None): + ''' + List existing tags a user has access to. + + CLI Example: + + .. code-block:: bash + + salt vm_minion vsphere.list_attached_tags domain-c2283 + + :param str object_id: + The identifier of the input object. + :param str managed_obj: + Classes that contain methods for creating and deleting resources + typically contain a class attribute specifying the resource type + for the resources being created and deleted. + :param basestring server: + Target DNS or IP of vCenter center. + :param basestring username: + Username associated with the vCenter center. + :param basestring password: + Password associated with the vCenter center. + :return: + The list of all tag identifiers that correspond to the + tags attached to the given object. + :rtype: + list of tags + :raise: Unauthorized + if you do not have the privilege to read the object. + :raise: Unauthenticated + if the user can not be authenticated. + ''' + attached_tags = None + client = _get_client(server, username, password) + + if client: + # Create dynamic id object associated with a type and an id. + # Note, here the default is ClusterComputeResource, which + # infers a lazy loaded vim.ClusterComputerResource. + + # The ClusterComputeResource data object aggregates the compute + # resources of associated HostSystem objects into a single compute + # resource for use by virtual machines. + dynamic_id = DynamicID(type=managed_obj, id=object_id) + try: + attached_tags = client.tagging.TagAssociation.list_attached_tags( + dynamic_id) + except vsphere_errors: + log.warning('Unable to list attached tags. Check user privileges' + ' and object_id (must be a string).') + return {'Attached tags': attached_tags} + + +@depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) +@supports_proxies('vcenter') +@gets_service_instance_via_proxy +def create_tag_category(name, description, cardinality, + server=None, username=None, password=None, + service_instance=None): + ''' + Create a category with given cardinality. + + CLI Example: + + .. code-block:: bash + + salt vm_minion vsphere.create_tag_category + + :param str name: + Name of tag category to create (ex. Machine, OS, Availability, etc.) + :param str description: + Given description of tag category. + :param str cardinality: + The associated cardinality (SINGLE, MULTIPLE) of the category. + :param basestring server: + Target DNS or IP of vCenter center. + :param basestring username: + Username associated with the vCenter center. + :param basestring password: + Password associated with the vCenter center. + :return: + Identifier of the created category. + :rtype: + str + :raise: AlreadyExists + if the name` provided in the create_spec is the name of an already + existing category. + :raise: InvalidArgument + if any of the information in the create_spec is invalid. + :raise: Unauthorized + if you do not have the privilege to create a category. + ''' + category_created = None + client = _get_client(server, username, password) + + if client: + if cardinality == 'SINGLE': + cardinality = CategoryModel.Cardinality.SINGLE + elif cardinality == 'MULTIPLE': + cardinality = CategoryModel.Cardinality.MULTIPLE + else: + # Cardinality must be supplied correctly + cardinality = None + + create_spec = client.tagging.Category.CreateSpec() + create_spec.name = name + create_spec.description = description + create_spec.cardinality = cardinality + associable_types = set() + create_spec.associable_types = associable_types + try: + category_created = client.tagging.Category.create(create_spec) + except vsphere_errors: + log.warning('Unable to create tag category. Check user privilege' + ' and see if category exists.') + return {'Category created': category_created} + + +@depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) +@supports_proxies('vcenter') +@gets_service_instance_via_proxy +def delete_tag_category(category_id, + server=None, username=None, password=None, + service_instance=None): + ''' + Delete a category. + + CLI Example: + + .. code-block:: bash + + salt vm_minion vsphere.delete_tag_category + + :param str category_id: + The identifier of category to be deleted. + The parameter must be an identifier for the resource type: + ``com.vmware.cis.tagging.Category``. + :param basestring server: + Target DNS or IP of vCenter center. + :param basestring username: + Username associated with the vCenter center. + :param basestring password: + Password associated with the vCenter center. + :raise: NotFound + if the tag for the given tag_id does not exist in the system. + :raise: Unauthorized + if you do not have the privilege to delete the tag. + :raise: Unauthenticated + if the user can not be authenticated. + ''' + category_deleted = None + client = _get_client(server, username, password) + + if client: + try: + category_deleted = client.tagging.Category.delete(category_id) + except vsphere_errors: + log.warning('Unable to delete tag category. Check user privilege' + ' and see if category exists.') + return {'Category deleted': category_deleted} + + +@depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) +@supports_proxies('vcenter') +@gets_service_instance_via_proxy +def create_tag(name, description, category_id, + server=None, username=None, password=None, + service_instance=None): + ''' + Create a tag under a category with given description. + + CLI Example: + + .. code-block:: bash + + salt vm_minion vsphere.create_tag + + :param basestring server: + Target DNS or IP of vCenter client. + :param basestring username: + Username associated with the vCenter client. + :param basestring password: + Password associated with the vCenter client. + :param str name: + Name of tag category to create (ex. Machine, OS, Availability, etc.) + :param str description: + Given description of tag category. + :param str category_id: + Value of category_id representative of the category created previously. + :return: + The identifier of the created tag. + :rtype: + str + :raise: AlreadyExists + if the name provided in the create_spec is the name of an already + existing tag in the input category. + :raise: InvalidArgument + if any of the input information in the create_spec is invalid. + :raise: NotFound + if the category for in the given create_spec does not exist in + the system. + :raise: Unauthorized + if you do not have the privilege to create tag. + ''' + tag_created = None + client = _get_client(server, username, password) + + if client: + create_spec = client.tagging.Tag.CreateSpec() + create_spec.name = name + create_spec.description = description + create_spec.category_id = category_id + try: + tag_created = client.tagging.Tag.create(create_spec) + except vsphere_errors: + log.warning('Unable to create tag. Check user privilege and see' + ' if category exists.') + return {'Tag created': tag_created} + + +@depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) +@supports_proxies('vcenter') +@gets_service_instance_via_proxy +def delete_tag(tag_id, + server=None, username=None, password=None, + service_instance=None): + ''' + Delete a tag. + + CLI Example: + + .. code-block:: bash + + salt vm_minion vsphere.delete_tag + + :param str tag_id: + The identifier of tag to be deleted. + The parameter must be an identifier for the resource type: + ``com.vmware.cis.tagging.Tag``. + :param basestring server: + Target DNS or IP of vCenter center. + :param basestring username: + Username associated with the vCenter center. + :param basestring password: + Password associated with the vCenter center. + :raise: AlreadyExists + if the name provided in the create_spec is the name of an already + existing category. + :raise: InvalidArgument + if any of the information in the create_spec is invalid. + :raise: Unauthorized + if you do not have the privilege to create a category. + ''' + tag_deleted = None + client = _get_client(server, username, password) + + if client: + try: + tag_deleted = client.tagging.Tag.delete(tag_id) + except vsphere_errors: + log.warning('Unable to delete category. Check user privileges' + ' and that category exists.') + return {'Tag deleted': tag_deleted} + + @depends(HAS_PYVMOMI) @supports_proxies('esxvm', 'esxcluster', 'esxdatacenter') @gets_service_instance_via_proxy diff --git a/salt/modules/win_iis.py b/salt/modules/win_iis.py index 661f1d8a5c44..73dc2305da59 100644 --- a/salt/modules/win_iis.py +++ b/salt/modules/win_iis.py @@ -13,12 +13,14 @@ from __future__ import absolute_import, print_function, unicode_literals import decimal import logging +import re import os +import yaml # Import salt libs import salt.utils.json import salt.utils.platform -from salt.ext.six.moves import range +from salt.ext.six.moves import range, map from salt.exceptions import SaltInvocationError, CommandExecutionError from salt.ext import six @@ -160,6 +162,45 @@ def _srvmgr(cmd, return_json=False): return ret +def _collection_match_to_index(pspath, colfilter, name, match): + ''' + Returns index of collection item matching the match dictionary. + ''' + collection = get_webconfiguration_settings(pspath, [{'name': name, 'filter': colfilter}])[0]['value'] + for idx, collect_dict in enumerate(collection): + if all(item in collect_dict.items() for item in match.items()): + return idx + return -1 + + +def _prepare_settings(pspath, settings): + ''' + Prepare settings before execution with get or set functions. + Removes settings with a match parameter when index is not found. + ''' + prepared_settings = [] + for setting in settings: + if setting.get('name', None) is None: + log.warning('win_iis: Setting has no name: {}'.format(setting)) + continue + if setting.get('filter', None) is None: + log.warning('win_iis: Setting has no filter: {}'.format(setting)) + continue + match = re.search(r'Collection\[(\{.*\})\]', setting['name']) + if match: + name = setting['name'][:match.start(1)-1] + match_dict = yaml.load(match.group(1)) + index = _collection_match_to_index(pspath, setting['filter'], name, match_dict) + if index == -1: + log.warning('win_iis: No match found for setting: {}'.format(setting)) + else: + setting['name'] = setting['name'].replace(match.group(1), str(index)) + prepared_settings.append(setting) + else: + prepared_settings.append(setting) + return prepared_settings + + def list_sites(): ''' List all the currently deployed websites. @@ -1985,3 +2026,167 @@ def set_webapp_settings(name, site, settings): log.debug('Settings configured successfully: {0}'.format(settings.keys())) return True + + +def get_webconfiguration_settings(name, settings): + r''' + Get the webconfiguration settings for the IIS PSPath. + + Args: + name (str): The PSPath of the IIS webconfiguration settings. + settings (list): A list of dictionaries containing setting name and filter. + + Returns: + dict: A list of dictionaries containing setting name, filter and value. + + CLI Example: + + .. code-block:: bash + + salt '*' win_iis.get_webconfiguration_settings name='IIS:\' settings="[{'name': 'enabled', 'filter': 'system.webServer/security/authentication/anonymousAuthentication'}]" + ''' + ret = {} + ps_cmd = [r'$Settings = New-Object System.Collections.ArrayList;'] + ps_cmd_validate = [] + settings = _prepare_settings(name, settings) + + if not settings: + log.warning('No settings provided') + return ret + + for setting in settings: + + # Build the commands to verify that the property names are valid. + + ps_cmd_validate.extend(['Get-WebConfigurationProperty', + '-PSPath', "'{0}'".format(name), + '-Filter', "'{0}'".format(setting['filter']), + '-Name', "'{0}'".format(setting['name']), + '-ErrorAction', 'Stop', + '|', 'Out-Null;']) + + # Some ItemProperties are Strings and others are ConfigurationAttributes. + # Since the former doesn't have a Value property, we need to account + # for this. + ps_cmd.append("$Property = Get-WebConfigurationProperty -PSPath '{0}'".format(name)) + ps_cmd.append("-Name '{0}' -Filter '{1}' -ErrorAction Stop;".format(setting['name'], setting['filter'])) + if setting['name'].split('.')[-1] == 'Collection': + if 'value' in setting: + ps_cmd.append("$Property = $Property | select -Property {0} ;" + .format(",".join(list(setting['value'][0].keys())))) + ps_cmd.append("$Settings.add(@{{filter='{0}';name='{1}';value=[System.Collections.ArrayList] @($Property)}})| Out-Null;" + .format(setting['filter'], setting['name'])) + else: + ps_cmd.append(r'if (([String]::IsNullOrEmpty($Property) -eq $False) -and') + ps_cmd.append(r"($Property.GetType()).Name -eq 'ConfigurationAttribute') {") + ps_cmd.append(r'$Property = $Property | Select-Object') + ps_cmd.append(r'-ExpandProperty Value };') + ps_cmd.append("$Settings.add(@{{filter='{0}';name='{1}';value=[String] $Property}})| Out-Null;" + .format(setting['filter'], setting['name'])) + ps_cmd.append(r'$Property = $Null;') + + # Validate the setting names that were passed in. + cmd_ret = _srvmgr(cmd=ps_cmd_validate, return_json=True) + + if cmd_ret['retcode'] != 0: + message = 'One or more invalid property names were specified for the provided container.' + raise SaltInvocationError(message) + + ps_cmd.append('$Settings') + cmd_ret = _srvmgr(cmd=ps_cmd, return_json=True) + + try: + ret = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + + except ValueError: + raise CommandExecutionError('Unable to parse return data as Json.') + + return ret + + +def set_webconfiguration_settings(name, settings): + r''' + Set the value of the setting for an IIS container. + + Args: + name (str): The PSPath of the IIS webconfiguration settings. + settings (list): A list of dictionaries containing setting name, filter and value. + + Returns: + bool: True if successful, otherwise False + + CLI Example: + + .. code-block:: bash + + salt '*' win_iis.set_webconfiguration_settings name='IIS:\' settings="[{'name': 'enabled', 'filter': 'system.webServer/security/authentication/anonymousAuthentication', 'value': False}]" + ''' + ps_cmd = [] + settings = _prepare_settings(name, settings) + + if not settings: + log.warning('No settings provided') + return False + + # Treat all values as strings for the purpose of comparing them to existing values. + for idx, setting in enumerate(settings): + if setting['name'].split('.')[-1] != 'Collection': + settings[idx]['value'] = six.text_type(setting['value']) + + current_settings = get_webconfiguration_settings( + name=name, settings=settings) + + if settings == current_settings: + log.debug('Settings already contain the provided values.') + return True + + for setting in settings: + # If the value is numeric, don't treat it as a string in PowerShell. + if setting['name'].split('.')[-1] != 'Collection': + try: + complex(setting['value']) + value = setting['value'] + except ValueError: + value = "'{0}'".format(setting['value']) + else: + configelement_list = [] + for value_item in setting['value']: + configelement_construct = [] + for key, value in value_item.items(): + configelement_construct.append("{0}='{1}'".format(key, value)) + configelement_list.append('@{' + ';'.join(configelement_construct) + '}') + value = ','.join(configelement_list) + + ps_cmd.extend(['Set-WebConfigurationProperty', + '-PSPath', "'{0}'".format(name), + '-Filter', "'{0}'".format(setting['filter']), + '-Name', "'{0}'".format(setting['name']), + '-Value', '{0};'.format(value)]) + + cmd_ret = _srvmgr(ps_cmd) + + if cmd_ret['retcode'] != 0: + msg = 'Unable to set settings for {0}'.format(name) + raise CommandExecutionError(msg) + + # Get the fields post-change so that we can verify tht all values + # were modified successfully. Track the ones that weren't. + new_settings = get_webconfiguration_settings( + name=name, settings=settings) + + failed_settings = [] + + for idx, setting in enumerate(settings): + + is_collection = setting['name'].split('.')[-1] == 'Collection' + + if ((not is_collection and six.text_type(setting['value']) != six.text_type(new_settings[idx]['value'])) + or (is_collection and list(map(dict, setting['value'])) != list(map(dict, new_settings[idx]['value'])))): + failed_settings.append(setting) + + if failed_settings: + log.error('Failed to change settings: %s', failed_settings) + return False + + log.debug('Settings configured successfully: %s', settings) + return True diff --git a/salt/modules/win_lgpo.py b/salt/modules/win_lgpo.py index 2d3fda61296a..aaff4cf1bfe0 100644 --- a/salt/modules/win_lgpo.py +++ b/salt/modules/win_lgpo.py @@ -4975,7 +4975,7 @@ def _buildElementNsmap(using_elements): return thisMap -def _get_audit_defaults(option=None): +def _get_advaudit_defaults(option=None): ''' Loads audit.csv defaults into a dict in __context__ called 'lgpo.audit_defaults'. The dictionary includes fieldnames and all @@ -5061,7 +5061,7 @@ def _get_audit_defaults(option=None): return __context__['lgpo.audit_defaults'] -def _findOptionValueAdvAudit(option): +def _get_advaudit_value(option): ''' Get the Advanced Auditing policy as configured in ``C:\\Windows\\Security\\Audit\\audit.csv`` @@ -5085,7 +5085,7 @@ def _findOptionValueAdvAudit(option): # If the GPO audit.csv exists, we'll use that one __salt__['file.copy'](f_audit_gpo, f_audit) else: - field_names = _get_audit_defaults('fieldnames') + field_names = _get_advaudit_defaults('fieldnames') # If the file doesn't exist anywhere, create it with default # fieldnames __salt__['file.makedirs'](f_audit) @@ -5177,7 +5177,7 @@ def _set_audit_file_data(option, value): # value is not None, write the new value log.debug('LGPO: Setting {0} to {1}' ''.format(option, value)) - defaults = _get_audit_defaults(option) + defaults = _get_advaudit_defaults(option) writer.writerow({ 'Machine Name': defaults['Machine Name'], 'Policy Target': defaults['Policy Target'], @@ -5201,7 +5201,7 @@ def _set_audit_file_data(option, value): return value_written -def _set_auditpol_data(option, value): +def _set_advaudit_pol_data(option, value): ''' Helper function that updates the current applied settings to match what has just been set in the audit.csv files. We're doing it this way instead of @@ -5219,13 +5219,13 @@ def _set_auditpol_data(option, value): '1': 'Success', '2': 'Failure', '3': 'Success and Failure'} - defaults = _get_audit_defaults(option) + defaults = _get_advaudit_defaults(option) return __utils__['auditpol.set_setting']( name=defaults['Auditpol Name'], value=auditpol_values[value]) -def _setOptionValueAdvAudit(option, value): +def _set_advaudit_value(option, value): ''' Helper function to update the Advanced Audit policy on the machine. This function modifies the two ``audit.csv`` files in the following locations: @@ -5249,7 +5249,7 @@ def _setOptionValueAdvAudit(option, value): raise CommandExecutionError('Failed to set audit.csv option: {0}' ''.format(option)) # Apply the settings locally - if not _set_auditpol_data(option=option, value=value): + if not _set_advaudit_pol_data(option=option, value=value): # Only log this error, it will be in effect the next time the machine # updates its policy log.debug('Failed to apply audit setting: {0}'.format(option)) @@ -5266,7 +5266,7 @@ def _setOptionValueAdvAudit(option, value): return True -def _findOptionValueNetSH(profile, option): +def _get_netsh_value(profile, option): if 'lgpo.netsh_data' not in __context__: __context__['lgpo.netsh_data'] = {} @@ -5280,7 +5280,7 @@ def _findOptionValueNetSH(profile, option): return __context__['lgpo.netsh_data'][profile][option] -def _setOptionValueNetSH(profile, section, option, value): +def _set_netsh_value(profile, section, option, value): if section not in ('firewallpolicy', 'settings', 'logging', 'state'): raise ValueError('LGPO: Invalid section: {0}'.format(section)) log.debug('LGPO: Setting the following\n' @@ -5300,17 +5300,20 @@ def _setOptionValueNetSH(profile, section, option, value): if section == 'state': salt.utils.win_lgpo_netsh.set_state( profile=profile, state=value, store='lgpo') + option = 'State' if section == 'logging': if option in ('FileName', 'MaxFileSize'): if value == 'Not configured': value = 'notconfigured' # Trim log for the two logging options + orig_option = option if option.startswith('Log'): option = option[3:] salt.utils.win_lgpo_netsh.set_logging_settings( profile=profile, setting=option, value=value, store='lgpo') - log.debug('LGPO: Clearing netsh data for {0} profile'.format(profile)) - __context__['lgpo.netsh_data'].pop(profile) + option = orig_option + log.debug('LGPO: Setting {0} for {1} profile'.format(option, profile)) + __context__['lgpo.netsh_data'][profile][option] = value return True @@ -5344,7 +5347,13 @@ def _get_secedit_data(): ''' if 'lgpo.secedit_data' not in __context__: log.debug('LGPO: Loading secedit data') - __context__['lgpo.secedit_data'] = _load_secedit_data() + data = _load_secedit_data() + secedit_data = {} + for line in data: + if '=' in line: + key, value = line.split('=') + secedit_data[key.strip()] = value.strip() + __context__['lgpo.secedit_data'] = secedit_data return __context__['lgpo.secedit_data'] @@ -5352,14 +5361,10 @@ def _get_secedit_value(option): ''' Helper function that looks for the passed option in the secedit data ''' - secedit_data = _get_secedit_data() - for _line in secedit_data: - if _line.startswith(option): - return _line.split('=')[1].strip() - return 'Not Defined' + return _get_secedit_data().get(option, 'Not Defined') -def _write_secedit_data(inf_data): +def _write_secedit_data(secedit_data): ''' Helper function to write secedit data to the database ''' @@ -5367,6 +5372,30 @@ def _write_secedit_data(inf_data): f_sdb = os.path.join(__opts__['cachedir'], 'secedit-{0}.sdb'.format(UUID)) f_inf = os.path.join(__opts__['cachedir'], 'secedit-{0}.inf'.format(UUID)) + # Generate inf data in this format: + # [Unicode] + # Unicode = yes + # [System Access] <==== Section + # EnableGuestAccount = 0 <==== value to set + # [Version] + # signature = "$CHICAGO$" + # Revision = 1 + + log.debug(secedit_data) + ini_data = ['[Unicode]', 'Unicode=yes'] + sections = ['System Access', + 'Event Audit', + 'Registry Values', + 'Privilege Rights'] + for section in sections: + if section in secedit_data: + ini_data.append('[{0}]'.format(section)) + ini_data.append(secedit_data[section][0]) + option, value = secedit_data[section][0].split('=') + ini_data.extend(['[Version]', 'signature="$CHICAGO$"', 'Revision=1']) + inf_data = os.linesep.join(ini_data) + log.debug('inf_data == %s', inf_data) + try: # Write the changes to the inf file __salt__['file.write'](f_inf, inf_data) @@ -5375,8 +5404,8 @@ def _write_secedit_data(inf_data): retcode = __salt__['cmd.retcode'](cmd) # Success if retcode == 0: - # Pop secedit data so it will always be current - __context__.pop('lgpo.secedit_data') + # Update __context__['lgpo.secedit_data'] + __context__['lgpo.secedit_data'][option.strip()] = value.strip() return True # Failure return False @@ -7730,12 +7759,12 @@ def get(policy_class=None, return_full_policy_names=True, class_vals[policy_name] = _val elif 'NetSH' in _pol: # get value from netsh - class_vals[policy_name] = _findOptionValueNetSH( + class_vals[policy_name] = _get_netsh_value( profile=_pol['NetSH']['Profile'], option=_pol['NetSH']['Option']) elif 'AdvAudit' in _pol: # get value from auditpol - class_vals[policy_name] = _findOptionValueAdvAudit( + class_vals[policy_name] = _get_advaudit_value( option=_pol['AdvAudit']['Option']) elif 'NetUserModal' in _pol: # get value from UserNetMod @@ -8193,18 +8222,7 @@ class in this module. raise SaltInvocationError(msg.format(lsaright)) if _secedits: # we've got secedits to make - log.debug(_secedits) - ini_data = '\r\n'.join(['[Unicode]', 'Unicode=yes']) - _seceditSections = ['System Access', 'Event Audit', 'Registry Values', 'Privilege Rights'] - for _seceditSection in _seceditSections: - if _seceditSection in _secedits: - ini_data = '\r\n'.join([ini_data, ''.join(['[', _seceditSection, ']']), - '\r\n'.join(_secedits[_seceditSection])]) - ini_data = '\r\n'.join([ini_data, '[Version]', - 'signature="$CHICAGO$"', - 'Revision=1']) - log.debug('ini_data == %s', ini_data) - if not _write_secedit_data(ini_data): + if not _write_secedit_data(_secedits): msg = ('Error while attempting to set policies via ' 'secedit. Some changes may not be applied as ' 'expected') @@ -8214,14 +8232,14 @@ class in this module. for setting in _netshs: log.debug('Setting firewall policy: {0}'.format(setting)) log.debug(_netshs[setting]) - _setOptionValueNetSH(**_netshs[setting]) + _set_netsh_value(**_netshs[setting]) if _advaudits: # We've got AdvAudit settings to make for setting in _advaudits: log.debug('Setting Advanced Audit policy: {0}'.format(setting)) log.debug(_advaudits[setting]) - _setOptionValueAdvAudit(**_advaudits[setting]) + _set_advaudit_value(**_advaudits[setting]) if _modal_sets: # we've got modalsets to make diff --git a/salt/modules/win_pkg.py b/salt/modules/win_pkg.py index 37af1f91183c..a1e3a055a2f5 100644 --- a/salt/modules/win_pkg.py +++ b/salt/modules/win_pkg.py @@ -95,6 +95,11 @@ def latest_version(*names, **kwargs): If the latest version of a given package is already installed, an empty string will be returned for that package. + .. note:: + Since this is looking for the latest version available, a refresh_db + will be triggered by default. This can take some time. To avoid this set + ``refresh`` to ``False``. + Args: names (str): A single or multiple names to lookup @@ -1447,6 +1452,11 @@ def install(name=None, refresh=False, pkgs=None, **kwargs): 'extra_install_flags': kwargs.get('extra_install_flags') } } + elif len(pkg_params) == 1: + # A dict of packages was passed, but it contains only 1 key, so we need + # to add the 'extra_install_flags' + pkg = next(iter(pkg_params)) + pkg_params[pkg]['extra_install_flags'] = kwargs.get('extra_install_flags') # Get a list of currently installed software for comparison at the end old = list_pkgs(saltenv=saltenv, refresh=refresh, versions_as_list=True) @@ -2149,6 +2159,13 @@ def _get_name_map(saltenv='base'): return u_name_map +def get_package_info(name, saltenv='base'): + ''' + Return package info. Returns empty map if package not available. + ''' + return _get_package_info(name=name, saltenv=saltenv) + + def _get_package_info(name, saltenv='base'): ''' Return package info. Returns empty map if package not available @@ -2183,7 +2200,7 @@ def _get_latest_pkg_version(pkginfo): def compare_versions(ver1='', oper='==', ver2=''): ''' - Compare software package versions + Compare software package versions. Made public for use with Jinja Args: ver1 (str): A software version to compare diff --git a/salt/modules/win_psget.py b/salt/modules/win_psget.py index c9a52846e043..2eb0e6fafc86 100644 --- a/salt/modules/win_psget.py +++ b/salt/modules/win_psget.py @@ -11,11 +11,10 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Python libs -import copy import logging +import xml.etree.ElementTree # Import Salt libs -import salt.utils.json import salt.utils.platform import salt.utils.versions from salt.exceptions import CommandExecutionError @@ -50,14 +49,42 @@ def __virtual__(): return __virtualname__ -def _pshell(cmd, cwd=None, json_depth=2): +def _ps_xml_to_dict(parent, dic=None): + ''' + Formats powershell Xml to a dict. + Note: This _ps_xml_to_dict is not perfect with powershell Xml. + ''' + + if dic is None: + dic = {} + + for child in parent: + if list(child): + new_dic = _ps_xml_to_dict(child, {}) + if "Name" in new_dic: + dic[new_dic["Name"]] = new_dic + else: + try: + dic[[name for ps_type, name in child.items() if ps_type == "Type"][0]] = new_dic + except IndexError: + dic[child.text] = new_dic + else: + for xml_type, name in child.items(): + if xml_type == "Name": + dic[name] = child.text + + return dic + + +def _pshell(cmd, cwd=None, depth=2): ''' Execute the desired powershell command and ensure that it returns data - in json format and load that into python + in Xml format and load that into python ''' - if 'convertto-json' not in cmd.lower(): - cmd = '{0} | ConvertTo-Json -Depth {1}'.format(cmd, json_depth) + + cmd = '{0} | ConvertTo-Xml -Depth {1} -As \"stream\"'.format(cmd, depth) log.debug('DSC: %s', cmd) + results = __salt__['cmd.run_all'](cmd, shell='powershell', cwd=cwd, python_shell=True) if 'pid' in results: @@ -68,9 +95,10 @@ def _pshell(cmd, cwd=None, json_depth=2): raise CommandExecutionError('Issue executing powershell {0}'.format(cmd), info=results) try: - ret = salt.utils.json.loads(results['stdout'], strict=False) - except ValueError: - raise CommandExecutionError('No JSON results from powershell', info=results) + ret = _ps_xml_to_dict(xml.etree.ElementTree.fromstring(results['stdout'].encode('utf-8'))) + except xml.etree.ElementTree.ParseError: + results['stdout'] = results['stdout'][:1000] + ". . ." + raise CommandExecutionError('No XML results from powershell', info=results) return ret @@ -86,8 +114,8 @@ def bootstrap(): salt 'win01' psget.bootstrap ''' - cmd = 'Get-PackageProvider -Name NuGet -ForceBootstrap' - ret = _pshell(cmd) + cmd = 'Get-PackageProvider -Name NuGet -ForceBootstrap | Select Name, Version, ProviderPath' + ret = _pshell(cmd, depth=1) return ret @@ -105,12 +133,13 @@ def avail_modules(desc=False): salt 'win01' psget.avail_modules salt 'win01' psget.avail_modules desc=True ''' - cmd = 'Find-Module' - modules = _pshell(cmd) + cmd = 'Find-Module | Select Name, Description' + modules = _pshell(cmd, depth=1) names = [] if desc: names = {} - for module in modules: + for key in modules: + module = modules[key] if desc: names[module['Name']] = module['Description'] continue @@ -134,19 +163,11 @@ def list_modules(desc=False): ''' cmd = 'Get-InstalledModule' modules = _pshell(cmd) - if isinstance(modules, dict): - ret = [] - if desc: - modules_ret = {} - modules_ret[modules['Name']] = copy.deepcopy(modules) - modules = modules_ret - return modules - ret.append(modules['Name']) - return ret names = [] if desc: names = {} - for module in modules: + for key in modules: + module = modules[key] if desc: names[module['Name']] = module continue diff --git a/salt/modules/win_status.py b/salt/modules/win_status.py index 048595761fd1..1a4604466b4a 100644 --- a/salt/modules/win_status.py +++ b/salt/modules/win_status.py @@ -580,10 +580,10 @@ def _win_remotes_on(port): # Connection to master is not as expected if master_connection_status is not connected: - event = salt.utils.event.get_event('minion', opts=__opts__, listen=False) - if master_connection_status: - event.fire_event({'master': master}, salt.minion.master_event(type='connected')) - else: - event.fire_event({'master': master}, salt.minion.master_event(type='disconnected')) + with salt.utils.event.get_event('minion', opts=__opts__, listen=False) as event_bus: + if master_connection_status: + event_bus.fire_event({'master': master}, salt.minion.master_event(type='connected')) + else: + event_bus.fire_event({'master': master}, salt.minion.master_event(type='disconnected')) return master_connection_status diff --git a/salt/modules/win_task.py b/salt/modules/win_task.py index 4f7e7a5c5d93..2e97ef94f283 100644 --- a/salt/modules/win_task.py +++ b/salt/modules/win_task.py @@ -155,9 +155,9 @@ 0x41306: 'Task was terminated by the user', 0x8004130F: 'Credentials became corrupted', 0x8004131F: 'An instance of this task is already running', + 0x800710E0: 'The operator or administrator has refused the request', 0x800704DD: 'The service is not available (Run only when logged ' 'in?)', - 0x800710E0: 'The operator or administrator has refused the request', 0xC000013A: 'The application terminated as a result of CTRL+C', 0xC06D007E: 'Unknown software exception'} @@ -2003,7 +2003,7 @@ def add_trigger(name=None, *MonthlyDay* - The task will run monthly an the specified day. + The task will run monthly on the specified day. months_of_year (list): Sets the months of the year during which the task runs. Should diff --git a/salt/modules/winrepo.py b/salt/modules/winrepo.py index 08397c954527..86570317ef1f 100644 --- a/salt/modules/winrepo.py +++ b/salt/modules/winrepo.py @@ -32,11 +32,8 @@ GLOBAL_ONLY ) from salt.ext import six -try: - import msgpack -except ImportError: - import msgpack_pure as msgpack # pylint: disable=import-error import salt.utils.gitfs +import salt.utils.msgpack as msgpack # pylint: enable=unused-import log = logging.getLogger(__name__) diff --git a/salt/modules/zcbuildout.py b/salt/modules/zcbuildout.py index efdf8c503983..1d3cc3759839 100644 --- a/salt/modules/zcbuildout.py +++ b/salt/modules/zcbuildout.py @@ -186,23 +186,20 @@ def by_level(self): LOG = _Logger() -def _encode_string(string): - if isinstance(string, six.text_type): - string = string.encode('utf-8') - return string - - def _encode_status(status): - status['out'] = _encode_string(status['out']) - status['outlog_by_level'] = _encode_string(status['outlog_by_level']) + if status['out'] is None: + status['out'] = None + else: + status['out'] = salt.utils.stringutils.to_unicode(status['out']) + status['outlog_by_level'] = salt.utils.stringutils.to_unicode(status['outlog_by_level']) if status['logs']: for i, data in enumerate(status['logs'][:]): - status['logs'][i] = (data[0], _encode_string(data[1])) + status['logs'][i] = (data[0], salt.utils.stringutils.to_unicode(data[1])) for logger in 'error', 'warn', 'info', 'debug': logs = status['logs_by_level'].get(logger, [])[:] if logs: for i, log in enumerate(logs): - status['logs_by_level'][logger][i] = _encode_string(log) + status['logs_by_level'][logger][i] = salt.utils.stringutils.to_unicode(log) return status @@ -222,7 +219,7 @@ def _set_status(m, if out and isinstance(out, six.string_types): outlog += HR outlog += 'OUTPUT:\n' - outlog += '{0}\n'.format(_encode_string(out)) + outlog += '{0}\n'.format(salt.utils.stringutils.to_unicode(out)) outlog += HR if m['logs']: outlog += HR @@ -233,13 +230,13 @@ def _set_status(m, outlog_by_level += HR for level, msg in m['logs']: outlog += '\n{0}: {1}\n'.format(level.upper(), - _encode_string(msg)) + salt.utils.stringutils.to_unicode(msg)) for logger in 'error', 'warn', 'info', 'debug': logs = m['logs_by_level'].get(logger, []) if logs: outlog_by_level += '\n{0}:\n'.format(logger.upper()) for idx, log in enumerate(logs[:]): - logs[idx] = _encode_string(log) + logs[idx] = salt.utils.stringutils.to_unicode(log) outlog_by_level += '\n'.join(logs) outlog_by_level += '\n' outlog += HR @@ -875,12 +872,12 @@ def _merge_statuses(statuses): status = BASE_STATUS.copy() status['status'] = None status['merged_statuses'] = True - status['out'] = [] + status['out'] = '' for st in statuses: if status['status'] is not False: status['status'] = st['status'] out = st['out'] - comment = _encode_string(st['comment']) + comment = salt.utils.stringutils.to_unicode(st['comment']) logs = st['logs'] logs_by_level = st['logs_by_level'] outlog_by_level = st['outlog_by_level'] @@ -890,32 +887,32 @@ def _merge_statuses(statuses): status['out'] = '' status['out'] += '\n' status['out'] += HR - out = _encode_string(out) + out = salt.utils.stringutils.to_unicode(out) status['out'] += '{0}\n'.format(out) status['out'] += HR if comment: if not status['comment']: status['comment'] = '' status['comment'] += '\n{0}\n'.format( - _encode_string(comment)) + salt.utils.stringutils.to_unicode(comment)) if outlog: if not status['outlog']: status['outlog'] = '' - outlog = _encode_string(outlog) + outlog = salt.utils.stringutils.to_unicode(outlog) status['outlog'] += '\n{0}'.format(HR) status['outlog'] += outlog if outlog_by_level: if not status['outlog_by_level']: status['outlog_by_level'] = '' status['outlog_by_level'] += '\n{0}'.format(HR) - status['outlog_by_level'] += _encode_string(outlog_by_level) + status['outlog_by_level'] += salt.utils.stringutils.to_unicode(outlog_by_level) status['logs'].extend([ - (a[0], _encode_string(a[1])) for a in logs]) + (a[0], salt.utils.stringutils.to_unicode(a[1])) for a in logs]) for log in logs_by_level: if log not in status['logs_by_level']: status['logs_by_level'][log] = [] status['logs_by_level'][log].extend( - [_encode_string(a) for a in logs_by_level[log]]) + [salt.utils.stringutils.to_unicode(a) for a in logs_by_level[log]]) return _encode_status(status) diff --git a/salt/modules/zpool.py b/salt/modules/zpool.py index 64de2194f92c..bfd879d352d9 100644 --- a/salt/modules/zpool.py +++ b/salt/modules/zpool.py @@ -9,8 +9,8 @@ :platform: illumos,freebsd,linux .. versionchanged:: 2018.3.1 - Big refactor to remove duplicate code, better type converions and improved - consistancy in output. + Big refactor to remove duplicate code, better type conversions and improved + consistency in output. ''' from __future__ import absolute_import, print_function, unicode_literals @@ -143,7 +143,7 @@ def status(zpool=None): ## parse status output # NOTE: output is 'key: value' except for the 'config' key. - # mulitple pools will repeat the output, so if switch pools if + # multiple pools will repeat the output, so if switch pools if # we see 'pool:' current_pool = None current_prop = None @@ -195,7 +195,7 @@ def status(zpool=None): # NOTE: data is indented by 1 tab, then multiples of 2 spaces # to differential root vdev, vdev, and dev # - # we just strip the intial tab (can't use .strip() here) + # we just strip the initial tab (can't use .strip() here) if line[0] == "\t": line = line[1:] @@ -372,7 +372,7 @@ def list_(properties='size,alloc,free,cap,frag,health', zpool=None, parsable=Tru .. note:: - Multiple storage pool can be provded as a space separated list + Multiple storage pool can be provided as a space separated list CLI Example: diff --git a/salt/netapi/rest_cherrypy/app.py b/salt/netapi/rest_cherrypy/app.py index 82615afb2977..988c8791fffc 100644 --- a/salt/netapi/rest_cherrypy/app.py +++ b/salt/netapi/rest_cherrypy/app.py @@ -820,9 +820,11 @@ def cors_tool(): resp_head['Connection'] = 'keep-alive' resp_head['Access-Control-Max-Age'] = '1400' - # CORS requests should short-circuit the other tools. - cherrypy.response.body = '' + # Note: CherryPy on Py3 uses binary objects for the response + # Python 2.6 also supports the byte prefix, so no need for conditionals + cherrypy.response.body = b'' cherrypy.response.status = 200 + # CORS requests should short-circuit the other tools. cherrypy.serving.request.handler = None # Needed to avoid the auth_tool check. diff --git a/salt/netapi/rest_tornado/saltnado.py b/salt/netapi/rest_tornado/saltnado.py index d6b367466505..d39beb2fa085 100644 --- a/salt/netapi/rest_tornado/saltnado.py +++ b/salt/netapi/rest_tornado/saltnado.py @@ -223,7 +223,7 @@ ) salt.utils.zeromq.install_zmq() -json = salt.utils.json.import_json() +_json = salt.utils.json.import_json() log = logging.getLogger(__name__) @@ -233,7 +233,7 @@ def _json_dumps(obj, **kwargs): salt.utils.json.import_json(). This ensures that we properly encode any strings in the object before we perform the serialization. ''' - return salt.utils.json.dumps(obj, _json_module=json, **kwargs) + return salt.utils.json.dumps(obj, _json_module=_json, **kwargs) # The clients rest_cherrypi supports. We want to mimic the interface, but not # necessarily use the same API under the hood diff --git a/salt/output/__init__.py b/salt/output/__init__.py index 3908a9e4b160..2c20ae580573 100644 --- a/salt/output/__init__.py +++ b/salt/output/__init__.py @@ -96,7 +96,7 @@ def display_output(data, out=None, opts=None, **kwargs): display_data = try_printout(data, out, opts, **kwargs) output_filename = opts.get('output_file', None) - log.trace('data = {0}'.format(data)) + log.trace('data = %s', data) try: # output filename can be either '' or None if output_filename: @@ -129,7 +129,7 @@ def display_output(data, out=None, opts=None, **kwargs): except IOError as exc: # Only raise if it's NOT a broken pipe if exc.errno != errno.EPIPE: - raise exc + six.reraise(*sys.exc_info()) def get_printout(out, opts=None, **kwargs): @@ -193,7 +193,10 @@ def is_pipe(): # Since the grains outputter was removed we don't need to fire this # error when old minions are asking for it if out != 'grains': - log.error('Invalid outputter {0} specified, fall back to nested'.format(out)) + log.error( + 'Invalid outputter %s specified, fall back to nested', + out, + ) return outputters['nested'] return outputters[out] diff --git a/salt/output/dson.py b/salt/output/dson.py new file mode 100644 index 000000000000..a337a02cf9ba --- /dev/null +++ b/salt/output/dson.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +''' +Display return data in DSON format +================================== + +This outputter is intended for demonstration purposes. Information on the DSON +spec can be found `here`__. + +.. __: http://vpzomtrrfrt.github.io/DSON/ + +This outputter requires `Dogeon`__ (installable via pip) + +.. __: https://github.com/soasme/dogeon +''' +from __future__ import absolute_import, print_function, unicode_literals + +# Import python libs +import logging + +# Import 3rd-party libs +try: + import dson +except ImportError: + dson = None + +from salt.ext import six + +log = logging.getLogger(__name__) + + +def __virtual__(): + if dson is None: + return (False, 'The dogeon Python package is not installed') + return True + + +def output(data, **kwargs): # pylint: disable=unused-argument + ''' + Print the output data in JSON + ''' + try: + dump_opts = {'indent': 4, 'default': repr} + + if 'output_indent' in __opts__: + + indent = __opts__.get('output_indent') + sort_keys = False + + if indent == 'pretty': + indent = 4 + sort_keys = True + + elif isinstance(indent, six.integer_types): + if indent >= 0: + indent = indent + else: + indent = None + + dump_opts['indent'] = indent + dump_opts['sort_keys'] = sort_keys + + return dson.dumps(data, **dump_opts) + + except UnicodeDecodeError as exc: + log.error('Unable to serialize output to dson') + return dson.dumps( + {'error': 'Unable to serialize output to DSON', + 'message': six.text_type(exc)} + ) + + except TypeError: + log.debug('An error occurred while outputting DSON', exc_info=True) + # Return valid JSON for unserializable objects + return dson.dumps({}) diff --git a/salt/output/json_out.py b/salt/output/json_out.py index e40c0baadc08..65ba33313a8c 100644 --- a/salt/output/json_out.py +++ b/salt/output/json_out.py @@ -28,6 +28,13 @@ {"mike": {"en0": {"hwaddr": "02:48:a2:4b:70:a0", ...}}} {"phill": {"en0": {"hwaddr": "02:1d:cc:a2:33:55", ...}}} {"stuart": {"en0": {"hwaddr": "02:9a:e0:ea:9e:3c", ...}}} + + +CLI Example: + +.. code-block:: bash + + salt '*' foo.bar --out=json ''' from __future__ import absolute_import, print_function, unicode_literals diff --git a/salt/output/newline_values_only.py b/salt/output/newline_values_only.py index 061764b4e053..cbe5f89d38c2 100644 --- a/salt/output/newline_values_only.py +++ b/salt/output/newline_values_only.py @@ -27,6 +27,10 @@ Example 1 ~~~~~~~~~ +.. code-block:: bash + + salt '*' foo.bar --out=newline_values_only + Input ----- @@ -40,7 +44,7 @@ Output ------ -.. code-block:: python +.. code-block:: text 127.0.0.1 10.0.0.1 @@ -50,6 +54,10 @@ Example 2 ~~~~~~~~~ +.. code-block:: bash + + salt '*' foo.bar --out=newline_values_only + Input ----- diff --git a/salt/output/no_out_quiet.py b/salt/output/no_out_quiet.py index 775acd40e4cf..8bd4b167ee33 100644 --- a/salt/output/no_out_quiet.py +++ b/salt/output/no_out_quiet.py @@ -4,6 +4,12 @@ ================= No output is produced when this outputter is selected + +CLI Example: + +.. code-block:: bash + + salt '*' foo.bar --out=quiet ''' # Define the module's virtual name diff --git a/salt/output/pony.py b/salt/output/pony.py index 2c8ba1f3cceb..85fb9ec6de52 100644 --- a/salt/output/pony.py +++ b/salt/output/pony.py @@ -39,6 +39,12 @@ █▄▄██████ █▄▄██████ █▄▄▄▄█ █▄▄▄▄█ + +CLI Example: + +.. code-block:: bash + + salt '*' foo.bar --out=pony ''' # Import Python libs diff --git a/salt/output/pprint_out.py b/salt/output/pprint_out.py index 01bee211fbe4..c2b4986ae1a7 100644 --- a/salt/output/pprint_out.py +++ b/salt/output/pprint_out.py @@ -6,7 +6,15 @@ The python pretty-print system was once the default outputter. It simply passes the return data through to ``pprint.pformat`` and prints the results. -Example output:: +CLI Example: + +.. code-block:: bash + + salt '*' foo.bar --out=pprint + +Example output: + +.. code-block:: python {'saltmine': {'foo': {'bar': 'baz', 'dictionary': {'abc': 123, 'def': 456}, diff --git a/salt/output/profile.py b/salt/output/profile.py index da1b39cc61d7..3d981c5a2999 100644 --- a/salt/output/profile.py +++ b/salt/output/profile.py @@ -5,7 +5,11 @@ Show profile data for returners that would normally show a highstate output. - salt MINION state.apply something --out=profile +CLI Example: + +.. code-block:: bash + + salt '*' state.apply something --out=profile Attempt to output the returns of state.sls and state.highstate as a table of names, modules and durations that looks somewhat like the following:: diff --git a/salt/output/raw.py b/salt/output/raw.py index 06972fc8e63d..57a4902c9e3c 100644 --- a/salt/output/raw.py +++ b/salt/output/raw.py @@ -11,8 +11,17 @@ This was the original outputter used by Salt before the outputter system was developed. -Example output:: +CLI Example: +.. code-block:: bash + + salt '*' foo.bar --out=raw + +Example output: + +.. code-block:: python + + salt '*' foo.bar --out=table {'myminion': {'foo': {'list': ['Hello', 'World'], 'bar': 'baz', 'dictionary': {'abc': 123, 'def': 456}}}} ''' diff --git a/salt/output/table_out.py b/salt/output/table_out.py index a71f21fa867a..459f9d763a8b 100644 --- a/salt/output/table_out.py +++ b/salt/output/table_out.py @@ -5,9 +5,11 @@ .. versionadded:: 2017.7.0 -This outputter displays a sequence of rows as table. +The ``table`` outputter displays a sequence of rows as table. -Example output:: +Example output: + +.. code-block:: text edge01.bjm01: ---------- @@ -32,6 +34,13 @@ ______________________________________________________________________________ result: ---------- + + +CLI Example: + +.. code-block:: bash + + salt '*' foo.bar --out=table ''' # Import Python libs diff --git a/salt/output/txt.py b/salt/output/txt.py index 14afc72007ec..595a04e9f682 100644 --- a/salt/output/txt.py +++ b/salt/output/txt.py @@ -3,9 +3,14 @@ Simple text outputter ===================== -The txt outputter has been developed to make the output from shell -commands on minions appear as they do when the command is executed -on the minion. +The ``txt`` outputter has been developed to make the output from shell commands +on minions appear as they do when the command is executed on the minion. + +CLI Example: + +.. code-block:: bash + + salt '*' foo.bar --out=txt ''' from __future__ import absolute_import, print_function, unicode_literals diff --git a/salt/output/yaml_out.py b/salt/output/yaml_out.py index 392f41f9b12b..ef3a5ace9cc6 100644 --- a/salt/output/yaml_out.py +++ b/salt/output/yaml_out.py @@ -5,7 +5,17 @@ This outputter defaults to printing in YAML block mode for better readability. -Example output:: +CLI Example: + +.. code-block:: bash + + salt '*' foo.bar --out=yaml + +Example output: + +CLI Example: + +.. code-block:: python saltmine: foo: diff --git a/salt/payload.py b/salt/payload.py index 8a370cce9598..8f625074156c 100644 --- a/salt/payload.py +++ b/salt/payload.py @@ -16,6 +16,7 @@ import salt.log import salt.transport.frame import salt.utils.immutabletypes as immutabletypes +import salt.utils.msgpack import salt.utils.stringutils from salt.exceptions import SaltReqTimeoutError, SaltDeserializationError from salt.utils.data import CaseInsensitiveDict @@ -30,63 +31,20 @@ log = logging.getLogger(__name__) -HAS_MSGPACK = False -try: - # Attempt to import msgpack - import msgpack - # There is a serialization issue on ARM and potentially other platforms - # for some msgpack bindings, check for it - if msgpack.version >= (0, 4, 0): - if msgpack.loads(msgpack.dumps([1, 2, 3], use_bin_type=False), use_list=True) is None: - raise ImportError - else: - if msgpack.loads(msgpack.dumps([1, 2, 3]), use_list=True) is None: - raise ImportError - HAS_MSGPACK = True -except ImportError: - # Fall back to msgpack_pure - try: - import msgpack_pure as msgpack # pylint: disable=import-error - HAS_MSGPACK = True - except ImportError: - # TODO: Come up with a sane way to get a configured logfile - # and write to the logfile when this error is hit also - LOG_FORMAT = '[%(levelname)-8s] %(message)s' - salt.log.setup_console_logger(log_format=LOG_FORMAT) - log.fatal('Unable to import msgpack or msgpack_pure python modules') - # Don't exit if msgpack is not available, this is to make local mode - # work without msgpack - #sys.exit(salt.defaults.exitcodes.EX_GENERIC) - - -if HAS_MSGPACK and not hasattr(msgpack, 'exceptions'): - class PackValueError(Exception): - ''' - older versions of msgpack do not have PackValueError - ''' - - class exceptions(object): - ''' - older versions of msgpack do not have an exceptions module - ''' - PackValueError = PackValueError() - - msgpack.exceptions = exceptions() - def package(payload): ''' This method for now just wraps msgpack.dumps, but it is here so that we can make the serialization a custom option in the future with ease. ''' - return msgpack.dumps(payload) + return salt.utils.msgpack.dumps(payload) def unpackage(package_): ''' Unpackages a payload ''' - return msgpack.loads(package_, use_list=True) + return salt.utils.msgpack.loads(package_, use_list=True) def format_payload(enc, **kwargs): @@ -142,12 +100,12 @@ def ext_type_decoder(code, data): gc.disable() # performance optimization for msgpack loads_kwargs = {'use_list': True, 'ext_hook': ext_type_decoder} - if msgpack.version >= (0, 4, 0): + if salt.utils.msgpack.version >= (0, 4, 0): # msgpack only supports 'encoding' starting in 0.4.0. # Due to this, if we don't need it, don't pass it at all so # that under Python 2 we can still work with older versions # of msgpack. - if msgpack.version >= (0, 5, 2): + if salt.utils.msgpack.version >= (0, 5, 2): if encoding is None: loads_kwargs['raw'] = True else: @@ -155,14 +113,14 @@ def ext_type_decoder(code, data): else: loads_kwargs['encoding'] = encoding try: - ret = msgpack.loads(msg, **loads_kwargs) + ret = salt.utils.msgpack.unpackb(msg, **loads_kwargs) except UnicodeDecodeError: # msg contains binary data loads_kwargs.pop('raw', None) loads_kwargs.pop('encoding', None) - ret = msgpack.loads(msg, **loads_kwargs) + ret = salt.utils.msgpack.loads(msg, **loads_kwargs) else: - ret = msgpack.loads(msg, **loads_kwargs) + ret = salt.utils.msgpack.loads(msg, **loads_kwargs) if six.PY3 and encoding is None and not raw: ret = salt.transport.frame.decode_embedded_strs(ret) except Exception as exc: @@ -216,7 +174,7 @@ def ext_type_encoder(obj): # msgpack doesn't support datetime.datetime and datetime.date datatypes. # So here we have converted these types to custom datatype # This is msgpack Extended types numbered 78 - return msgpack.ExtType(78, salt.utils.stringutils.to_bytes( + return salt.utils.msgpack.ExtType(78, salt.utils.stringutils.to_bytes( obj.strftime('%Y%m%dT%H:%M:%S.%f'))) # The same for immutable types elif isinstance(obj, immutabletypes.ImmutableDict): @@ -232,26 +190,25 @@ def ext_type_encoder(obj): return obj try: - if msgpack.version >= (0, 4, 0): - # msgpack only supports 'use_bin_type' starting in 0.4.0. - # Due to this, if we don't need it, don't pass it at all so - # that under Python 2 we can still work with older versions - # of msgpack. - return msgpack.dumps(msg, default=ext_type_encoder, use_bin_type=use_bin_type) - else: - return msgpack.dumps(msg, default=ext_type_encoder) - except (OverflowError, msgpack.exceptions.PackValueError): + return salt.utils.msgpack.packb(msg, default=ext_type_encoder, use_bin_type=use_bin_type) + except (OverflowError, salt.utils.msgpack.exceptions.PackValueError): # msgpack<=0.4.6 don't call ext encoder on very long integers raising the error instead. # Convert any very long longs to strings and call dumps again. - def verylong_encoder(obj): + def verylong_encoder(obj, context): + # Make sure we catch recursion here. + objid = id(obj) + if objid in context: + return ''.format(type(obj).__name__, id(obj)) + context.add(objid) + if isinstance(obj, dict): for key, value in six.iteritems(obj.copy()): - obj[key] = verylong_encoder(value) + obj[key] = verylong_encoder(value, context) return dict(obj) elif isinstance(obj, (list, tuple)): obj = list(obj) for idx, entry in enumerate(obj): - obj[idx] = verylong_encoder(entry) + obj[idx] = verylong_encoder(entry, context) return obj # A value of an Integer object is limited from -(2^63) upto (2^64)-1 by MessagePack # spec. Here we care only of JIDs that are positive integers. @@ -260,11 +217,8 @@ def verylong_encoder(obj): else: return obj - msg = verylong_encoder(msg) - if msgpack.version >= (0, 4, 0): - return msgpack.dumps(msg, default=ext_type_encoder, use_bin_type=use_bin_type) - else: - return msgpack.dumps(msg, default=ext_type_encoder) + msg = verylong_encoder(msg, set()) + return salt.utils.msgpack.packb(msg, default=ext_type_encoder, use_bin_type=use_bin_type) def dump(self, msg, fn_): ''' @@ -410,5 +364,7 @@ def destroy(self): if self.context.closed is False: self.context.term() + # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 diff --git a/salt/pillar/__init__.py b/salt/pillar/__init__.py index 13fed4593ae4..b7679fa734ff 100644 --- a/salt/pillar/__init__.py +++ b/salt/pillar/__init__.py @@ -197,8 +197,10 @@ def destroy(self): self._closing = True self.channel.close() + # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 class RemotePillar(RemotePillarMixin): @@ -262,8 +264,10 @@ def destroy(self): self._closing = True self.channel.close() + # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 class PillarCache(object): @@ -1122,8 +1126,10 @@ def destroy(self): return self._closing = True + # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 # TODO: actually migrate from Pillar to AsyncPillar to allow for futures in diff --git a/salt/pillar/consul_pillar.py b/salt/pillar/consul_pillar.py index e725fd4f750f..6903ccde2d8b 100644 --- a/salt/pillar/consul_pillar.py +++ b/salt/pillar/consul_pillar.py @@ -187,10 +187,10 @@ def ext_pillar(minion_id, if minion_id not in minions: return {} - root_re = re.compile('root=(\S*)') # pylint: disable=W1401 + root_re = re.compile('(?`, by using he +set of minions when :mod:`running states `, by using the ``pillarenv`` argument. The CLI pillarenv will override one set in the minion config file. So, assuming that a pillarenv of ``base`` was set for a minion, it would not get any of the pillar variables configured in the ``qux`` remote, diff --git a/salt/proxy/napalm.py b/salt/proxy/napalm.py index 91f4fc4c4951..bc82309cbf14 100644 --- a/salt/proxy/napalm.py +++ b/salt/proxy/napalm.py @@ -7,7 +7,7 @@ Proxy minion for managing network devices via NAPALM_ library. -:codeauthor: Mircea Ulinic & Jerome Fleury +:codeauthor: Mircea Ulinic & Jerome Fleury :maturity: new :depends: napalm :platform: unix diff --git a/salt/proxy/vcenter.py b/salt/proxy/vcenter.py index 2adcba6adec0..d09d9416efef 100644 --- a/salt/proxy/vcenter.py +++ b/salt/proxy/vcenter.py @@ -202,6 +202,7 @@ except ImportError: HAS_JSONSCHEMA = False + # Variables are scoped to this module so we can have persistent data # across calls to fns in here. DETAILS = {} @@ -278,10 +279,11 @@ def init(opts): 'mehchanism \'userpass\'') try: username, password = find_credentials() - DETAILS['password'] = password except salt.exceptions.SaltSystemExit as err: log.critical('Error: %s', err) return False + else: + DETAILS['password'] = password return True diff --git a/salt/queues/pgjsonb_queue.py b/salt/queues/pgjsonb_queue.py index 7dfc5e79a56a..0a40f77f5ebd 100644 --- a/salt/queues/pgjsonb_queue.py +++ b/salt/queues/pgjsonb_queue.py @@ -95,7 +95,7 @@ def _conn(commit=False): error = err.args sys.stderr.write(six.text_type(error)) cursor.execute("ROLLBACK") - raise err + six.reraise(*sys.exc_info()) else: if commit: cursor.execute("COMMIT") diff --git a/salt/renderers/dson.py b/salt/renderers/dson.py index a7e1f50303ba..6adf99acf145 100644 --- a/salt/renderers/dson.py +++ b/salt/renderers/dson.py @@ -30,7 +30,7 @@ def __virtual__(): if dson is None: - return False, 'Failed to load: dson module not installed' + return (False, 'The dogeon Python package is not installed') return True diff --git a/salt/renderers/msgpack.py b/salt/renderers/msgpack.py index f58d11b85b8d..eceac4f53bb5 100644 --- a/salt/renderers/msgpack.py +++ b/salt/renderers/msgpack.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals -# Import third party libs -import msgpack - # Import salt libs +import salt.utils.msgpack from salt.ext import six @@ -28,4 +26,4 @@ def render(msgpack_data, saltenv='base', sls='', **kws): msgpack_data = msgpack_data[(msgpack_data.find('\n') + 1):] if not msgpack_data.strip(): return {} - return msgpack.loads(msgpack_data) + return salt.utils.msgpack.loads(msgpack_data) diff --git a/salt/returners/hipchat_return.py b/salt/returners/hipchat_return.py deleted file mode 100644 index a777a7322803..000000000000 --- a/salt/returners/hipchat_return.py +++ /dev/null @@ -1,400 +0,0 @@ -# -*- coding: utf-8 -*- -''' -Return salt data via hipchat. - -.. versionadded:: 2015.5.0 - -The following fields can be set in the minion conf file:: - - hipchat.room_id (required) - hipchat.api_key (required) - hipchat.api_version (required) - hipchat.api_url (optional) - hipchat.from_name (required) - hipchat.color (optional) - hipchat.notify (optional) - hipchat.profile (optional) - hipchat.url (optional) - -.. note:: - - When using Hipchat's API v2, ``api_key`` needs to be assigned to the room with the - "Label" set to what you would have been set in the hipchat.from_name field. The v2 - API disregards the ``from_name`` in the data sent for the room notification and uses - the Label assigned through the Hipchat control panel. - -Alternative configuration values can be used by prefacing the configuration. -Any values not found in the alternative configuration will be pulled from -the default location:: - - hipchat.room_id - hipchat.api_key - hipchat.api_version - hipchat.api_url - hipchat.from_name - -Hipchat settings may also be configured as: - -.. code-block:: yaml - - hipchat: - room_id: RoomName - api_url: https://hipchat.myteam.con - api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - api_version: v1 - from_name: user@email.com - - alternative.hipchat: - room_id: RoomName - api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - api_version: v1 - from_name: user@email.com - - hipchat_profile: - hipchat.api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - hipchat.api_version: v1 - hipchat.from_name: user@email.com - - hipchat: - profile: hipchat_profile - room_id: RoomName - - alternative.hipchat: - profile: hipchat_profile - room_id: RoomName - - hipchat: - room_id: RoomName - api_key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - api_version: v1 - api_url: api.hipchat.com - from_name: user@email.com - -To use the HipChat returner, append '--return hipchat' to the salt command. - -.. code-block:: bash - - salt '*' test.ping --return hipchat - -To use the alternative configuration, append '--return_config alternative' to the salt command. - -.. versionadded:: 2015.5.0 - -.. code-block:: bash - - salt '*' test.ping --return hipchat --return_config alternative - -To override individual configuration items, append --return_kwargs '{"key:": "value"}' to the salt command. - -.. versionadded:: 2016.3.0 - -.. code-block:: bash - - salt '*' test.ping --return hipchat --return_kwargs '{"room_id": "another-room"}' - -''' -from __future__ import absolute_import, print_function, unicode_literals - -# Import Python libs -import pprint -import logging - -# pylint: disable=import-error,no-name-in-module -from salt.ext import six -from salt.ext.six.moves.urllib.parse import urljoin as _urljoin -from salt.ext.six.moves.urllib.parse import urlencode as _urlencode -import salt.ext.six.moves.http_client -# pylint: enable=import-error - -# Import Salt Libs -import salt.returners -import salt.utils.json - - -log = logging.getLogger(__name__) -__virtualname__ = 'hipchat' - - -def _get_options(ret=None): - ''' - Get the hipchat options from salt. - ''' - - defaults = {'color': 'yellow', - 'notify': False, - 'api_url': 'api.hipchat.com'} - - attrs = {'hipchat_profile': 'profile', - 'room_id': 'room_id', - 'from_name': 'from_name', - 'api_key': 'api_key', - 'api_version': 'api_version', - 'color': 'color', - 'notify': 'notify', - 'api_url': 'api_url', - } - - profile_attr = 'hipchat_profile' - - profile_attrs = {'from_jid': 'from_jid', - 'api_key': 'api_key', - 'api_version': 'api_key', - 'api_url': 'api_url', - } - - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - profile_attr=profile_attr, - profile_attrs=profile_attrs, - __salt__=__salt__, - __opts__=__opts__, - defaults=defaults) - return _options - - -def __virtual__(): - ''' - Return virtual name of the module. - - :return: The virtual name of the module. - ''' - return __virtualname__ - - -def _query(function, - api_key=None, - api_version=None, - room_id=None, - api_url=None, - method='GET', - data=None): - ''' - HipChat object method function to construct and execute on the API URL. - - :param api_url: The HipChat API URL. - :param api_key: The HipChat api key. - :param function: The HipChat api function to perform. - :param api_version: The HipChat api version (v1 or v2). - :param method: The HTTP method, e.g. GET or POST. - :param data: The data to be sent for POST method. - :return: The json response from the API call or False. - ''' - headers = {} - query_params = {} - - if room_id: - room_id = 'room/{0}/notification'.format(six.text_type(room_id)) - else: - room_id = 'room/0/notification' - - hipchat_functions = { - 'v1': { - 'rooms': { - 'request': 'rooms/list', - 'response': 'rooms', - }, - 'users': { - 'request': 'users/list', - 'response': 'users', - }, - 'message': { - 'request': 'rooms/message', - 'response': 'status', - }, - }, - 'v2': { - 'rooms': { - 'request': 'room', - 'response': 'items', - }, - 'users': { - 'request': 'user', - 'response': 'items', - }, - 'message': { - 'request': room_id, - 'response': None, - }, - }, - } - - api_url = 'https://{0}'.format(api_url) - base_url = _urljoin(api_url, api_version + '/') - path = hipchat_functions.get(api_version).get(function).get('request') - url = _urljoin(base_url, path, False) - - if api_version == 'v1': - query_params['format'] = 'json' - query_params['auth_token'] = api_key - - if method == 'POST': - headers['Content-Type'] = 'application/x-www-form-urlencoded' - - if data: - if data.get('notify'): - data['notify'] = 1 - else: - data['notify'] = 0 - data = _urlencode(data) - elif api_version == 'v2': - headers['Content-Type'] = 'application/json' - headers['Authorization'] = 'Bearer {0}'.format(api_key) - if data: - data = salt.utils.json.dumps(data) - else: - log.error('Unsupported HipChat API version') - return False - - result = salt.utils.http.query( - url, - method, - params=query_params, - data=data, - decode=True, - status=True, - header_dict=headers, - opts=__opts__, - ) - - if result.get('status', None) == salt.ext.six.moves.http_client.OK: - response = hipchat_functions.get(api_version).get(function).get('response') - return result.get('dict', {}).get(response, None) - elif result.get('status', None) == salt.ext.six.moves.http_client.NO_CONTENT: - return False - else: - log.debug(url) - log.debug(query_params) - log.debug(data) - log.debug(result) - if result.get('error'): - log.error(result) - return False - - -def _send_message(room_id, - message, - from_name, - api_key=None, - api_version=None, - api_url=None, - color=None, - notify=False): - ''' - Send a message to a HipChat room. - :param room_id: The room id or room name, either will work. - :param message: The message to send to the HipChat room. - :param from_name: Specify who the message is from. - :param api_url: The HipChat API URL, if not specified in the configuration. - :param api_key: The HipChat api key, if not specified in the configuration. - :param api_version: The HipChat api version, if not specified in the configuration. - :param color: The color for the message, default: yellow. - :param notify: Whether to notify the room, default: False. - :return: Boolean if message was sent successfully. - ''' - - parameters = dict() - parameters['room_id'] = room_id - parameters['from'] = from_name[:15] - parameters['message'] = message[:10000] - parameters['message_format'] = 'text' - parameters['color'] = color - parameters['notify'] = notify - - result = _query(function='message', - api_key=api_key, - api_version=api_version, - room_id=room_id, - api_url=api_url, - method='POST', - data=parameters) - - if result: - return True - else: - return False - - -def _verify_options(options): - ''' - Verify Hipchat options and log warnings - - Returns True if all options can be verified, - otherwise False - ''' - if not options.get('room_id'): - log.error('hipchat.room_id not defined in salt config') - return False - - if not options.get('from_name'): - log.error('hipchat.from_name not defined in salt config') - return False - - if not options.get('api_key'): - log.error('hipchat.api_key not defined in salt config') - return False - - if not options.get('api_version'): - log.error('hipchat.api_version not defined in salt config') - return False - - return True - - -def returner(ret): - ''' - Send an hipchat message with the return data from a job - ''' - - _options = _get_options(ret) - - if not _verify_options(_options): - return - - message = ('id: {0}\r\n' - 'function: {1}\r\n' - 'function args: {2}\r\n' - 'jid: {3}\r\n' - 'return: {4}\r\n').format( - ret.get('id'), - ret.get('fun'), - ret.get('fun_args'), - ret.get('jid'), - pprint.pformat(ret.get('return'))) - - if ret.get('retcode') == 0: - color = _options.get('color') - else: - color = 'red' - - hipchat = _send_message(_options.get('room_id'), # room_id - message, # message - _options.get('from_name'), # from_name - api_key=_options.get('api_key'), - api_version=_options.get('api_version'), - api_url=_options.get('api_url'), - color=color, - notify=_options.get('notify')) - - return hipchat - - -def event_return(events): - ''' - Return event data to hipchat - ''' - _options = _get_options() - - for event in events: - # TODO: - # Pre-process messages to apply individualized colors for various - # event types. - log.trace('Hipchat returner received event: %s', event) - _send_message(_options.get('room_id'), # room_id - event['data'], # message - _options.get('from_name'), # from_name - api_key=_options.get('api_key'), - api_version=_options.get('api_version'), - api_url=_options.get('api_url'), - color=_options.get('color'), - notify=_options.get('notify')) diff --git a/salt/returners/local_cache.py b/salt/returners/local_cache.py index c18a0834a3ff..dde9d5aeb1fc 100644 --- a/salt/returners/local_cache.py +++ b/salt/returners/local_cache.py @@ -20,11 +20,11 @@ import salt.utils.files import salt.utils.jid import salt.utils.minions +import salt.utils.msgpack import salt.utils.stringutils import salt.exceptions # Import 3rd-party libs -import msgpack from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin @@ -520,7 +520,7 @@ def save_reg(data): raise try: with salt.utils.files.fopen(regfile, 'a') as fh_: - msgpack.dump(data, fh_) + salt.utils.msgpack.dump(data, fh_) except Exception: log.error('Could not write to msgpack file %s', __opts__['outdir']) raise @@ -534,7 +534,7 @@ def load_reg(): regfile = os.path.join(reg_dir, 'register') try: with salt.utils.files.fopen(regfile, 'r') as fh_: - return msgpack.load(fh_) + return salt.utils.msgpack.load(fh_) except Exception: log.error('Could not write to msgpack file %s', __opts__['outdir']) raise diff --git a/salt/returners/mysql.py b/salt/returners/mysql.py index fbaaacfe0dee..1289bf0d2d75 100644 --- a/salt/returners/mysql.py +++ b/salt/returners/mysql.py @@ -46,7 +46,8 @@ Should you wish the returner data to be cleaned out every so often, set `keep_jobs` to the number of hours for the jobs to live in the tables. -Setting it to `0` or leaving it unset will cause the data to stay in the tables. +Setting it to `0` will cause the data to stay in the tables. The default +setting for `keep_jobs` is set to `24`. Should you wish to archive jobs in a different table for later processing, set `archive_jobs` to True. Salt will create 3 archive tables @@ -278,7 +279,7 @@ def _get_serv(ret=None, commit=False): error = err.args sys.stderr.write(six.text_type(error)) cursor.execute("ROLLBACK") - raise err + six.reraise(*sys.exc_info()) else: if commit: cursor.execute("COMMIT") diff --git a/salt/returners/pgjsonb.py b/salt/returners/pgjsonb.py index 04f2af58c050..e34e2fb8119e 100644 --- a/salt/returners/pgjsonb.py +++ b/salt/returners/pgjsonb.py @@ -275,7 +275,7 @@ def _get_serv(ret=None, commit=False): error = err.args sys.stderr.write(six.text_type(error)) cursor.execute("ROLLBACK") - raise err + six.reraise(*sys.exc_info()) else: if commit: cursor.execute("COMMIT") diff --git a/salt/returners/postgres.py b/salt/returners/postgres.py index 9f67ab1a0442..6c87752ea65d 100644 --- a/salt/returners/postgres.py +++ b/salt/returners/postgres.py @@ -210,7 +210,7 @@ def _get_serv(ret=None, commit=False): error = err.args sys.stderr.write(six.text_type(error)) cursor.execute("ROLLBACK") - raise err + six.reraise(*sys.exc_info()) else: if commit: cursor.execute("COMMIT") diff --git a/salt/returners/slack_webhook_return.py b/salt/returners/slack_webhook_return.py new file mode 100644 index 000000000000..aad1cdf656ab --- /dev/null +++ b/salt/returners/slack_webhook_return.py @@ -0,0 +1,338 @@ +# -*- coding: utf-8 -*- +''' +Return salt data via Slack using Incoming Webhooks + +:codeauthor: `Carlos D. Álvaro ` + +The following fields can be set in the minion conf file: + +.. code-block:: none + + slack_webhook.webhook (required, the webhook id. Just the part after: 'https://hooks.slack.com/services/') + slack_webhook.success_title (optional, short title for succeeded states. By default: '{id} | Succeeded') + slack_webhook.failure_title (optional, short title for failed states. By default: '{id} | Failed') + slack_webhook.author_icon (optional, a URL that with a small 16x16px image. Must be of type: GIF, JPEG, PNG, and BMP) + slack_webhook.show_tasks (optional, show identifiers for changed and failed tasks. By default: False) + +Alternative configuration values can be used by prefacing the configuration. +Any values not found in the alternative configuration will be pulled from +the default location: + +.. code-block:: none + + slack_webhook.webhook + slack_webhook.success_title + slack_webhook.failure_title + slack_webhook.author_icon + slack_webhook.show_tasks + +Slack settings may also be configured as: + +.. code-block:: none + + slack_webhook: + webhook: T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX + success_title: [{id}] | Success + failure_title: [{id}] | Failure + author_icon: https://platform.slack-edge.com/img/default_application_icon.png + show_tasks: true + + alternative.slack_webhook: + webhook: T00000000/C00000000/YYYYYYYYYYYYYYYYYYYYYYYY + show_tasks: false + +To use the Slack returner, append '--return slack_webhook' to the salt command. + +.. code-block:: bash + + salt '*' test.ping --return slack_webhook + +To use the alternative configuration, append '--return_config alternative' to the salt command. + +.. code-block:: bash + + salt '*' test.ping --return slack_webhook --return_config alternative + +''' +from __future__ import absolute_import, print_function, unicode_literals + +# Import Python libs +import logging +import json + +# pylint: disable=import-error,no-name-in-module,redefined-builtin +import salt.ext.six.moves.http_client +from salt.ext.six.moves.urllib.parse import urlencode as _urlencode +from salt.ext import six +from salt.ext.six.moves import map +from salt.ext.six.moves import range +# pylint: enable=import-error,no-name-in-module,redefined-builtin + +# Import Salt Libs +import salt.returners +import salt.utils.http +import salt.utils.yaml + +log = logging.getLogger(__name__) + +__virtualname__ = 'slack_webhook' + + +def _get_options(ret=None): + ''' + Get the slack_webhook options from salt. + :param ret: Salt return dictionary + :return: A dictionary with options + ''' + + defaults = { + 'success_title': '{id} | Succeeded', + 'failure_title': '{id} | Failed', + 'author_icon': '', + 'show_tasks': False + } + + attrs = { + 'webhook': 'webhook', + 'success_title': 'success_title', + 'failure_title': 'failure_title', + 'author_icon': 'author_icon', + 'show_tasks': 'show_tasks' + } + + _options = salt.returners.get_returner_options(__virtualname__, + ret, + attrs, + __salt__=__salt__, + __opts__=__opts__, + defaults=defaults) + return _options + + +def __virtual__(): + ''' + Return virtual name of the module. + + :return: The virtual name of the module. + ''' + return __virtualname__ + + +def _sprinkle(config_str): + ''' + Sprinkle with grains of salt, that is + convert 'test {id} test {host} ' types of strings + :param config_str: The string to be sprinkled + :return: The string sprinkled + ''' + parts = [x for sub in config_str.split('{') for x in sub.split('}')] + for i in range(1, len(parts), 2): + parts[i] = six.text_type(__grains__.get(parts[i], '')) + return ''.join(parts) + + +def _format_task(task): + ''' + Return a dictionary with the task ready for slack fileds + :param task: The name of the task + + :return: A dictionary ready to be inserted in Slack fields array + ''' + return {'value': task, 'short': False} + + +def _generate_payload(author_icon, title, report): + ''' + Prepare the payload for Slack + :param author_icon: The url for the thumbnail to be displayed + :param title: The title of the message + :param report: A dictionary with the report of the Salt function + :return: The payload ready for Slack + ''' + + title = _sprinkle(title) + + unchanged = { + 'color': 'good', + 'title': 'Unchanged: {unchanged}'.format(unchanged=report['unchanged'].get('counter', None)) + } + + changed = { + 'color': 'warning', + 'title': 'Changed: {changed}'.format(changed=report['changed'].get('counter', None)) + } + + if report['changed'].get('tasks'): + changed['fields'] = list( + map(_format_task, report['changed'].get('tasks'))) + + failed = { + 'color': 'danger', + 'title': 'Failed: {failed}'.format(failed=report['failed'].get('counter', None)) + } + + if report['failed'].get('tasks'): + failed['fields'] = list( + map(_format_task, report['failed'].get('tasks'))) + + text = 'Function: {function}\n'.format(function=report.get('function')) + if report.get('arguments'): + text += 'Function Args: {arguments}\n'.format( + arguments=str(list(map(str, report.get('arguments'))))) + + text += 'JID: {jid}\n'.format(jid=report.get('jid')) + text += 'Total: {total}\n'.format(total=report.get('total')) + text += 'Duration: {duration:.2f} secs'.format( + duration=float(report.get('duration'))) + + payload = { + 'attachments': [ + { + 'fallback': title, + 'color': "#272727", + 'author_name': _sprinkle('{id}'), + 'author_link': _sprinkle('{localhost}'), + 'author_icon': author_icon, + 'title': 'Success: {success}'.format(success=str(report.get('success'))), + 'text': text + }, + unchanged, + changed, + failed + ] + } + + return payload + + +def _generate_report(ret, show_tasks): + ''' + Generate a report of the Salt function + :param ret: The Salt return + :param show_tasks: Flag to show the name of the changed and failed states + :return: The report + ''' + + returns = ret.get('return') + + sorted_data = sorted( + returns.items(), + key=lambda s: s[1].get('__run_num__', 0) + ) + + total = 0 + failed = 0 + changed = 0 + duration = 0.0 + + changed_tasks = [] + failed_tasks = [] + + # gather stats + for state, data in sorted_data: + # state: module, stateid, name, function + _, stateid, _, _ = state.split('_|-') + task = '{filename}.sls | {taskname}'.format( + filename=str(data.get('__sls__')), taskname=stateid) + + if not data.get('result', True): + failed += 1 + failed_tasks.append(task) + + if data.get('changes', {}): + changed += 1 + changed_tasks.append(task) + + total += 1 + try: + duration += float(data.get('duration', 0.0)) + except ValueError: + pass + + unchanged = total - failed - changed + + log.debug('%s total: %s', __virtualname__, total) + log.debug('%s failed: %s', __virtualname__, failed) + log.debug('%s unchanged: %s', __virtualname__, unchanged) + log.debug('%s changed: %s', __virtualname__, changed) + + report = { + 'id': ret.get('id'), + 'success': True if failed == 0 else False, + 'total': total, + 'function': ret.get('fun'), + 'arguments': ret.get('fun_args', []), + 'jid': ret.get('jid'), + 'duration': duration / 1000, + 'unchanged': { + 'counter': unchanged + }, + 'changed': { + 'counter': changed, + 'tasks': changed_tasks if show_tasks else [] + }, + 'failed': { + 'counter': failed, + 'tasks': failed_tasks if show_tasks else [] + } + } + + return report + + +def _post_message(webhook, author_icon, title, report): + ''' + Send a message to a Slack room through a webhook + :param webhook: The url of the incoming webhook + :param author_icon: The thumbnail image to be displayed on the right side of the message + :param title: The title of the message + :param report: The report of the function state + :return: Boolean if message was sent successfully + ''' + + payload = _generate_payload(author_icon, title, report) + + data = _urlencode({ + 'payload': json.dumps(payload, ensure_ascii=False) + }) + + webhook_url = 'https://hooks.slack.com/services/{webhook}'.format(webhook=webhook) + query_result = salt.utils.http.query(webhook_url, 'POST', data=data) + + if query_result['body'] == 'ok' or query_result['status'] <= 201: + return True + else: + log.error('Slack incoming webhook message post result: %s', query_result) + return { + 'res': False, + 'message': query_result.get('body', query_result['status']) + } + + +def returner(ret): + ''' + Send a slack message with the data through a webhook + :param ret: The Salt return + :return: The result of the post + ''' + + _options = _get_options(ret) + + webhook = _options.get('webhook', None) + show_tasks = _options.get('show_tasks') + author_icon = _options.get('author_icon') + + if not webhook or webhook is '': + log.error('%s.webhook not defined in salt config', __virtualname__) + return + + report = _generate_report(ret, show_tasks) + + if report.get('success'): + title = _options.get('success_title') + else: + title = _options.get('failure_title') + + slack = _post_message(webhook, author_icon, title, report) + + return slack diff --git a/salt/runner.py b/salt/runner.py index 3e3657e8eaa6..25aa6702b5c6 100644 --- a/salt/runner.py +++ b/salt/runner.py @@ -41,9 +41,11 @@ class RunnerClient(mixins.SyncClientMixin, mixins.AsyncClientMixin, object): client = 'runner' tag_prefix = 'run' - def __init__(self, opts): + def __init__(self, opts, context=None): self.opts = opts - self.context = {} + if context is None: + context = {} + self.context = context @property def functions(self): @@ -160,9 +162,9 @@ class Runner(RunnerClient): ''' Execute the salt runner interface ''' - def __init__(self, opts): - super(Runner, self).__init__(opts) - self.returners = salt.loader.returners(opts, self.functions) + def __init__(self, opts, context=None): + super(Runner, self).__init__(opts, context=context) + self.returners = salt.loader.returners(opts, self.functions, context=context) self.outputters = salt.loader.outputters(opts) def print_docs(self): @@ -270,14 +272,14 @@ def run(self): async_pub['jid'], daemonize=False) except salt.exceptions.SaltException as exc: - evt = salt.utils.event.get_event('master', opts=self.opts) - evt.fire_event({'success': False, - 'return': '{0}'.format(exc), - 'retcode': 254, - 'fun': self.opts['fun'], - 'fun_args': fun_args, - 'jid': self.jid}, - tag='salt/run/{0}/ret'.format(self.jid)) + with salt.utils.event.get_event('master', opts=self.opts) as evt: + evt.fire_event({'success': False, + 'return': '{0}'.format(exc), + 'retcode': 254, + 'fun': self.opts['fun'], + 'fun_args': fun_args, + 'jid': self.jid}, + tag='salt/run/{0}/ret'.format(self.jid)) # Attempt to grab documentation if 'fun' in low: ret = self.get_docs('{0}*'.format(low['fun'])) diff --git a/salt/runners/queue.py b/salt/runners/queue.py index 3440335b7f2e..aad005d99dd0 100644 --- a/salt/runners/queue.py +++ b/salt/runners/queue.py @@ -206,24 +206,24 @@ def process_queue(queue, quantity=1, backend='sqlite', is_runner=False): salt-run queue.process_queue myqueue all backend=sqlite ''' # get ready to send an event - event = get_event( + with get_event( 'master', __opts__['sock_dir'], __opts__['transport'], opts=__opts__, - listen=False) - try: - items = pop(queue=queue, quantity=quantity, backend=backend, is_runner=is_runner) - except SaltInvocationError as exc: - error_txt = '{0}'.format(exc) - __jid_event__.fire_event({'errors': error_txt}, 'progress') - return False - - data = {'items': items, - 'backend': backend, - 'queue': queue, - } - event.fire_event(data, tagify([queue, 'process'], prefix='queue')) + listen=False) as event_bus: + try: + items = pop(queue=queue, quantity=quantity, backend=backend, is_runner=is_runner) + except SaltInvocationError as exc: + error_txt = '{0}'.format(exc) + __jid_event__.fire_event({'errors': error_txt}, 'progress') + return False + + data = {'items': items, + 'backend': backend, + 'queue': queue, + } + event_bus.fire_event(data, tagify([queue, 'process'], prefix='queue')) return data diff --git a/salt/runners/saltutil.py b/salt/runners/saltutil.py index 6f6af53dea38..f80e865f29cc 100644 --- a/salt/runners/saltutil.py +++ b/salt/runners/saltutil.py @@ -64,6 +64,7 @@ def sync_all(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): ret['tops'] = sync_tops(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) ret['tokens'] = sync_eauth_tokens(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) ret['serializers'] = sync_serializers(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) + ret['executors'] = sync_executors(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) return ret @@ -607,3 +608,29 @@ def sync_serializers(saltenv='base', extmod_whitelist=None, extmod_blacklist=Non ''' return salt.utils.extmods.sync(__opts__, 'serializers', saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist)[0] + + +def sync_executors(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): + ''' + .. versionadded:: Neon + + Sync executor modules from ``salt://_executors`` to the master + + saltenv : base + The fileserver environment from which to sync. To sync from more than + one environment, pass a comma-separated list. + + extmod_whitelist : None + comma-seperated list of modules to sync + + extmod_blacklist : None + comma-seperated list of modules to blacklist based on type + + CLI Example: + + .. code-block:: bash + + salt-run saltutil.sync_executors + ''' + return salt.utils.extmods.sync(__opts__, 'executors', saltenv=saltenv, extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist)[0] diff --git a/salt/runners/winrepo.py b/salt/runners/winrepo.py index 480a3138b66b..321b91638eb8 100644 --- a/salt/runners/winrepo.py +++ b/salt/runners/winrepo.py @@ -12,15 +12,12 @@ # Import third party libs from salt.ext import six -try: - import msgpack -except ImportError: - import msgpack_pure as msgpack # pylint: disable=import-error # Import salt libs from salt.exceptions import CommandExecutionError, SaltRenderError import salt.utils.files import salt.utils.gitfs +import salt.utils.msgpack import salt.utils.path import logging import salt.minion @@ -124,7 +121,7 @@ def genrepo(opts=None, fire_event=True): ret.setdefault('name_map', {}).update(revmap) with salt.utils.files.fopen( os.path.join(winrepo_dir, winrepo_cachefile), 'w+b') as repo: - repo.write(msgpack.dumps(ret)) + repo.write(salt.utils.msgpack.dumps(ret)) return ret diff --git a/salt/sdb/sqlite3.py b/salt/sdb/sqlite3.py index 540a289d56ae..006d574c283b 100644 --- a/salt/sdb/sqlite3.py +++ b/salt/sdb/sqlite3.py @@ -54,11 +54,9 @@ HAS_SQLITE3 = False # Import salt libs +import salt.utils.msgpack from salt.ext import six -# Import third party libs -import msgpack - DEFAULT_TABLE = 'sdb' @@ -126,9 +124,9 @@ def set_(key, value, profile=None): return False conn, cur, table = _connect(profile) if six.PY2: - value = buffer(msgpack.packb(value)) + value = buffer(salt.utils.msgpack.packb(value)) else: - value = memoryview(msgpack.packb(value)) + value = memoryview(salt.utils.msgpack.packb(value)) q = profile.get('set_query', ('INSERT OR REPLACE INTO {0} VALUES ' '(:key, :value)').format(table)) conn.execute(q, {'key': key, 'value': value}) @@ -149,4 +147,4 @@ def get(key, profile=None): res = res.fetchone() if not res: return None - return msgpack.unpackb(res[0]) + return salt.utils.msgpack.unpackb(res[0]) diff --git a/salt/serializers/msgpack.py b/salt/serializers/msgpack.py index f55fa878b669..7f545b2bb801 100644 --- a/salt/serializers/msgpack.py +++ b/salt/serializers/msgpack.py @@ -12,41 +12,17 @@ import logging # Import Salt Libs -from salt.log import setup_console_logger +import salt.utils.msgpack from salt.serializers import DeserializationError, SerializationError # Import 3rd-party libs from salt.ext import six log = logging.getLogger(__name__) - - -try: - # Attempt to import msgpack - import msgpack - # There is a serialization issue on ARM and potentially other platforms - # for some msgpack bindings, check for it - if msgpack.loads(msgpack.dumps([1, 2, 3]), use_list=True) is None: - raise ImportError - available = True -except ImportError: - # Fall back to msgpack_pure - try: - import msgpack_pure as msgpack # pylint: disable=import-error - except ImportError: - # TODO: Come up with a sane way to get a configured logfile - # and write to the logfile when this error is hit also - LOG_FORMAT = '[%(levelname)-8s] %(message)s' - setup_console_logger(log_format=LOG_FORMAT) - log.fatal('Unable to import msgpack or msgpack_pure python modules') - # Don't exit if msgpack is not available, this is to make local mode - # work without msgpack - #sys.exit(salt.defaults.exitcodes.EX_GENERIC) - available = False +available = salt.utils.msgpack.HAS_MSGPACK if not available: - def _fail(): raise RuntimeError('msgpack is not available') @@ -56,11 +32,11 @@ def _serialize(obj, **options): def _deserialize(stream_or_string, **options): _fail() -elif msgpack.version >= (0, 2, 0): +elif salt.utils.msgpack.version >= (0, 2, 0): def _serialize(obj, **options): try: - return msgpack.dumps(obj, **options) + return salt.utils.msgpack.dumps(obj, **options) except Exception as error: raise SerializationError(error) @@ -68,7 +44,7 @@ def _deserialize(stream_or_string, **options): try: options.setdefault('use_list', True) options.setdefault('encoding', 'utf-8') - return msgpack.loads(stream_or_string, **options) + return salt.utils.msgpack.loads(stream_or_string, **options) except Exception as error: raise DeserializationError(error) @@ -95,14 +71,14 @@ def _decoder(obj): def _serialize(obj, **options): try: obj = _encoder(obj) - return msgpack.dumps(obj, **options) + return salt.utils.msgpack.dumps(obj, **options) except Exception as error: raise SerializationError(error) def _deserialize(stream_or_string, **options): options.setdefault('use_list', True) try: - obj = msgpack.loads(stream_or_string) + obj = salt.utils.msgpack.loads(stream_or_string) return _decoder(obj) except Exception as error: raise DeserializationError(error) diff --git a/salt/state.py b/salt/state.py index dfa9c6de5893..0b58375badb5 100644 --- a/salt/state.py +++ b/salt/state.py @@ -40,6 +40,7 @@ import salt.utils.files import salt.utils.hashutils import salt.utils.immutabletypes as immutabletypes +import salt.utils.msgpack import salt.utils.platform import salt.utils.process import salt.utils.url @@ -56,7 +57,6 @@ import salt.utils.yamlloader as yamlloader # Import third party libs -import msgpack # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext import six from salt.ext.six.moves import map, range, reload_module @@ -957,7 +957,7 @@ def _load_states(self): self.states = salt.loader.thorium(self.opts, self.functions, {}) # TODO: Add runners, proxy? else: self.states = salt.loader.states(self.opts, self.functions, self.utils, - self.serializers, proxy=self.proxy) + self.serializers, context=self.state_con, proxy=self.proxy) def load_modules(self, data=None, proxy=None): ''' @@ -991,7 +991,7 @@ def load_modules(self, data=None, proxy=None): self.serializers = salt.loader.serializers(self.opts) self._load_states() self.rend = salt.loader.render(self.opts, self.functions, - states=self.states, proxy=self.proxy) + states=self.states, proxy=self.proxy, context=self.state_con) def module_refresh(self): ''' @@ -1794,7 +1794,7 @@ def call_parallel(self, cdata, low): if not name: name = low.get('name', low.get('__id__')) - proc = salt.utils.process.MultiprocessingProcess( + proc = salt.utils.process.Process( target=self._call_parallel_target, args=(name, cdata, low)) proc.start() @@ -2054,24 +2054,96 @@ def __eval_slot(self, slot): 'test.arg(\'arg\', kw=\'kwarg\')') return slot log.debug('Calling slot: %s(%s, %s)', fun, args, kwargs) - return self.functions[fun](*args, **kwargs) + slot_return = self.functions[fun](*args, **kwargs) + + # Given input __slot__:salt:test.arg(somekey="value").not.exist ~ /appended + # slot_text should be __slot...).not.exist + # append_data should be ~ /appended + slot_text = fmt[2].split('~')[0] + append_data = fmt[2].split('~', 1)[1:] + log.debug('slot_text: %s', slot_text) + log.debug('append_data: %s', append_data) + + # Support parsing slot dict response + # return_get should result in a kwargs.nested.dict path by getting + # everything after first closing paren: ) + return_get = None + try: + return_get = slot_text[slot_text.rindex(')')+1:] + except ValueError: + pass + if return_get: + #remove first period + return_get = return_get.split('.', 1)[1].strip() + log.debug('Searching slot result %s for %s', slot_return, return_get) + slot_return = salt.utils.data.traverse_dict_and_list(slot_return, + return_get, + default=None, + delimiter='.' + ) + + if append_data: + if isinstance(slot_return, six.string_types): + # Append text to slot string result + append_data = ' '.join(append_data).strip() + log.debug('appending to slot result: %s', append_data) + slot_return += append_data + else: + log.error('Ignoring slot append, slot result is not a string') + + return slot_return def format_slots(self, cdata): ''' Read in the arguments from the low level slot syntax to make a last minute runtime call to gather relevant data for the specific routine + + Will parse strings, first level of dictionary values, and strings and + first level dict values inside of lists ''' # __slot__:salt.cmd.run(foo, bar, baz=qux) + SLOT_TEXT = '__slot__:' ctx = (('args', enumerate(cdata['args'])), ('kwargs', cdata['kwargs'].items())) for atype, avalues in ctx: for ind, arg in avalues: arg = salt.utils.data.decode(arg, keep=True) - if not isinstance(arg, six.text_type) \ - or not arg.startswith('__slot__:'): + if isinstance(arg, dict): + # Search dictionary values for __slot__: + for key, value in arg.items(): + try: + if value.startswith(SLOT_TEXT): + log.trace("Slot processsing dict value %s", value) + cdata[atype][ind][key] = self.__eval_slot(value) + except AttributeError: + # Not a string/slot + continue + elif isinstance(arg, list): + for idx, listvalue in enumerate(arg): + log.trace("Slot processing list value: %s", listvalue) + if isinstance(listvalue, dict): + # Search dict values in list for __slot__: + for key, value in listvalue.items(): + try: + if value.startswith(SLOT_TEXT): + log.trace("Slot processsing nested dict value %s", value) + cdata[atype][ind][idx][key] = self.__eval_slot(value) + except AttributeError: + # Not a string/slot + continue + if isinstance(listvalue, six.text_type): + # Search strings in a list for __slot__: + if listvalue.startswith(SLOT_TEXT): + log.trace("Slot processsing nested string %s", listvalue) + cdata[atype][ind][idx] = self.__eval_slot(listvalue) + elif isinstance(arg, six.text_type) \ + and arg.startswith(SLOT_TEXT): + # Search strings for __slot__: + log.trace("Slot processsing %s", arg) + cdata[atype][ind] = self.__eval_slot(arg) + else: # Not a slot, skip it continue - cdata[atype][ind] = self.__eval_slot(arg) def verify_retry_data(self, retry_data): ''' @@ -2188,7 +2260,7 @@ def check_pause(self, low): with salt.utils.files.fopen(pause_path, 'rb') as fp_: try: pdat = msgpack_deserialize(fp_.read()) - except msgpack.UnpackValueError: + except salt.utils.msgpack.exceptions.UnpackValueError: # Reading race condition if tries > 10: # Break out if there are a ton of read errors @@ -4138,7 +4210,7 @@ def load_modules(self, data=None, proxy=None): self.utils = salt.loader.utils(self.opts) self.serializers = salt.loader.serializers(self.opts) self.states = salt.loader.states(self.opts, self.functions, self.utils, self.serializers) - self.rend = salt.loader.render(self.opts, self.functions, states=self.states) + self.rend = salt.loader.render(self.opts, self.functions, states=self.states, context=self.state_con) class MasterHighState(HighState): @@ -4190,5 +4262,7 @@ def destroy(self): self._closing = True self.channel.close() + # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 diff --git a/salt/states/acme.py b/salt/states/acme.py index 9ecc1fee284d..b4c0101bd824 100644 --- a/salt/states/acme.py +++ b/salt/states/acme.py @@ -29,6 +29,9 @@ from __future__ import absolute_import, print_function, unicode_literals import logging +# Import salt libs +import salt.utils.dictdiffer + log = logging.getLogger(__name__) @@ -88,74 +91,52 @@ def cert(name, :param dns_plugin: Name of a DNS plugin to use (currently only 'cloudflare') :param dns_plugin_credentials: Path to the credentials file if required by the specified DNS plugin ''' + ret = {'name': name, 'result': 'changeme', 'comment': [], 'changes': {}} + action = None - if __opts__['test']: - ret = { - 'name': name, - 'changes': {}, - 'result': None - } - window = None - try: - window = int(renew) - except Exception: - pass - - comment = 'Certificate {0} '.format(name) - if not __salt__['acme.has'](name): - comment += 'would have been obtained' - elif __salt__['acme.needs_renewal'](name, window): - comment += 'would have been renewed' - else: - comment += 'would not have been touched' - ret['result'] = True - ret['comment'] = comment - return ret - + current_certificate = {} + new_certificate = {} if not __salt__['acme.has'](name): - old = None - else: - old = __salt__['acme.info'](name) - - res = __salt__['acme.cert']( - name, - aliases=aliases, - email=email, - webroot=webroot, - certname=certname, - test_cert=test_cert, - renew=renew, - keysize=keysize, - server=server, - owner=owner, - group=group, - mode=mode, - preferred_challenges=preferred_challenges, - tls_sni_01_port=tls_sni_01_port, - tls_sni_01_address=tls_sni_01_address, - http_01_port=http_01_port, - http_01_address=http_01_address, - dns_plugin=dns_plugin, - dns_plugin_credentials=dns_plugin_credentials, - ) - - ret = { - 'name': name, - 'result': res['result'] is not False, - 'comment': res['comment'] - } - - if res['result'] is None: - ret['changes'] = {} + action = 'obtain' + elif __salt__['acme.needs_renewal'](name, renew): + action = 'renew' + current_certificate = __salt__['acme.info'](name) else: - if not __salt__['acme.has'](name): - new = None + ret['result'] = True + ret['comment'].append('Certificate {} exists and does not need renewal.' + ''.format(name)) + + if action: + if __opts__['test']: + ret['result'] = None + ret['comment'].append('Certificate {} would have been {}ed.' + ''.format(name, action)) + ret['changes'] = {'old': 'current certificate', 'new': 'new certificate'} else: - new = __salt__['acme.info'](name) - - ret['changes'] = { - 'old': old, - 'new': new - } - + res = __salt__['acme.cert']( + name, + aliases=aliases, + email=email, + webroot=webroot, + certname=certname, + test_cert=test_cert, + renew=renew, + keysize=keysize, + server=server, + owner=owner, + group=group, + mode=mode, + preferred_challenges=preferred_challenges, + tls_sni_01_port=tls_sni_01_port, + tls_sni_01_address=tls_sni_01_address, + http_01_port=http_01_port, + http_01_address=http_01_address, + dns_plugin=dns_plugin, + dns_plugin_credentials=dns_plugin_credentials, + ) + ret['result'] = res['result'] + ret['comment'].append(res['comment']) + if ret['result']: + new_certificate = __salt__['acme.info'](name) + ret['changes'] = salt.utils.dictdiffer.deep_diff(current_certificate, new_certificate) return ret diff --git a/salt/states/archive.py b/salt/states/archive.py index 61ec3d79227f..53f9bc271698 100644 --- a/salt/states/archive.py +++ b/salt/states/archive.py @@ -183,6 +183,7 @@ def extracted(name, force=False, overwrite=False, clean=False, + clean_parent=False, user=None, group=None, if_missing=None, @@ -412,6 +413,10 @@ def extracted(name, Set this to ``True`` if archive should be extracted if source_hash has changed. This would extract regardless of the ``if_missing`` parameter. + Note that this is only checked if the ``source`` value has not changed. + If it has (e.g. to increment a version number in the path) then the + archive will not be extracted even if the hash has changed. + .. versionadded:: 2016.3.0 skip_verify : False @@ -522,6 +527,14 @@ def extracted(name, .. versionadded:: 2016.11.1 + clean_parent : False + If ``True``, and the archive is extracted, delete the parent + directory (i.e. the directory into which the archive is extracted), and + then re-create that directory before extracting. Note that ``clean`` + and ``clean_parent`` are mutually exclusive. + + .. versionadded:: Sodium + user The user to own each extracted file. Not available on Windows. @@ -1076,6 +1089,11 @@ def extracted(name, )) return ret + if clean and clean_parent: + ret['comment'] = "Only one of 'clean' and 'clean_parent' can be set to True" + ret['result'] = False + return ret + extraction_needed = overwrite contents_missing = False @@ -1148,6 +1166,15 @@ def extracted(name, ) ) return ret + if __opts__['test'] and clean_parent and contents is not None: + ret['result'] = None + ret['comment'] += ( + ' Since the \'clean_parent\' option is enabled, the ' + 'destination parent directory would be removed first ' + 'and then re-created and the archive would be ' + 'extracted' + ) + return ret # Skip notices of incorrect types if we're cleaning if not (clean and contents is not None): @@ -1216,6 +1243,26 @@ def extracted(name, _add_explanation(ret, source_hash_trigger, contents_missing) return ret + if clean_parent and contents is not None: + errors = [] + log.debug('Removing directory %s due to clean_parent set to True', name) + try: + salt.utils.files.rm_rf(name.rstrip(os.sep)) + ret['changes'].setdefault( + 'removed', "Directory {} was removed prior to the extraction".format(name)) + except OSError as exc: + if exc.errno != errno.ENOENT: + errors.append(six.text_type(exc)) + if errors: + msg = ( + 'Unable to remove the directory {}. The following ' + 'errors were observed:\n'.format(name) + ) + for error in errors: + msg += '\n- {0}'.format(error) + ret['comment'] = msg + return ret + if clean and contents is not None: errors = [] log.debug('Cleaning archive paths from within %s', name) @@ -1350,10 +1397,13 @@ def extracted(name, ) return ret - tar_opts = shlex.split(options) + # Ignore verbose file list options as we are already using + # "v" below in tar_shortopts + tar_opts = [x for x in shlex.split(options) + if x not in ('v', '-v', '--verbose')] tar_cmd = ['tar'] - tar_shortopts = 'x' + tar_shortopts = 'xv' tar_longopts = [] for position, opt in enumerate(tar_opts): @@ -1383,9 +1433,9 @@ def extracted(name, ret['changes'] = results return ret if _is_bsdtar(): - files = results['stderr'] + files = results['stderr'].splitlines() else: - files = results['stdout'] + files = results['stdout'].splitlines() if not files: files = 'no tar output so far' except CommandExecutionError as exc: diff --git a/salt/states/azurearm_dns.py b/salt/states/azurearm_dns.py new file mode 100644 index 000000000000..e62d5f2038c3 --- /dev/null +++ b/salt/states/azurearm_dns.py @@ -0,0 +1,683 @@ +# -*- coding: utf-8 -*- +''' +Azure (ARM) DNS State Module + +.. versionadded:: Sodium + +:maintainer: +:maturity: new +:depends: + * `azure `_ >= 2.0.0 + * `azure-common `_ >= 1.1.8 + * `azure-mgmt `_ >= 1.0.0 + * `azure-mgmt-compute `_ >= 1.0.0 + * `azure-mgmt-dns `_ >= 1.0.1 + * `azure-mgmt-network `_ >= 1.7.1 + * `azure-mgmt-resource `_ >= 1.1.0 + * `azure-mgmt-storage `_ >= 1.0.0 + * `azure-mgmt-web `_ >= 0.32.0 + * `azure-storage `_ >= 0.34.3 + * `msrestazure `_ >= 0.4.21 +:platform: linux + +:configuration: This module requires Azure Resource Manager credentials to be passed as a dictionary of +keyword arguments to the ``connection_auth`` parameter in order to work properly. Since the authentication +parameters are sensitive, it's recommended to pass them to the states via pillar. + + Required provider parameters: + + if using username and password: + * ``subscription_id`` + * ``username`` + * ``password`` + + if using a service principal: + * ``subscription_id`` + * ``tenant`` + * ``client_id`` + * ``secret`` + + Optional provider parameters: + + **cloud_environment**: Used to point the cloud driver to different API endpoints, such as Azure GovCloud. Possible values: + * ``AZURE_PUBLIC_CLOUD`` (default) + * ``AZURE_CHINA_CLOUD`` + * ``AZURE_US_GOV_CLOUD`` + * ``AZURE_GERMAN_CLOUD`` + + Example Pillar for Azure Resource Manager authentication: + + .. code-block:: yaml + + azurearm: + user_pass_auth: + subscription_id: 3287abc8-f98a-c678-3bde-326766fd3617 + username: fletch + password: 123pass + mysubscription: + subscription_id: 3287abc8-f98a-c678-3bde-326766fd3617 + tenant: ABCDEFAB-1234-ABCD-1234-ABCDEFABCDEF + client_id: ABCDEFAB-1234-ABCD-1234-ABCDEFABCDEF + secret: XXXXXXXXXXXXXXXXXXXXXXXX + cloud_environment: AZURE_PUBLIC_CLOUD + + Example states using Azure Resource Manager authentication: + + .. code-block:: yaml + + {% set profile = salt['pillar.get']('azurearm:mysubscription') %} + Ensure DNS zone exists: + azurearm_dns.zone_present: + - name: contoso.com + - resource_group: my_rg + - tags: + how_awesome: very + contact_name: Elmer Fudd Gantry + - connection_auth: {{ profile }} + + Ensure DNS record set exists: + azurearm_dns.record_set_present: + - name: web + - zone_name: contoso.com + - resource_group: my_rg + - record_type: A + - ttl: 300 + - arecords: + - ipv4_address: 10.0.0.1 + - tags: + how_awesome: very + contact_name: Elmer Fudd Gantry + - connection_auth: {{ profile }} + + Ensure DNS record set is absent: + azurearm_dns.record_set_absent: + - name: web + - zone_name: contoso.com + - resource_group: my_rg + - record_type: A + - connection_auth: {{ profile }} + + Ensure DNS zone is absent: + azurearm_dns.zone_absent: + - name: contoso.com + - resource_group: my_rg + - connection_auth: {{ profile }} + +''' + +# Python libs +from __future__ import absolute_import +import logging + +# Salt libs +import salt.ext.six as six +try: + from salt.ext.six.moves import range as six_range +except ImportError: + six_range = range + +__virtualname__ = 'azurearm_dns' + +log = logging.getLogger(__name__) + + +def __virtual__(): + ''' + Only make this state available if the azurearm_dns module is available. + ''' + return __virtualname__ if 'azurearm_dns.zones_list_by_resource_group' in __salt__ else False + + +def zone_present(name, resource_group, etag=None, if_match=None, if_none_match=None, + registration_virtual_networks=None, resolution_virtual_networks=None, + tags=None, zone_type='Public', connection_auth=None, **kwargs): + ''' + .. versionadded:: Sodium + + Ensure a DNS zone exists. + + :param name: + Name of the DNS zone (without a terminating dot). + + :param resource_group: + The resource group assigned to the DNS zone. + + :param etag: + The etag of the zone. `Etags `_ are used + to handle concurrent changes to the same resource safely. + + :param if_match: + The etag of the DNS zone. Omit this value to always overwrite the current zone. Specify the last-seen etag + value to prevent accidentally overwritting any concurrent changes. + + :param if_none_match: + Set to '*' to allow a new DNS zone to be created, but to prevent updating an existing zone. Other values will + be ignored. + + :param registration_virtual_networks: + A list of references to virtual networks that register hostnames in this DNS zone. This is only when zone_type + is Private. (requires `azure-mgmt-dns `_ >= 2.0.0rc1) + + :param resolution_virtual_networks: + A list of references to virtual networks that resolve records in this DNS zone. This is only when zone_type is + Private. (requires `azure-mgmt-dns `_ >= 2.0.0rc1) + + :param tags: + A dictionary of strings can be passed as tag metadata to the DNS zone object. + + :param zone_type: + The type of this DNS zone (Public or Private). Possible values include: 'Public', 'Private'. Default value: 'Public' + (requires `azure-mgmt-dns `_ >= 2.0.0rc1) + + :param connection_auth: + A dict with subscription and authentication parameters to be used in connecting to the + Azure Resource Manager API. + + Example usage: + + .. code-block:: yaml + + Ensure DNS zone exists: + azurearm_dns.zone_present: + - name: contoso.com + - resource_group: my_rg + - zone_type: Private + - registration_virtual_networks: + - /subscriptions/{{ sub }}/resourceGroups/my_rg/providers/Microsoft.Network/virtualNetworks/test_vnet + - tags: + how_awesome: very + contact_name: Elmer Fudd Gantry + - connection_auth: {{ profile }} + + ''' + ret = { + 'name': name, + 'result': False, + 'comment': '', + 'changes': {} + } + + if not isinstance(connection_auth, dict): + ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + return ret + + zone = __salt__['azurearm_dns.zone_get'](name, resource_group, azurearm_log_level='info', **connection_auth) + + if 'error' not in zone: + tag_changes = __utils__['dictdiffer.deep_diff'](zone.get('tags', {}), tags or {}) + if tag_changes: + ret['changes']['tags'] = tag_changes + + # The zone_type parameter is only accessible in azure-mgmt-dns >=2.0.0rc1 + if zone.get('zone_type'): + if zone.get('zone_type').lower() != zone_type.lower(): + ret['changes']['zone_type'] = { + 'old': zone['zone_type'], + 'new': zone_type + } + + if zone_type.lower() == 'private': + # The registration_virtual_networks parameter is only accessible in azure-mgmt-dns >=2.0.0rc1 + if registration_virtual_networks and not isinstance(registration_virtual_networks, list): + ret['comment'] = 'registration_virtual_networks must be supplied as a list of VNET ID paths!' + return ret + reg_vnets = zone.get('registration_virtual_networks', []) + remote_reg_vnets = sorted([vnet['id'].lower() for vnet in reg_vnets if 'id' in vnet]) + local_reg_vnets = sorted([vnet.lower() for vnet in registration_virtual_networks or []]) + if local_reg_vnets != remote_reg_vnets: + ret['changes']['registration_virtual_networks'] = { + 'old': remote_reg_vnets, + 'new': local_reg_vnets + } + + # The resolution_virtual_networks parameter is only accessible in azure-mgmt-dns >=2.0.0rc1 + if resolution_virtual_networks and not isinstance(resolution_virtual_networks, list): + ret['comment'] = 'resolution_virtual_networks must be supplied as a list of VNET ID paths!' + return ret + res_vnets = zone.get('resolution_virtual_networks', []) + remote_res_vnets = sorted([vnet['id'].lower() for vnet in res_vnets if 'id' in vnet]) + local_res_vnets = sorted([vnet.lower() for vnet in resolution_virtual_networks or []]) + if local_res_vnets != remote_res_vnets: + ret['changes']['resolution_virtual_networks'] = { + 'old': remote_res_vnets, + 'new': local_res_vnets + } + + if not ret['changes']: + ret['result'] = True + ret['comment'] = 'DNS zone {0} is already present.'.format(name) + return ret + + if __opts__['test']: + ret['result'] = None + ret['comment'] = 'DNS zone {0} would be updated.'.format(name) + return ret + + else: + ret['changes'] = { + 'old': {}, + 'new': { + 'name': name, + 'resource_group': resource_group, + 'etag': etag, + 'registration_virtual_networks': registration_virtual_networks, + 'resolution_virtual_networks': resolution_virtual_networks, + 'tags': tags, + 'zone_type': zone_type, + } + } + + if __opts__['test']: + ret['comment'] = 'DNS zone {0} would be created.'.format(name) + ret['result'] = None + return ret + + zone_kwargs = kwargs.copy() + zone_kwargs.update(connection_auth) + + zone = __salt__['azurearm_dns.zone_create_or_update']( + name=name, + resource_group=resource_group, + etag=etag, + if_match=if_match, + if_none_match=if_none_match, + registration_virtual_networks=registration_virtual_networks, + resolution_virtual_networks=resolution_virtual_networks, + tags=tags, + zone_type=zone_type, + **zone_kwargs + ) + + if 'error' not in zone: + ret['result'] = True + ret['comment'] = 'DNS zone {0} has been created.'.format(name) + return ret + + ret['comment'] = 'Failed to create DNS zone {0}! ({1})'.format(name, zone.get('error')) + return ret + + +def zone_absent(name, resource_group, connection_auth=None): + ''' + .. versionadded:: Sodium + + Ensure a DNS zone does not exist in the resource group. + + :param name: + Name of the DNS zone. + + :param resource_group: + The resource group assigned to the DNS zone. + + :param connection_auth: + A dict with subscription and authentication parameters to be used in connecting to the + Azure Resource Manager API. + ''' + ret = { + 'name': name, + 'result': False, + 'comment': '', + 'changes': {} + } + + if not isinstance(connection_auth, dict): + ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + return ret + + zone = __salt__['azurearm_dns.zone_get']( + name, + resource_group, + azurearm_log_level='info', + **connection_auth + ) + + if 'error' in zone: + ret['result'] = True + ret['comment'] = 'DNS zone {0} was not found.'.format(name) + return ret + + elif __opts__['test']: + ret['comment'] = 'DNS zone {0} would be deleted.'.format(name) + ret['result'] = None + ret['changes'] = { + 'old': zone, + 'new': {}, + } + return ret + + deleted = __salt__['azurearm_dns.zone_delete'](name, resource_group, **connection_auth) + + if deleted: + ret['result'] = True + ret['comment'] = 'DNS zone {0} has been deleted.'.format(name) + ret['changes'] = { + 'old': zone, + 'new': {} + } + return ret + + ret['comment'] = 'Failed to delete DNS zone {0}!'.format(name) + return ret + + +def record_set_present(name, zone_name, resource_group, record_type, if_match=None, if_none_match=None, + etag=None, metadata=None, ttl=None, arecords=None, aaaa_records=None, mx_records=None, + ns_records=None, ptr_records=None, srv_records=None, txt_records=None, cname_record=None, + soa_record=None, caa_records=None, connection_auth=None, **kwargs): + ''' + .. versionadded:: Sodium + + Ensure a record set exists in a DNS zone. + + :param name: + The name of the record set, relative to the name of the zone. + + :param zone_name: + Name of the DNS zone (without a terminating dot). + + :param resource_group: + The resource group assigned to the DNS zone. + + :param record_type: + The type of DNS record in this record set. Record sets of type SOA can be updated but not created + (they are created when the DNS zone is created). Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', + 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' + + :param if_match: + The etag of the record set. Omit this value to always overwrite the current record set. Specify the last-seen + etag value to prevent accidentally overwritting any concurrent changes. + + :param if_none_match: + Set to '*' to allow a new record set to be created, but to prevent updating an existing record set. Other values + will be ignored. + + :param etag: + The etag of the record set. `Etags `_ are + used to handle concurrent changes to the same resource safely. + + :param metadata: + A dictionary of strings can be passed as tag metadata to the record set object. + + :param ttl: + The TTL (time-to-live) of the records in the record set. Required when specifying record information. + + :param arecords: + The list of A records in the record set. View the + `Azure SDK documentation `_ + to create a list of dictionaries representing the record objects. + + :param aaaa_records: + The list of AAAA records in the record set. View the + `Azure SDK documentation `_ + to create a list of dictionaries representing the record objects. + + :param mx_records: + The list of MX records in the record set. View the + `Azure SDK documentation `_ + to create a list of dictionaries representing the record objects. + + :param ns_records: + The list of NS records in the record set. View the + `Azure SDK documentation `_ + to create a list of dictionaries representing the record objects. + + :param ptr_records: + The list of PTR records in the record set. View the + `Azure SDK documentation `_ + to create a list of dictionaries representing the record objects. + + :param srv_records: + The list of SRV records in the record set. View the + `Azure SDK documentation `_ + to create a list of dictionaries representing the record objects. + + :param txt_records: + The list of TXT records in the record set. View the + `Azure SDK documentation `_ + to create a list of dictionaries representing the record objects. + + :param cname_record: + The CNAME record in the record set. View the + `Azure SDK documentation `_ + to create a dictionary representing the record object. + + :param soa_record: + The SOA record in the record set. View the + `Azure SDK documentation `_ + to create a dictionary representing the record object. + + :param caa_records: + The list of CAA records in the record set. View the + `Azure SDK documentation `_ + to create a list of dictionaries representing the record objects. + + :param connection_auth: + A dict with subscription and authentication parameters to be used in connecting to the + Azure Resource Manager API. + + Example usage: + + .. code-block:: yaml + + Ensure record set exists: + azurearm_dns.record_set_present: + - name: web + - zone_name: contoso.com + - resource_group: my_rg + - record_type: A + - ttl: 300 + - arecords: + - ipv4_address: 10.0.0.1 + - metadata: + how_awesome: very + contact_name: Elmer Fudd Gantry + - connection_auth: {{ profile }} + + ''' + ret = { + 'name': name, + 'result': False, + 'comment': '', + 'changes': {} + } + + record_vars = [ + 'arecords', + 'aaaa_records', + 'mx_records', + 'ns_records', + 'ptr_records', + 'srv_records', + 'txt_records', + 'cname_record', + 'soa_record', + 'caa_records' + ] + + if not isinstance(connection_auth, dict): + ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + return ret + + rec_set = __salt__['azurearm_dns.record_set_get']( + name, + zone_name, + resource_group, + record_type, + azurearm_log_level='info', + **connection_auth + ) + + if 'error' not in rec_set: + metadata_changes = __utils__['dictdiffer.deep_diff'](rec_set.get('metadata', {}), metadata or {}) + if metadata_changes: + ret['changes']['metadata'] = metadata_changes + + for record_str in record_vars: + # pylint: disable=eval-used + record = eval(record_str) + if record: + if not ttl: + ret['comment'] = 'TTL is required when specifying record information!' + return ret + if not rec_set.get(record_str): + ret['changes'] = {'new': {record_str: record}} + continue + if record_str[-1] != 's': + if not isinstance(record, dict): + ret['comment'] = '{0} record information must be specified as a dictionary!'.format(record_str) + return ret + for k, v in record.items(): + if v != rec_set[record_str].get(k): + ret['changes'] = {'new': {record_str: record}} + elif record_str[-1] == 's': + if not isinstance(record, list): + ret['comment'] = '{0} record information must be specified as a list of dictionaries!'.format( + record_str + ) + return ret + local, remote = [sorted(config) for config in (record, rec_set[record_str])] + for idx in six_range(0, len(local)): + for key in local[idx]: + local_val = local[idx][key] + remote_val = remote[idx].get(key) + if isinstance(local_val, six.string_types): + local_val = local_val.lower() + if isinstance(remote_val, six.string_types): + remote_val = remote_val.lower() + if local_val != remote_val: + ret['changes'] = {'new': {record_str: record}} + + if not ret['changes']: + ret['result'] = True + ret['comment'] = 'Record set {0} is already present.'.format(name) + return ret + + if __opts__['test']: + ret['result'] = None + ret['comment'] = 'Record set {0} would be updated.'.format(name) + return ret + + else: + ret['changes'] = { + 'old': {}, + 'new': { + 'name': name, + 'zone_name': zone_name, + 'resource_group': resource_group, + 'record_type': record_type, + 'etag': etag, + 'metadata': metadata, + 'ttl': ttl, + } + } + for record in record_vars: + # pylint: disable=eval-used + if eval(record): + # pylint: disable=eval-used + ret['changes']['new'][record] = eval(record) + + if __opts__['test']: + ret['comment'] = 'Record set {0} would be created.'.format(name) + ret['result'] = None + return ret + + rec_set_kwargs = kwargs.copy() + rec_set_kwargs.update(connection_auth) + + rec_set = __salt__['azurearm_dns.record_set_create_or_update']( + name=name, + zone_name=zone_name, + resource_group=resource_group, + record_type=record_type, + if_match=if_match, + if_none_match=if_none_match, + etag=etag, + ttl=ttl, + metadata=metadata, + arecords=arecords, + aaaa_records=aaaa_records, + mx_records=mx_records, + ns_records=ns_records, + ptr_records=ptr_records, + srv_records=srv_records, + txt_records=txt_records, + cname_record=cname_record, + soa_record=soa_record, + caa_records=caa_records, + **rec_set_kwargs + ) + + if 'error' not in rec_set: + ret['result'] = True + ret['comment'] = 'Record set {0} has been created.'.format(name) + return ret + + ret['comment'] = 'Failed to create record set {0}! ({1})'.format(name, rec_set.get('error')) + return ret + + +def record_set_absent(name, zone_name, resource_group, connection_auth=None): + ''' + .. versionadded:: Sodium + + Ensure a record set does not exist in the DNS zone. + + :param name: + Name of the record set. + + :param zone_name: + Name of the DNS zone. + + :param resource_group: + The resource group assigned to the DNS zone. + + :param connection_auth: + A dict with subscription and authentication parameters to be used in connecting to the + Azure Resource Manager API. + ''' + ret = { + 'name': name, + 'result': False, + 'comment': '', + 'changes': {} + } + + if not isinstance(connection_auth, dict): + ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + return ret + + rec_set = __salt__['azurearm_dns.record_set_get']( + name, + zone_name, + resource_group, + azurearm_log_level='info', + **connection_auth + ) + + if 'error' in rec_set: + ret['result'] = True + ret['comment'] = 'Record set {0} was not found in zone {1}.'.format(name, zone_name) + return ret + + elif __opts__['test']: + ret['comment'] = 'Record set {0} would be deleted.'.format(name) + ret['result'] = None + ret['changes'] = { + 'old': rec_set, + 'new': {}, + } + return ret + + deleted = __salt__['azurearm_dns.record_set_delete'](name, zone_name, resource_group, **connection_auth) + + if deleted: + ret['result'] = True + ret['comment'] = 'Record set {0} has been deleted.'.format(name) + ret['changes'] = { + 'old': rec_set, + 'new': {} + } + return ret + + ret['comment'] = 'Failed to delete record set {0}!'.format(name) + return ret diff --git a/salt/states/beacon.py b/salt/states/beacon.py index f707972a59bc..b4add307228f 100644 --- a/salt/states/beacon.py +++ b/salt/states/beacon.py @@ -31,6 +31,42 @@ - 0.1 - 1.0 + .. versionadded:: Neon + + Beginning in the Neon release, multiple copies of a beacon can be configured + using the ``beacon_module`` parameter. + + inotify_infs: + beacon.present: + - save: True + - enable: True + - files: + /etc/infs.conf: + mask: + - create + - delete + - modify + recurse: True + auto_add: True + - interval: 10 + - beacon_module: inotify + - disable_during_state_run: True + + inotify_ntp: + beacon.present: + - save: True + - enable: True + - files: + /etc/ntp.conf: + mask: + - create + - delete + - modify + recurse: True + auto_add: True + - interval: 10 + - beacon_module: inotify + - disable_during_state_run: True ''' from __future__ import absolute_import, print_function, unicode_literals diff --git a/salt/states/blockdev.py b/salt/states/blockdev.py index 38543ac8a073..d911c75cb81f 100644 --- a/salt/states/blockdev.py +++ b/salt/states/blockdev.py @@ -193,5 +193,7 @@ def _checkblk(name): Check if the blk exists and return its fstype if ok ''' - blk = __salt__['cmd.run']('blkid -o value -s TYPE {0}'.format(name)) + blk = __salt__['cmd.run']( + ['blkid', '-o', 'value', '-s', 'TYPE', name], + ignore_retcode=True) return '' if not blk else blk diff --git a/salt/states/cmd.py b/salt/states/cmd.py index 4d20b5138175..1c0608712068 100644 --- a/salt/states/cmd.py +++ b/salt/states/cmd.py @@ -33,7 +33,8 @@ cmd.run: - creates: /tmp/foo -``creates`` also accepts a list of files: +``creates`` also accepts a list of files, in which case this state will +run if **any** of the files do not exist: .. code-block:: yaml @@ -196,7 +197,7 @@ - require: - file: /usr/local/bin/postinstall.sh -``cmd.wait`` itself does not do anything; all functionality is inside its ``mod_watch`` +``cmd.wait`` itself do not do anything; all functionality is inside its ``mod_watch`` function, which is called by ``watch`` on changes. The preferred format is using the :ref:`onchanges Requisite `, which @@ -402,6 +403,7 @@ def wait(name, unless=None, creates=None, cwd=None, + root=None, runas=None, shell=None, env=(), @@ -436,6 +438,10 @@ def wait(name, The current working directory to execute the command in, defaults to /root + root + Path to the root of the jail to use. If this parameter is set, the command + will run inside a chroot + runas The user name to run the command as @@ -490,7 +496,9 @@ def wait(name, a state. For more information, see the :ref:`stateful-argument` section. creates - Only run if the file or files specified by ``creates`` do not exist. + Only run if the file specified by ``creates`` do not exist. If you + specify a list of files then this state will only run if **any** of + the files do not exist. .. versionadded:: 2014.7.0 @@ -674,6 +682,7 @@ def run(name, unless=None, creates=None, cwd=None, + root=None, runas=None, shell=None, env=None, @@ -707,6 +716,10 @@ def run(name, The current working directory to execute the command in, defaults to /root + root + Path to the root of the jail to use. If this parameter is set, the command + will run inside a chroot + runas The user name to run the command as @@ -801,7 +814,9 @@ def run(name, .. versionadded:: 2015.8.0 creates - Only run if the file or files specified by ``creates`` do not exist. + Only run if the file specified by ``creates`` do not exist. If you + specify a list of files then this state will only run if **any** of + the files do not exist. .. versionadded:: 2014.7.0 @@ -882,6 +897,7 @@ def run(name, cmd_kwargs = copy.deepcopy(kwargs) cmd_kwargs.update({'cwd': cwd, + 'root': root, 'runas': runas, 'use_vt': use_vt, 'shell': shell or __grains__['shell'], @@ -912,10 +928,11 @@ def run(name, # Wow, we passed the test, run this sucker! try: - cmd_all = __salt__['cmd.run_all']( - name, timeout=timeout, python_shell=True, **cmd_kwargs + run_cmd = 'cmd.run_all' if not root else 'cmd.run_chroot' + cmd_all = __salt__[run_cmd]( + cmd=name, timeout=timeout, python_shell=True, **cmd_kwargs ) - except CommandExecutionError as err: + except Exception as err: ret['comment'] = six.text_type(err) return ret @@ -1053,7 +1070,9 @@ def script(name, 'arg two' arg3" creates - Only run if the file or files specified by ``creates`` do not exist. + Only run if the file specified by ``creates`` do not exist. If you + specify a list of files then this state will only run if **any** of + the files do not exist. .. versionadded:: 2014.7.0 diff --git a/salt/states/disk.py b/salt/states/disk.py index 82254a391a2e..aca5f7c2d113 100644 --- a/salt/states/disk.py +++ b/salt/states/disk.py @@ -47,10 +47,11 @@ # Import salt libs from salt.ext.six import string_types +from os import path __monitor__ = [ - 'status', - ] + 'status', +] def _validate_int(name, value, limits=(), strip='%'): @@ -74,12 +75,88 @@ def _validate_int(name, value, limits=(), strip='%'): return value, comment -def status(name, maximum=None, minimum=None, absolute=False): +def _status_mount(name, ret, minimum, maximum, absolute, free, data): + # Get used space + if absolute: + used = int(data[name]['used']) + available = int(data[name]['available']) + else: + # POSIX-compliant df output reports percent used as 'capacity' + used = int(data[name]['capacity'].strip('%')) + available = 100 - used + + # Collect return information + ret['data'] = data[name] + return _check_min_max(absolute, free, available, used, maximum, minimum, ret) + + +def _status_path(directory, ret, minimum, maximum, absolute, free): + if path.isdir(directory) is False: + ret['result'] = False + ret['comment'] += ('Directory {0} does not exist or is not a directory'.format(directory)) + return ret + + data = __salt__['status.diskusage'](directory) + + if absolute: + used = int(data[directory]['total']) - int(data[directory]['available']) + available = int(data[directory]['available']) + else: + if int(data[directory]['total']) == 0: + used = 0 + available = 0 + else: + used = round(float(int(data[directory]['total']) - int(data[directory]['available'])) / + int(data[directory]['total']) * 100, 1) + available = round(float(data[directory]['available']) / int(data[directory]['total']) * 100, 1) + + ret['data'] = data + return _check_min_max(absolute, free, available, used, maximum, minimum, ret) + + +def _check_min_max(absolute, free, available, used, maximum, minimum, ret): + unit = 'KB' if absolute else '%' + if minimum is not None: + if free: + if available < minimum: + ret['comment'] = ('Disk available space is below minimum' + ' of {0} {2} at {1} {2}' + ''.format(minimum, available, unit) + ) + return ret + else: + if used < minimum: + ret['comment'] = ('Disk used space is below minimum' + ' of {0} {2} at {1} {2}' + ''.format(minimum, used, unit) + ) + return ret + if maximum is not None: + if free: + if available > maximum: + ret['comment'] = ('Disk available space is above maximum' + ' of {0} {2} at {1} {2}' + ''.format(maximum, available, unit) + ) + return ret + else: + if used > maximum: + ret['comment'] = ('Disk used space is above maximum' + ' of {0} {2} at {1} {2}' + ''.format(maximum, used, unit) + ) + return ret + ret['comment'] = 'Disk used space in acceptable range' + ret['result'] = True + return ret + + +def status(name, maximum=None, minimum=None, absolute=False, free=False): ''' Return the current disk usage stats for the named mount point name - Disk mount with which to check used space + Disk mount or directory for which to check used space maximum The maximum disk utilization @@ -92,6 +169,10 @@ def status(name, maximum=None, minimum=None, absolute=False): the `absolute` flag to use kilobytes. .. versionadded:: 2016.11.0 + + free + By default, `minimum` & `maximum` refer to the amount of used space. + Set to `True` to evaluate the free space instead. ''' # Monitoring state, no changes will be made so no test interface needed ret = {'name': name, @@ -100,13 +181,6 @@ def status(name, maximum=None, minimum=None, absolute=False): 'changes': {}, 'data': {}} # Data field for monitoring state - data = __salt__['disk.usage']() - - # Validate name - if name not in data: - ret['result'] = False - ret['comment'] += 'Named disk mount not present ' - return ret # Validate extrema if maximum is not None: if not absolute: @@ -126,30 +200,11 @@ def status(name, maximum=None, minimum=None, absolute=False): if ret['comment']: return ret - # Get used space - if absolute: - used = int(data[name]['used']) - else: - # POSIX-compliant df output reports percent used as 'capacity' - used = int(data[name]['capacity'].strip('%')) + data = __salt__['disk.usage']() - # Collect return information - ret['data'] = data[name] - unit = 'KB' if absolute else '%' - if minimum is not None: - if used < minimum: - ret['comment'] = ('Disk used space is below minimum' - ' of {0} {2} at {1} {2}' - ''.format(minimum, used, unit) - ) - return ret - if maximum is not None: - if used > maximum: - ret['comment'] = ('Disk used space is above maximum' - ' of {0} {2} at {1} {2}' - ''.format(maximum, used, unit) - ) - return ret - ret['comment'] = 'Disk used space in acceptable range' - ret['result'] = True - return ret + # Validate name + if name not in data: + ret['comment'] += ('Disk mount {0} not present. '.format(name)) + return _status_path(name, ret, minimum, maximum, absolute, free) + else: + return _status_mount(name, ret, minimum, maximum, absolute, free, data) diff --git a/salt/states/docker_container.py b/salt/states/docker_container.py index e6a08981e1dd..ed63cae35386 100644 --- a/salt/states/docker_container.py +++ b/salt/states/docker_container.py @@ -1237,8 +1237,6 @@ def running(name, ``--net=none``). Not to be confused with Python's ``None``. - ``container:`` - Reuses another container's network stack - ``host`` - Use the host's network stack inside the container - - Any name that identifies an existing network that might be created - with ``docker.network_present``. .. warning:: @@ -2062,7 +2060,8 @@ def _get_nets(): __context__[contextkey] = new_container_info.get( 'NetworkSettings', {}).get('Networks', {}) return __context__[contextkey] - autoip_keys = __opts__['docker.compare_container_networks'].get('automatic', []) + autoip_keys = __salt__['config.option']( + 'docker.compare_container_networks').get('automatic', []) for net_name, net_changes in six.iteritems( ret['changes'].get('container', {}).get('Networks', {})): if 'IPConfiguration' in net_changes \ diff --git a/salt/states/file.py b/salt/states/file.py index 84167da578f3..e9665d48e638 100644 --- a/salt/states/file.py +++ b/salt/states/file.py @@ -1072,6 +1072,68 @@ def _symlink_check(name, target, force, user, group, win_owner): 'should be. Did you mean to use force?'.format(name)), changes +def _hardlink_same(name, target): + ''' + Check to see if the inodes match for the name and the target + ''' + res = __salt__['file.stats'](name, None, follow_symlinks=False) + if 'inode' not in res: + return False + name_i = res['inode'] + + res = __salt__['file.stats'](target, None, follow_symlinks=False) + if 'inode' not in res: + return False + target_i = res['inode'] + + return name_i == target_i + + +def _hardlink_check(name, target, force): + ''' + Check the hardlink function + ''' + changes = {} + if not os.path.exists(target): + msg = 'Target {0} for hard link does not exist'.format(target) + return False, msg, changes + + elif os.path.isdir(target): + msg = 'Unable to hard link from directory {0}'.format(target) + return False, msg, changes + + if os.path.isdir(name): + msg = 'Unable to hard link to directory {0}'.format(name) + return False, msg, changes + + elif not os.path.exists(name): + msg = 'Hard link {0} to {1} is set for creation'.format(name, target) + changes['new'] = name + return None, msg, changes + + elif __salt__['file.is_hardlink'](name): + if _hardlink_same(name, target): + msg = 'The hard link {0} is presently targetting {1}'.format(name, target) + return True, msg, changes + + msg = 'Link {0} target is set to be changed to {1}'.format(name, target) + changes['change'] = name + return None, msg, changes + + if force: + msg = ( + 'The file or directory {0} is set for removal to ' + 'make way for a new hard link targeting {1}'.format(name, target) + ) + return None, msg, changes + + msg = ( + 'File or directory exists where the hard link {0} ' + 'should be. Did you mean to use force?'.format(name) + ) + return False, msg, changes + + def _test_owner(kwargs, user=None): ''' Convert owner to user, since other config management tools use owner, @@ -1332,6 +1394,203 @@ def _makedirs(name, mode=dir_mode) +def hardlink( + name, + target, + force=False, + makedirs=False, + user=None, + group=None, + dir_mode=None, + **kwargs): + ''' + Create a hard link + If the file already exists and is a hard link pointing to any location other + than the specified target, the hard link will be replaced. If the hard link + is a regular file or directory then the state will return False. If the + regular file is desired to be replaced with a hard link pass force: True + + name + The location of the hard link to create + target + The location that the hard link points to + force + If the name of the hard link exists and force is set to False, the + state will fail. If force is set to True, the file or directory in the + way of the hard link file will be deleted to make room for the hard + link, unless backupname is set, when it will be renamed + makedirs + If the location of the hard link does not already have a parent directory + then the state will fail, setting makedirs to True will allow Salt to + create the parent directory + user + The user to own any directories made if makedirs is set to true. This + defaults to the user salt is running as on the minion + group + The group ownership set on any directories made if makedirs is set to + true. This defaults to the group salt is running as on the minion. On + Windows, this is ignored + dir_mode + If directories are to be created, passing this option specifies the + permissions for those directories. + ''' + name = os.path.expanduser(name) + + # Make sure that leading zeros stripped by YAML loader are added back + dir_mode = salt.utils.files.normalize_mode(dir_mode) + + user = _test_owner(kwargs, user=user) + ret = {'name': name, + 'changes': {}, + 'result': True, + 'comment': ''} + if not name: + return _error(ret, 'Must provide name to file.hardlink') + + if user is None: + user = __opts__['user'] + + if salt.utils.is_windows(): + if group is not None: + log.warning( + 'The group argument for {0} has been ignored as this ' + 'is a Windows system.'.format(name) + ) + group = user + + if group is None: + group = __salt__['file.gid_to_group']( + __salt__['user.info'](user).get('gid', 0) + ) + + preflight_errors = [] + uid = __salt__['file.user_to_uid'](user) + gid = __salt__['file.group_to_gid'](group) + + if uid == '': + preflight_errors.append('User {0} does not exist'.format(user)) + + if gid == '': + preflight_errors.append('Group {0} does not exist'.format(group)) + + if not os.path.isabs(name): + preflight_errors.append( + 'Specified file {0} is not an absolute path'.format(name) + ) + + if not os.path.isabs(target): + preflight_errors.append( + 'Specified target {0} is not an absolute path'.format(target) + ) + + if preflight_errors: + msg = '. '.join(preflight_errors) + if len(preflight_errors) > 1: + msg += '.' + return _error(ret, msg) + + if __opts__['test']: + presult, pcomment, pchanges = _hardlink_check(name, target, force) + ret['result'] = presult + ret['comment'] = pcomment + ret['changes'] = pchanges + return ret + + # We use zip_longest here because there's a number of issues in pylint's + # tracker that complains about not linking the zip builtin. + for direction, item in zip_longest(['to', 'from'], [name, target]): + if os.path.isdir(item): + msg = 'Unable to hard link {0} directory {1}'.format(direction, item) + return _error(ret, msg) + + if not os.path.exists(target): + msg = 'Target {0} for hard link does not exist'.format(target) + return _error(ret, msg) + + # Check that the directory to write the hard link to exists + if not os.path.isdir(os.path.dirname(name)): + if makedirs: + __salt__['file.makedirs']( + name, + user=user, + group=group, + mode=dir_mode) + + else: + return _error( + ret, + 'Directory {0} for hard link is not present'.format( + os.path.dirname(name) + ) + ) + + # If file is not a hard link and we're actually overwriting it, then verify + # that this was forced. + if os.path.isfile(name) and not __salt__['file.is_hardlink'](name): + + # Remove whatever is in the way. This should then hit the else case + # of the file.is_hardlink check below + if force: + os.remove(name) + ret['changes']['forced'] = 'File for hard link was forcibly replaced' + + # Otherwise throw an error + else: + return _error(ret, + ('File exists where the hard link {0} should be' + .format(name))) + + # If the file is a hard link, then we can simply rewrite its target since + # nothing is really being lost here. + if __salt__['file.is_hardlink'](name): + + # If the inodes point to the same thing, then there's nothing to do + # except for let the user know that this has already happened. + if _hardlink_same(name, target): + ret['result'] = True + ret['comment'] = ('Target of hard link {0} is already pointing ' + 'to {1}'.format(name, target)) + return ret + + # First remove the old hard link since a reference to it already exists + os.remove(name) + + # Now we can remake it + try: + __salt__['file.link'](target, name) + + # Or not... + except CommandExecutionError as E: + ret['result'] = False + ret['comment'] = ('Unable to set target of hard link {0} -> ' + '{1}: {2}'.format(name, target, E)) + return ret + + # Good to go + ret['result'] = True + ret['comment'] = 'Set target of hard link {0} -> {1}'.format(name, target) + ret['changes']['new'] = name + + # The link is not present, so simply make it + elif not os.path.exists(name): + try: + __salt__['file.link'](target, name) + + # Or not... + except CommandExecutionError as E: + ret['result'] = False + ret['comment'] = ('Unable to create new hard link {0} -> ' + '{1}: {2}'.format(name, target, E)) + return ret + + # Made a new hard link, things are ok + ret['result'] = True + ret['comment'] = 'Created new hard link {0} -> {1}'.format(name, target) + ret['changes']['new'] = name + + return ret + + def symlink( name, target, @@ -1954,7 +2213,9 @@ def managed(name, (use ~ in YAML), the file will be created as an empty file and the content will not be managed. This is also the case when a file already exists and the source is undefined; the contents of the file - will not be changed or managed. + will not be changed or managed. If source is left blank or None, please + also set replaced to False to make your intention explicit. + If the file is hosted on a HTTP or FTP server then the source_hash argument is also required. @@ -2311,8 +2572,10 @@ def managed(name, If ``True``, files managed using ``contents``, ``contents_pillar``, or ``contents_grains`` will have a newline added to the end of the file if - one is not present. Setting this option to ``False`` will omit this - final newline. + one is not present. Setting this option to ``False`` will ensure the + final line, or entry, does not contain a new line. If the last line, or + entry in the file does contain a new line already, this option will not + remove it. contents_delimiter .. versionadded:: 2015.8.4 @@ -2635,8 +2898,11 @@ def managed(name, for part in validated_contents: for line in part.splitlines(): contents += line.rstrip('\n').rstrip('\r') + os.linesep - if contents_newline and not contents.endswith(os.linesep): - contents += os.linesep + if not contents_newline: + # If contents newline is set to False, strip out the newline + # character and carriage return character + contents = contents.rstrip('\n').rstrip('\r') + except UnicodeDecodeError: # Either something terrible happened, or we have binary data. if template: @@ -6417,7 +6683,6 @@ def rename(name, source, force=False, makedirs=False): if not force: ret['comment'] = ('The target file "{0}" exists and will not be ' 'overwritten'.format(name)) - ret['result'] = False return ret elif not __opts__['test']: # Remove the destination to prevent problems later diff --git a/salt/states/glance.py b/salt/states/glance.py index e38fe41323ad..8c97b86036e2 100644 --- a/salt/states/glance.py +++ b/salt/states/glance.py @@ -94,6 +94,13 @@ def image_present(name, visibility='public', protected=None, - disk_format ('raw' (default), 'vhd', 'vhdx', 'vmdk', 'vdi', 'iso', 'qcow2', 'aki', 'ari' or 'ami') ''' + __utils__['versions.warn_until']( + 'Aluminium', + ( + 'The glance state module has been deprecated and will be removed in {version}. ' + 'Please update to using the glance_image state module' + ), + ) ret = {'name': name, 'changes': {}, 'result': True, diff --git a/salt/states/grains.py b/salt/states/grains.py index ca3a0bc9248f..7121d9701bf3 100644 --- a/salt/states/grains.py +++ b/salt/states/grains.py @@ -45,6 +45,30 @@ def exists(name, delimiter=DEFAULT_TARGET_DELIM): return ret +def make_hashable(list_grain, result=None): + ''' + Ensure that a list grain is hashable. + + list_grain + The list grain that should be hashable + + result + This function is recursive, so it must be possible to use a + sublist as parameter to the function. Should not be used by a caller + outside of the function. + + Make it possible to compare two list grains to each other if the list + contains complex objects. + ''' + result = result or set() + for sublist in list_grain: + if type(sublist) == list: + make_hashable(sublist, result) + else: + result.add(frozenset(sublist)) + return result + + def present(name, value, delimiter=DEFAULT_TARGET_DELIM, force=False): ''' Ensure that a grain is set @@ -174,7 +198,7 @@ def list_present(name, value, delimiter=DEFAULT_TARGET_DELIM): ret['comment'] = 'Grain {0} is not a valid list'.format(name) return ret if isinstance(value, list): - if set(value).issubset(set(__salt__['grains.get'](name))): + if make_hashable(value).issubset(make_hashable(__salt__['grains.get'](name))): ret['comment'] = 'Value {1} is already in grain {0}'.format(name, value) return ret elif name in __context__.get('pending_grains', {}): diff --git a/salt/states/hipchat.py b/salt/states/hipchat.py deleted file mode 100644 index cb31d58dd9c7..000000000000 --- a/salt/states/hipchat.py +++ /dev/null @@ -1,144 +0,0 @@ -# -*- coding: utf-8 -*- -''' -Send a message to Hipchat -========================= - -This state is useful for sending messages to Hipchat during state runs. - -The property api_url is optional. By defaul will use the public HipChat API at https://api.hipchat.com - -.. versionadded:: 2015.5.0 - -.. code-block:: yaml - - hipchat-message: - hipchat.send_message: - - room_id: 123456 - - from_name: SuperAdmin - - message: 'This state was executed successfully.' - - api_url: https://hipchat.myteam.com - - api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - - api_version: v1 - -The api key can be specified in the master or minion configuration like below: - -.. code-block:: yaml - - hipchat: - api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - api_version: v1 - -''' - -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals - - -def __virtual__(): - ''' - Only load if the hipchat module is available in __salt__ - ''' - return 'hipchat' if 'hipchat.send_message' in __salt__ else False - - -def send_message(name, - room_id, - from_name, - message, - api_url=None, - api_key=None, - api_version=None, - message_color='yellow', - notify=False): - ''' - Send a message to a Hipchat room. - - .. code-block:: yaml - - hipchat-message: - hipchat.send_message: - - room_id: 123456 - - from_name: SuperAdmin - - message: 'This state was executed successfully.' - - api_url: https://hipchat.myteam.com - - api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - - api_version: v1 - - message_color: green - - notify: True - - The following parameters are required: - - name - The unique name for this event. - - room_id - The room to send the message to. Can either be the ID or the name. - - from_name - The name of that is to be shown in the "from" field. - If not specified, defaults to. - - message - The message that is to be sent to the Hipchat room. - - The following parameters are optional: - - api_url - The API URl to be used. - If not specified here or in the configuration options of master or minion, - will use the public HipChat API: https://api.hipchat.com - - api_key - The api key for Hipchat to use for authentication, - if not specified in the configuration options of master or minion. - - api_version - The api version for Hipchat to use, - if not specified in the configuration options of master or minion. - - message_color - The color the Hipchat message should be displayed in. One of the following, default: yellow - "yellow", "red", "green", "purple", "gray", or "random". - - notify - Should a notification in the room be raised. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - - if __opts__['test']: - ret['comment'] = 'The following message is to be sent to Hipchat: {0}'.format(message) - ret['result'] = None - return ret - - if not room_id: - ret['comment'] = 'Hipchat room id is missing: {0}'.format(name) - return ret - - if not from_name: - ret['comment'] = 'Hipchat from name is missing: {0}'.format(name) - return ret - - if not message: - ret['comment'] = 'Hipchat message is missing: {0}'.format(name) - return ret - - ret['result'] = __salt__['hipchat.send_message']( - room_id=room_id, - message=message, - from_name=from_name, - api_url=api_url, - api_key=api_key, - api_version=api_version, - color=message_color, - notify=notify, - ) - - if ret and ret['result']: - ret['comment'] = 'Sent message: {0}'.format(name) - else: - ret['comment'] = 'Failed to send message: {0}'.format(name) - - return ret diff --git a/salt/states/host.py b/salt/states/host.py index e38646e4fc93..9ec24161132c 100644 --- a/salt/states/host.py +++ b/salt/states/host.py @@ -78,9 +78,9 @@ def present(name, ip, clean=False): # pylint: disable=C0103 The ip addr(s) to apply to the host. Can be a single IP or a list of IP addresses. - clean : False + clean Remove any entries which don't match those configured in the ``ip`` - option. + option. Default is ``False``. .. versionadded:: 2018.3.4 ''' diff --git a/salt/states/keystore.py b/salt/states/keystore.py new file mode 100644 index 000000000000..2476f34d060e --- /dev/null +++ b/salt/states/keystore.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +''' +State management of a java keystore +''' + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals +import logging +import os + +__virtualname__ = 'keystore' + +# Init logger +log = logging.getLogger(__name__) + + +def __virtual__(): + ''' + Only load this module if the keystore execution module is available + ''' + if 'keystore.list' in __salt__: + return __virtualname__ + return (False, ('Cannot load the {0} state module: ' + 'keystore execution module not found'.format(__virtualname__))) + + +def managed(name, passphrase, entries, force_remove=False): + ''' + Create or manage a java keystore. + + name + The path to the keystore file + + passphrase + The password to the keystore + + entries + A list containing an alias, certificate, and optional private_key. + The certificate and private_key can be a file or a string + + .. code-block:: yaml + + - entries: + - alias: hostname2 + certificate: /path/to/cert.crt + private_key: /path/to/key.key + - alias: stringhost + certificate: | + -----BEGIN CERTIFICATE----- + MIICEjCCAXsCAg36MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG + ... + 2VguKv4SWjRFoRkIfIlHX0qVviMhSlNy2ioFLy7JcPZb+v3ftDGywUqcBiVDoea0 + -----END CERTIFICATE----- + + force_remove + If True will cause the state to remove any entries found in the keystore which are not + defined in the state. The default is False. + + Example + + .. code-block:: yaml + + define_keystore: + keystore.managed: + - name: /path/to/keystore + - passphrase: changeit + - force_remove: True + - entries: + - alias: hostname1 + certificate: /path/to/cert.crt + - alias: remotehost + certificate: /path/to/cert2.crt + private_key: /path/to/key2.key + - alias: pillarhost + certificate: {{ salt.pillar.get('path:to:cert') }} + ''' + ret = {'changes': {}, + 'comment': '', + 'name': name, + 'result': True} + + keep_list = [] + old_aliases = [] + + if force_remove: + if os.path.exists(name): + existing_entries = __salt__['keystore.list'](name, passphrase) + for entry in existing_entries: + old_aliases.append(entry.get('alias')) + log.debug("Existing aliases list: %s", old_aliases) + + for entry in entries: + update_entry = True + existing_entry = None + if os.path.exists(name): + if force_remove: + keep_list.append(entry['alias']) + + existing_entry = __salt__['keystore.list'](name, passphrase, entry['alias']) + if existing_entry: + existing_sha1 = existing_entry[0]['sha1'] + new_sha1 = __salt__['x509.read_certificate'](entry['certificate'])['SHA1 Finger Print'] + if existing_sha1 == new_sha1: + update_entry = False + + if update_entry: + if __opts__['test']: + ret['result'] = None + if existing_entry: + ret['comment'] += "Alias {0} would have been updated\n".format(entry['alias']) + else: + ret['comment'] += "Alias {0} would have been added\n".format(entry['alias']) + else: + if existing_entry: + result = __salt__['keystore.remove'](entry['alias'], name, passphrase) + result = __salt__['keystore.add'](entry['alias'], + name, + passphrase, + entry['certificate'], + private_key=entry.get('private_key', None) + ) + if result: + ret['changes'][entry['alias']] = "Updated" + ret['comment'] += "Alias {0} updated.\n".format(entry['alias']) + else: + result = __salt__['keystore.add'](entry['alias'], + name, + passphrase, + entry['certificate'], + private_key=entry.get('private_key', None) + ) + if result: + ret['changes'][entry['alias']] = "Added" + ret['comment'] += "Alias {0} added.\n".format(entry['alias']) + + if force_remove: + # Determine which aliases need to be removed + remove_list = list(set(old_aliases) - set(keep_list)) + log.debug("Will remove: %s", remove_list) + for alias_name in remove_list: + if __opts__['test']: + ret['comment'] += "Alias {0} would have been removed".format(alias_name) + ret['result'] = None + else: + __salt__['keystore.remove'](alias_name, name, passphrase) + ret['changes'][alias_name] = "Removed" + ret['comment'] += "Alias {0} removed.\n".format(alias_name) + + if not ret['changes'] and not ret['comment']: + ret['comment'] = "No changes made.\n" + return ret diff --git a/salt/states/loop.py b/salt/states/loop.py index edaf8c306371..7ed3dcadf47d 100644 --- a/salt/states/loop.py +++ b/salt/states/loop.py @@ -78,6 +78,11 @@ def until(name, 'result': False, 'comment': ''} + if m_args is None: + m_args = () + if m_kwargs is None: + m_kwargs = {} + if name not in __salt__: ret['comment'] = 'Cannot find module {0}'.format(name) return ret @@ -94,6 +99,10 @@ def until(name, ret['comment'] = 'The execution module {0} will be run'.format(name) ret['result'] = None return ret + if not m_args: + m_args = [] + if not m_kwargs: + m_kwargs = {} def timed_out(): if time.time() >= timeout: diff --git a/salt/states/lvm.py b/salt/states/lvm.py index bc937a33ab27..5cb15d0ed6e8 100644 --- a/salt/states/lvm.py +++ b/salt/states/lvm.py @@ -56,7 +56,7 @@ def pv_present(name, **kwargs): 'name': name, 'result': True} - if __salt__['lvm.pvdisplay'](name): + if __salt__['lvm.pvdisplay'](name, quiet=True): ret['comment'] = 'Physical Volume {0} already present'.format(name) elif __opts__['test']: ret['comment'] = 'Physical Volume {0} is set to be created'.format(name) @@ -86,7 +86,7 @@ def pv_absent(name): 'name': name, 'result': True} - if not __salt__['lvm.pvdisplay'](name): + if not __salt__['lvm.pvdisplay'](name, quiet=True): ret['comment'] = 'Physical Volume {0} does not exist'.format(name) elif __opts__['test']: ret['comment'] = 'Physical Volume {0} is set to be removed'.format(name) @@ -95,7 +95,7 @@ def pv_absent(name): else: changes = __salt__['lvm.pvremove'](name) - if __salt__['lvm.pvdisplay'](name): + if __salt__['lvm.pvdisplay'](name, quiet=True): ret['comment'] = 'Failed to remove Physical Volume {0}'.format(name) ret['result'] = False else: @@ -125,7 +125,7 @@ def vg_present(name, devices=None, **kwargs): if isinstance(devices, six.string_types): devices = devices.split(',') - if __salt__['lvm.vgdisplay'](name): + if __salt__['lvm.vgdisplay'](name, quiet=True): ret['comment'] = 'Volume Group {0} already present'.format(name) for device in devices: realdev = os.path.realpath(device) @@ -185,7 +185,7 @@ def vg_absent(name): 'name': name, 'result': True} - if not __salt__['lvm.vgdisplay'](name): + if not __salt__['lvm.vgdisplay'](name, quiet=True): ret['comment'] = 'Volume Group {0} already absent'.format(name) elif __opts__['test']: ret['comment'] = 'Volume Group {0} is set to be removed'.format(name) @@ -194,7 +194,7 @@ def vg_absent(name): else: changes = __salt__['lvm.vgremove'](name) - if not __salt__['lvm.vgdisplay'](name): + if not __salt__['lvm.vgdisplay'](name, quiet=True): ret['comment'] = 'Removed Volume Group {0}'.format(name) ret['changes']['removed'] = changes else: @@ -311,7 +311,7 @@ def lv_absent(name, vgname=None): 'result': True} lvpath = '/dev/{0}/{1}'.format(vgname, name) - if not __salt__['lvm.lvdisplay'](lvpath): + if not __salt__['lvm.lvdisplay'](lvpath, quiet=True): ret['comment'] = 'Logical Volume {0} already absent'.format(name) elif __opts__['test']: ret['comment'] = 'Logical Volume {0} is set to be removed'.format(name) @@ -320,7 +320,7 @@ def lv_absent(name, vgname=None): else: changes = __salt__['lvm.lvremove'](name, vgname) - if not __salt__['lvm.lvdisplay'](lvpath): + if not __salt__['lvm.lvdisplay'](lvpath, quiet=True): ret['comment'] = 'Removed Logical Volume {0}'.format(name) ret['changes']['removed'] = changes else: diff --git a/salt/states/mdadm_raid.py b/salt/states/mdadm_raid.py index fd285b6acee2..d634522c334e 100644 --- a/salt/states/mdadm_raid.py +++ b/salt/states/mdadm_raid.py @@ -98,7 +98,7 @@ def present(name, if dev == 'missing' or not __salt__['file.access'](dev, 'f'): missing.append(dev) continue - superblock = __salt__['raid.examine'](dev) + superblock = __salt__['raid.examine'](dev, quiet=True) if 'MD_UUID' in superblock: uuid = superblock['MD_UUID'] diff --git a/salt/states/module.py b/salt/states/module.py index 9968529ffda9..d21e327c4ec1 100644 --- a/salt/states/module.py +++ b/salt/states/module.py @@ -3,42 +3,188 @@ Execution of Salt modules from within states ============================================ +.. note:: + + There are two styles of calling ``module.run``. **The legacy style will no + longer be available starting in the Sodium release.** To opt-in early to the + new style you must add the following to your ``/etc/salt/minion`` config + file: + + .. code-block:: yaml + + use_superseded: + - module.run + With `module.run` these states allow individual execution module calls to be -made via states. To call a single module function use a :mod:`module.run ` -state: +made via states. Here's a contrived example, to show you how it's done: .. code-block:: yaml - mine.send: + # New Style + test.random_hash: module.run: - - network.interfaces + - test.random_hash: + - size: 42 + - hash_type: sha256 -Note that this example is probably unnecessary to use in practice, since the -``mine_functions`` and ``mine_interval`` config parameters can be used to -schedule updates for the mine (see :ref:`here ` for more info). + # Legacy Style + test.random_hash: + module.run: + - size: 42 + - hash_type: sha256 -It is sometimes desirable to trigger a function call after a state is executed, -for this the :mod:`module.wait ` state can be used: +In the new style, the state ID (``test.random_hash``, in this case) is +irrelevant when using ``module.run``. It could have very well been written: .. code-block:: yaml - fetch_out_of_band: + Generate a random hash: module.run: - - git.fetch: - - cwd: /path/to/my/repo - - user: myuser - - opts: '--all' + - test.random_hash: + - size: 42 + - hash_type: sha256 + +For a simple state like that it's not a big deal, but if the module you're +using has certain parameters, things can get cluttered, fast. Using the +contrived custom module (stuck in ``/srv/salt/_modules/foo.py``, or your +configured file_roots_): -Another example: +.. code-block:: python + + def bar(name, names, fun, state, saltenv): + return "Name: {name} Names: {names} Fun: {fun} State: {state} Saltenv: {saltenv}".format(**locals()) + +Your legacy state has to look like this: .. code-block:: yaml - mine.send: + # Legacy style + Unfortunate example: + module.run: + - name: foo.bar + - m_name: Some name + - m_names: + - Such names + - very wow + - m_state: Arkansas + - m_fun: Such fun + - m_saltenv: Salty + +With the new style it's much cleaner: + +.. code-block:: yaml + + # New style + Better: + module.run: + - foo.bar: + - name: Some name + - names: + - Such names + - very wow + - state: Arkansas + - fun: Such fun + - saltenv: Salty + +The new style also allows multiple modules in one state. For instance, you can +do this: + +.. code-block:: yaml + + Do many things: + module.run: + - test.random_hash: + - size: 10 + - hash_type: md5 + # Note the `:` at the end + - test.true: + - test.arg: + - this + - has + - args + - and: kwargs + - isn't: that neat? + # Note the `:` at the end, too + - test.version: + - test.fib: + - 4 + +Where in the legacy style you would have had to split your states like this: + +.. code-block:: yaml + + test.random_hash: + module.run: + - size: 10 + - hash_type: md5 + + test.nop: + module.run + + test.arg: + module.run: + - args: + - this + - has + - args + - kwargs: + and: kwargs + isn't: that neat? + + test.version: + module.run + +Another difference is that in the legacy style, unconsumed arguments to the +``module`` state were simply passed into the module function being executed: + +.. code-block:: yaml + + show off module.run with args: + module.run: + - name: test.random_hash + - size: 42 + - hash_type: sha256 + +The new style is much more explicit, with the arguments and keyword arguments +being nested under the name of the function: + +.. code-block:: yaml + + show off module.run with args: module.run: - - network.ip_addrs: - - interface: eth0 + # Note the lack of `name: `, and trailing `:` + - test.random_hash: + - size: 42 + - hash_type: sha256 -And more complex example: +If the function takes ``*args``, they can be passed in as well: + +.. code-block:: yaml + + args and kwargs: + module.run: + - test.arg: + - isn't + - this + - fun + - this: that + - salt: stack + +Modern Examples +--------------- + +Here are some other examples using the modern ``module.run``: + +.. code-block:: yaml + + fetch_out_of_band: + module.run: + - git.fetch: + - cwd: /path/to/my/repo + - user: myuser + - opts: '--all' + +A more complex example: .. code-block:: yaml @@ -53,39 +199,31 @@ - start_date: '2017-1-20' - start_time: '11:59PM' -Please note, this is a new behaviour of `module.run` function. - -With the previous `module.run` there are several differences: - -- The need of `name` keyword -- The need of `m_` prefix -- No way to call more than one function at once - -For example: +It is sometimes desirable to trigger a function call after a state is executed, +for this the :mod:`module.wait ` state can be used: .. code-block:: yaml + add example to hosts: + file.append: + - name: /etc/hosts + - text: 203.0.113.13 example.com + + # New Style mine.send: module.wait: - - name: network.interfaces + # Again, note the trailing `:` + - hosts.list_hosts: - watch: - - file: /etc/network/interfaces + - file: add example to hosts -All arguments that the ``module`` state does not consume are passed through to -the execution module function being executed: +Legacy (Default) Examples +------------------------- -.. code-block:: yaml - - fetch_out_of_band: - module.run: - - name: git.fetch - - cwd: /path/to/my/repo - - user: myuser - - opts: '--all' - -Due to how the state system works, if a module function accepts an -argument called, ``name``, then ``m_name`` must be used to specify that -argument, to avoid a collision with the ``name`` argument. +If you're using the legacy ``module.run``, due to how the state system works, +if a module function accepts an argument called, ``name``, then ``m_name`` must +be used to specify that argument, to avoid a collision with the ``name`` +argument. Here is a list of keywords hidden by the state system, which must be prefixed with ``m_``: @@ -133,6 +271,15 @@ delvol_on_destroy: 'True' } +Other modules take the keyword arguments using this style: + +.. code-block:: yaml + + mac_enable_ssh: + module.run: + - name: system.set_remote_login + - enable: True + Another example that creates a recurring task that runs a batch file on a Windows system: @@ -151,26 +298,7 @@ start_time: '11:59PM' } -Another option is to use the new version of `module.run`. With which you can call one (or more!) -functions at once the following way: - -.. code-block:: yaml - - call_something: - module.run: - - git.fetch: - - cwd: /path/to/my/repo - - user: myuser - - opts: '--all' - -By default this behaviour is not turned on. In order to do so, please add the following -configuration to the minion: - -.. code-block:: yaml - - use_superseded: - - module.run - +.. _file_roots: https://docs.saltstack.com/en/latest/ref/configuration/master.html#file-roots ''' from __future__ import absolute_import, print_function, unicode_literals @@ -482,7 +610,6 @@ def _run(name, **kwargs): nkwargs = {} if aspec.keywords and aspec.keywords in kwargs: nkwargs = kwargs.pop(aspec.keywords) - if not isinstance(nkwargs, dict): msg = "'{0}' must be a dict." ret['comment'] = msg.format(aspec.keywords) diff --git a/salt/states/mount.py b/salt/states/mount.py index 162da1ca621e..2f5946b647ad 100644 --- a/salt/states/mount.py +++ b/salt/states/mount.py @@ -956,3 +956,295 @@ def mod_watch(name, user=None, **kwargs): else: ret['comment'] = 'Watch not supported in {0} at this time'.format(kwargs['sfun']) return ret + + +def _convert_to(maybe_device, convert_to): + ''' + Convert a device name, UUID or LABEL to a device name, UUID or + LABEL. + + Return the fs_spec required for fstab. + + ''' + + # Fast path. If we already have the information required, we can + # save one blkid call + if not convert_to or \ + (convert_to == 'device' and maybe_device.startswith('/')) or \ + maybe_device.startswith('{}='.format(convert_to.upper())): + return maybe_device + + # Get the device information + if maybe_device.startswith('/'): + blkid = __salt__['disk.blkid'](maybe_device) + else: + blkid = __salt__['disk.blkid'](token=maybe_device) + + result = None + if len(blkid) == 1: + if convert_to == 'device': + result = list(blkid.keys())[0] + else: + key = convert_to.upper() + result = '{}={}'.format(key, list(blkid.values())[0][key]) + + return result + + +def fstab_present(name, fs_file, fs_vfstype, fs_mntops='defaults', + fs_freq=0, fs_passno=0, mount_by=None, + config='/etc/fstab', mount=True, match_on='auto'): + ''' + Makes sure that a fstab mount point is pressent. + + name + The name of block device. Can be any valid fs_spec value. + + fs_file + Mount point (target) for the filesystem. + + fs_vfstype + The type of the filesystem (e.g. ext4, xfs, btrfs, ...) + + fs_mntops + The mount options associated with the filesystem. Default is + ``defaults``. + + fs_freq + Field is used by dump to determine which fs need to be + dumped. Default is ``0`` + + fs_passno + Field is used by fsck to determine the order in which + filesystem checks are done at boot time. Default is ``0`` + + mount_by + Select the final value for fs_spec. Can be [``None``, + ``device``, ``label``, ``uuid``, ``partlabel``, + ``partuuid``]. If ``None``, the value for fs_spect will be the + parameter ``name``, in other case will search the correct + value based on the device name. For example, for ``uuid``, the + value for fs_spec will be of type 'UUID=xxx' instead of the + device name set in ``name``. + + config + Place where the fstab file lives. Default is ``/etc/fstab`` + + mount + Set if the mount should be mounted immediately. Default is + ``True`` + + match_on + A name or list of fstab properties on which this state should + be applied. Default is ``auto``, a special value indicating + to guess based on fstype. In general, ``auto`` matches on + name for recognized special devices and device otherwise. + + ''' + ret = { + 'name': name, + 'result': False, + 'changes': {}, + 'comment': [], + } + + # Adjust fs_mntops based on the OS + if fs_mntops == 'defaults': + if __grains__['os'] in ['MacOS', 'Darwin']: + fs_mntops = 'noowners' + elif __grains__['os'] == 'AIX': + fs_mntops = '' + + # Adjust the config file based on the OS + if config == '/etc/fstab': + if __grains__['os'] in ['MacOS', 'Darwin']: + config = '/etc/auto_salt' + elif __grains__['os'] == 'AIX': + config = '/etc/filesystems' + + if not fs_file == '/': + fs_file = fs_file.rstrip('/') + + fs_spec = _convert_to(name, mount_by) + + # Validate that the device is valid after the conversion + if not fs_spec: + msg = 'Device {} cannot be converted to {}' + ret['comment'].append(msg.format(name, mount_by)) + return ret + + if __opts__['test']: + if __grains__['os'] in ['MacOS', 'Darwin']: + out = __salt__['mount.set_automaster'](name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + config=config, + test=True) + elif __grains__['os'] == 'AIX': + out = __salt__['mount.set_filesystems'](name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + mount=mount, + config=config, + test=True, + match_on=match_on) + else: + out = __salt__['mount.set_fstab'](name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + dump=fs_freq, + pass_num=fs_passno, + config=config, + test=True, + match_on=match_on) + ret['result'] = None + if out == 'present': + msg = '{} entry is already in {}.' + ret['comment'].append(msg.format(fs_file, config)) + elif out == 'new': + msg = '{} entry will be written in {}.' + ret['comment'].append(msg.format(fs_file, config)) + elif out == 'change': + msg = '{} entry will be updated in {}.' + ret['comment'].append(msg.format(fs_file, config)) + else: + ret['result'] = False + msg = '{} entry cannot be created in {}: {}.' + ret['comment'].append(msg.format(fs_file, config, out)) + return ret + + if __grains__['os'] in ['MacOS', 'Darwin']: + out = __salt__['mount.set_automaster'](name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + config=config) + elif __grains__['os'] == 'AIX': + out = __salt__['mount.set_filesystems'](name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + mount=mount, + config=config, + match_on=match_on) + else: + out = __salt__['mount.set_fstab'](name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + dump=fs_freq, + pass_num=fs_passno, + config=config, + match_on=match_on) + + ret['result'] = True + if out == 'present': + msg = '{} entry was already in {}.' + ret['comment'].append(msg.format(fs_file, config)) + elif out == 'new': + ret['changes']['persist'] = out + msg = '{} entry added in {}.' + ret['comment'].append(msg.format(fs_file, config)) + elif out == 'change': + ret['changes']['persist'] = out + msg = '{} entry updated in {}.' + ret['comment'].append(msg.format(fs_file, config)) + else: + ret['result'] = False + msg = '{} entry cannot be changed in {}: {}.' + ret['comment'].append(msg.format(fs_file, config, out)) + + return ret + + +def fstab_absent(name, fs_file, mount_by=None, config='/etc/fstab'): + ''' + Makes sure that a fstab mount point is absent. + + name + The name of block device. Can be any valid fs_spec value. + + fs_file + Mount point (target) for the filesystem. + + mount_by + Select the final value for fs_spec. Can be [``None``, + ``device``, ``label``, ``uuid``, ``partlabel``, + ``partuuid``]. If ``None``, the value for fs_spect will be the + parameter ``name``, in other case will search the correct + value based on the device name. For example, for ``uuid``, the + value for fs_spec will be of type 'UUID=xxx' instead of the + device name set in ``name``. + + config + Place where the fstab file lives + + ''' + ret = { + 'name': name, + 'result': False, + 'changes': {}, + 'comment': [], + } + + # Adjust the config file based on the OS + if config == '/etc/fstab': + if __grains__['os'] in ['MacOS', 'Darwin']: + config = '/etc/auto_salt' + elif __grains__['os'] == 'AIX': + config = '/etc/filesystems' + + if not fs_file == '/': + fs_file = fs_file.rstrip('/') + + fs_spec = _convert_to(name, mount_by) + + if __grains__['os'] in ['MacOS', 'Darwin']: + fstab_data = __salt__['mount.automaster'](config) + elif __grains__['os'] == 'AIX': + fstab_data = __salt__['mount.filesystems'](config) + else: + fstab_data = __salt__['mount.fstab'](config) + + if __opts__['test']: + ret['result'] = None + if fs_file not in fstab_data: + msg = '{} entry is already missing in {}.' + ret['comment'].append(msg.format(fs_file, config)) + else: + msg = '{} entry will be removed from {}.' + ret['comment'].append(msg.format(fs_file, config)) + return ret + + if fs_file in fstab_data: + if __grains__['os'] in ['MacOS', 'Darwin']: + out = __salt__['mount.rm_automaster'](name=fs_file, + device=fs_spec, + config=config) + elif __grains__['os'] == 'AIX': + out = __salt__['mount.rm_filesystems'](name=fs_file, + device=fs_spec, + config=config) + else: + out = __salt__['mount.rm_fstab'](name=fs_file, + device=fs_spec, + config=config) + + if out is not True: + ret['result'] = False + msg = '{} entry failed when removing from {}.' + ret['comment'].append(msg.format(fs_file, config)) + else: + ret['result'] = True + ret['changes']['persist'] = 'removed' + msg = '{} entry removed from {}.' + ret['comment'].append(msg.format(fs_file, config)) + else: + ret['result'] = True + msg = '{} entry is already missing in {}.' + ret['comment'].append(msg.format(fs_file, config)) + + return ret diff --git a/salt/states/netacl.py b/salt/states/netacl.py index c49cdabdc520..1b3930b26771 100644 --- a/salt/states/netacl.py +++ b/salt/states/netacl.py @@ -10,7 +10,7 @@ .. versionadded:: 2017.7.0 -:codeauthor: Mircea Ulinic +:codeauthor: Mircea Ulinic :maturity: new :depends: capirca, napalm :platform: unix diff --git a/salt/states/netconfig.py b/salt/states/netconfig.py index 535a96805d3b..b900c35f00b1 100644 --- a/salt/states/netconfig.py +++ b/salt/states/netconfig.py @@ -5,7 +5,7 @@ Manage the configuration on a network device given a specific static config or template. -:codeauthor: Mircea Ulinic & Jerome Fleury +:codeauthor: Mircea Ulinic & Jerome Fleury :maturity: new :depends: napalm :platform: unix diff --git a/salt/states/netntp.py b/salt/states/netntp.py index 0184b19039b3..2a190a5f6a30 100644 --- a/salt/states/netntp.py +++ b/salt/states/netntp.py @@ -7,7 +7,7 @@ Manage the configuration of NTP peers and servers on the network devices through the NAPALM proxy. -:codeauthor: Mircea Ulinic & Jerome Fleury +:codeauthor: Mircea Ulinic & Jerome Fleury :maturity: new :depends: napalm :platform: unix diff --git a/salt/states/netsnmp.py b/salt/states/netsnmp.py index 9bafffefbc93..2833c24175be 100644 --- a/salt/states/netsnmp.py +++ b/salt/states/netsnmp.py @@ -5,7 +5,7 @@ Manage the SNMP configuration on network devices. -:codeauthor: Mircea Ulinic +:codeauthor: Mircea Ulinic :maturity: new :depends: napalm :platform: unix @@ -23,11 +23,10 @@ import logging log = logging.getLogger(__name__) -from json import loads, dumps - # salt lib from salt.ext import six # import NAPALM utils +import salt.utils.json import salt.utils.napalm # ---------------------------------------------------------------------------------------------------------------------- @@ -69,7 +68,7 @@ def _ordered_dict_to_dict(config): Forced the datatype to dict, in case OrderedDict is used. ''' - return loads(dumps(config)) + return salt.utils.json.loads(salt.utils.json.dumps(config)) def _expand_config(config, defaults): diff --git a/salt/states/netusers.py b/salt/states/netusers.py index 58b3751c6b74..74e2e5346ede 100644 --- a/salt/states/netusers.py +++ b/salt/states/netusers.py @@ -5,7 +5,7 @@ Manage the users configuration on network devices via the NAPALM proxy. -:codeauthor: Mircea Ulinic +:codeauthor: Mircea Ulinic :maturity: new :depends: napalm :platform: unix @@ -25,11 +25,11 @@ # Python std lib from copy import deepcopy -from json import loads, dumps # salt lib from salt.ext import six # import NAPALM utils +import salt.utils.json import salt.utils.napalm # ---------------------------------------------------------------------------------------------------------------------- @@ -69,7 +69,7 @@ def _ordered_dict_to_dict(probes): '''.''' - return loads(dumps(probes)) + return salt.utils.json.loads(salt.utils.json.dumps(probes)) def _expand_users(device_users, common_users): diff --git a/salt/states/pip_state.py b/salt/states/pip_state.py index a9b2995e09e1..2bee4eee743f 100644 --- a/salt/states/pip_state.py +++ b/salt/states/pip_state.py @@ -96,7 +96,21 @@ def pip_has_exceptions_mod(ver): HAS_PIP = True except ImportError: HAS_PIP = False + # Remove references to the loaded pip module above so reloading works + import sys + pip_related_entries = [ + (k, v) for (k, v) in sys.modules.items() + or getattr(v, '__module__', '').startswith('pip.') + or (isinstance(v, types.ModuleType) and v.__name__.startswith('pip.')) + ] + for name, entry in pip_related_entries: + sys.modules.pop(name) + del entry + del pip + sys_modules_pip = sys.modules.pop('pip', None) + if sys_modules_pip is not None: + del sys_modules_pip if HAS_PIP is True: if not hasattr(purge_pip, '__pip_ver__'): @@ -105,9 +119,13 @@ def pip_has_exceptions_mod(ver): purge_pip() import pip purge_pip.__pip_ver__ = pip.__version__ - if pip_has_internal_exceptions_mod(pip.__version__): + if salt.utils.versions.compare(ver1=pip.__version__, + oper='>=', + ver2='18.1'): from pip._internal.exceptions import InstallationError # pylint: disable=E0611,E0401 - elif pip_has_exceptions_mod(pip.__version__): + elif salt.utils.versions.compare(ver1=pip.__version__, + oper='>=', + ver2='1.0'): from pip.exceptions import InstallationError # pylint: disable=E0611,E0401 else: InstallationError = ValueError @@ -129,8 +147,8 @@ def _from_line(*args, **kwargs): import pip._internal.req.constructors # pylint: disable=E0611,E0401 return pip._internal.req.constructors.install_req_from_line(*args, **kwargs) elif salt.utils.versions.compare(ver1=pip.__version__, - oper='>=', - ver2='10.0'): + oper='>=', + ver2='10.0'): import pip._internal.req # pylint: disable=E0611,E0401 return pip._internal.req.InstallRequirement.from_line(*args, **kwargs) else: @@ -404,6 +422,7 @@ def installed(name, no_cache_dir=False, cache_dir=None, no_binary=None, + extra_args=None, **kwargs): ''' Make sure the package is installed @@ -665,6 +684,23 @@ def installed(name, - reload_modules: True - exists_action: i + extra_args + pip keyword and positional arguments not yet implemented in salt + + .. code-block:: yaml + + pandas: + pip.installed: + - name: pandas + - extra_args: + - --latest-pip-kwarg: param + - --latest-pip-arg + + .. warning:: + + If unsupported options are passed here that are not supported in a + minion's version of pip, a `No such option error` will be thrown. + .. _`virtualenv`: http://www.virtualenv.org/en/latest/ ''' @@ -901,6 +937,8 @@ def installed(name, use_vt=use_vt, trusted_host=trusted_host, no_cache_dir=no_cache_dir, + extra_args=extra_args, + disable_version_check=True, **kwargs ) diff --git a/salt/states/pkg.py b/salt/states/pkg.py index 0aca1e0af88b..68423aa47eac 100644 --- a/salt/states/pkg.py +++ b/salt/states/pkg.py @@ -135,10 +135,7 @@ # The following imports are used by the namespaced win_pkg funcs # and need to be included in their globals. # pylint: disable=import-error,unused-import - try: - import msgpack - except ImportError: - import msgpack_pure as msgpack + import salt.utils.msgpack as msgpack from salt.utils.versions import LooseVersion # pylint: enable=import-error,unused-import # pylint: enable=invalid-name @@ -1514,6 +1511,75 @@ def installed( see the :ref:`Reloading Modules ` documentation for more information. + .. seealso:: unless and onlyif + + You can use the :ref:`unless ` or + :ref:`onlyif ` syntax to skip a full package run. + This can be helpful in large environments with multiple states that + include requisites for packages to be installed. + + .. code-block:: yaml + + # Using file.file_exists for a single-factor check + install_nginx: + pkg.installed: + - name: nginx + - unless: + - fun: file.file_exists + args: + - /etc/nginx/nginx.conf + + .. code-block:: yaml + + # Using file.search for a two-factor check + install_nginx: + pkg.installed: + - name: nginx + - unless: + - fun: file.search + args: + - /etc/nginx/nginx.conf + - 'user www-data;' + + The above examples use two different methods to reasonably ensure + that a package has already been installed. First, with checking for a + file that would be created with the package. Second, by checking for + specific text within a file that would be created or managed by salt. + With these requisists satisfied, unless will return ``True`` and the + ``pkg.installed`` state will be skipped. + + .. code-block:: bash + + # Example of state run without unless used + salt 'saltdev' state.apply nginx + saltdev: + ---------- + ID: install_nginx + Function: pkg.installed + Name: nginx + Result: True + Comment: All specified packages are already installed + Started: 20:11:56.388331 + Duration: 4290.0 ms + Changes: + + # Example of state run using unless requisite + salt 'saltdev' state.apply nginx + saltdev: + ---------- + ID: install_nginx + Function: pkg.installed + Name: nginx + Result: True + Comment: unless condition is true + Started: 20:10:50.659215 + Duration: 1530.0 ms + Changes: + + The result is a reduction of almost 3 seconds. In larger environments, + small reductions in waiting time can add up. + + :ref:`Unless Requisite ` ''' if isinstance(pkgs, list) and len(pkgs) == 0: return {'name': name, diff --git a/salt/states/pkgrepo.py b/salt/states/pkgrepo.py index 4d5e9eea92fc..d52ebcc1a5cc 100644 --- a/salt/states/pkgrepo.py +++ b/salt/states/pkgrepo.py @@ -230,7 +230,7 @@ def managed(name, ppa=None, **kwargs): Included to reduce confusion due to YUM/DNF/Zypper's use of the ``enabled`` argument. If this is passed for an APT-based distro, then the reverse will be passed as ``disabled``. For example, passing - ``enabled=False`` will assume ``disabled=False``. + ``enabled=False`` will assume ``disabled=True``. architectures On apt-based systems, architectures can restrict the available diff --git a/salt/states/probes.py b/salt/states/probes.py index e621fb353606..24725bc4c0e0 100644 --- a/salt/states/probes.py +++ b/salt/states/probes.py @@ -5,7 +5,7 @@ Configure RPM (JunOS)/SLA (Cisco) probes on the device via NAPALM proxy. -:codeauthor: Mircea Ulinic & Jerome Fleury +:codeauthor: Mircea Ulinic & Jerome Fleury :maturity: new :depends: napalm :platform: unix @@ -25,10 +25,10 @@ log = logging.getLogger(__name__) from copy import deepcopy -from json import loads, dumps # salt modules from salt.ext import six +import salt.utils.json # import NAPALM utils import salt.utils.napalm @@ -205,7 +205,7 @@ def _ordered_dict_to_dict(probes): '''Mandatory to be dict type in order to be used in the NAPALM Jinja template.''' - return loads(dumps(probes)) + return salt.utils.json.loads(salt.utils.json.dumps(probes)) def _set_rpm_probes(probes): diff --git a/salt/states/saltutil.py b/salt/states/saltutil.py new file mode 100644 index 000000000000..3803abf18de0 --- /dev/null +++ b/salt/states/saltutil.py @@ -0,0 +1,353 @@ +# -*- coding: utf-8 -*- +''' +Saltutil State +============== + +This state wraps the saltutil execution modules to make them easier to run +from a states. Rather than needing to to use ``module.run`` this state allows for +improved change detection. + + .. versionadded: Neon +''' +from __future__ import absolute_import, unicode_literals, print_function + +import logging + +# Define the module's virtual name +__virtualname__ = 'saltutil' + +log = logging.getLogger(__name__) + + +def __virtual__(): + ''' + Named saltutil + ''' + return __virtualname__ + + +def _sync_single(name, module, **kwargs): + ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + + if __opts__['test']: + ret['result'] = None + ret['comment'] = "saltutil.sync_{0} would have been run".format(module) + return ret + + try: + sync_status = __salt__['saltutil.sync_{0}'.format(module)](**kwargs) + if sync_status: + ret['changes'][module] = sync_status + ret['comment'] = "Updated {0}.".format(module) + except Exception as e: + log.error("Failed to run saltutil.sync_%s: %s", module, e) + ret['result'] = False + ret['comment'] = "Failed to run sync_{0}: {1}".format(module, e) + return ret + + if not ret['changes']: + ret['comment'] = "No updates to sync" + + return ret + + +def sync_all(name, **kwargs): + ''' + Performs the same task as saltutil.sync_all module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_all: + - refresh: True + ''' + ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + + if __opts__['test']: + ret['result'] = None + ret['comment'] = "saltutil.sync_all would have been run" + return ret + + try: + sync_status = __salt__['saltutil.sync_all'](**kwargs) + for key, value in sync_status.items(): + if value: + ret['changes'][key] = value + ret['comment'] = "Sync performed" + except Exception as e: + log.error("Failed to run saltutil.sync_all: %s", e) + ret['result'] = False + ret['comment'] = "Failed to run sync_all: {0}".format(e) + return ret + + if not ret['changes']: + ret['comment'] = "No updates to sync" + + return ret + + +def sync_beacons(name, **kwargs): + ''' + Performs the same task as saltutil.sync_beacons module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_beacons: + - refresh: True + ''' + return _sync_single(name, "beacons", **kwargs) + + +def sync_clouds(name, **kwargs): + ''' + Performs the same task as saltutil.sync_clouds module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_clouds: + - refresh: True + ''' + return _sync_single(name, "clouds", **kwargs) + + +def sync_engines(name, **kwargs): + ''' + Performs the same task as saltutil.sync_engines module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_engines: + - refresh: True + ''' + return _sync_single(name, "engines", **kwargs) + + +def sync_grains(name, **kwargs): + ''' + Performs the same task as saltutil.sync_grains module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_grains: + - refresh: True + ''' + return _sync_single(name, "grains", **kwargs) + + +def sync_executors(name, **kwargs): + ''' + Performs the same task as saltutil.sync_executors module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_executors: + - refresh: True + ''' + return _sync_single(name, "executors", **kwargs) + + +def sync_log_handlers(name, **kwargs): + ''' + Performs the same task as saltutil.sync_log_handlers module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_log_handlers: + - refresh: True + ''' + return _sync_single(name, "log_handlers", **kwargs) + + +def sync_modules(name, **kwargs): + ''' + Performs the same task as saltutil.sync_modules module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_modules: + - refresh: True + ''' + return _sync_single(name, "modules", **kwargs) + + +def sync_output(name, **kwargs): + ''' + Performs the same task as saltutil.sync_output module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_output: + - refresh: True + ''' + return _sync_single(name, "output", **kwargs) + + +def sync_outputters(name, **kwargs): + ''' + Performs the same task as saltutil.sync_outputters module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_outputters: + - refresh: True + ''' + return _sync_single(name, "outputters", **kwargs) + + +def sync_pillar(name, **kwargs): + ''' + Performs the same task as saltutil.sync_pillar module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_pillar: + - refresh: True + ''' + return _sync_single(name, "pillar", **kwargs) + + +def sync_proxymodules(name, **kwargs): + ''' + Performs the same task as saltutil.sync_proxymodules module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_proxymodules: + - refresh: True + ''' + return _sync_single(name, "proxymodules", **kwargs) + + +def sync_matchers(name, **kwargs): + ''' + Performs the same task as saltutil.sync_matchers module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_matchers: + - refresh: True + ''' + return _sync_single(name, "matchers", **kwargs) + + +def sync_renderers(name, **kwargs): + ''' + Performs the same task as saltutil.sync_renderers module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_renderers: + - refresh: True + ''' + return _sync_single(name, "renderers", **kwargs) + + +def sync_returners(name, **kwargs): + ''' + Performs the same task as saltutil.sync_returners module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_returners: + - refresh: True + ''' + return _sync_single(name, "returners", **kwargs) + + +def sync_sdb(name, **kwargs): + ''' + Performs the same task as saltutil.sync_sdb module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_sdb: + - refresh: True + ''' + return _sync_single(name, "sdb", **kwargs) + + +def sync_states(name, **kwargs): + ''' + Performs the same task as saltutil.sync_states module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_states: + - refresh: True + ''' + return _sync_single(name, "states", **kwargs) + + +def sync_thorium(name, **kwargs): + ''' + Performs the same task as saltutil.sync_thorium module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_thorium: + - refresh: True + ''' + return _sync_single(name, "thorium", **kwargs) + + +def sync_utils(name, **kwargs): + ''' + Performs the same task as saltutil.sync_utils module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_utils: + - refresh: True + ''' + return _sync_single(name, "utils", **kwargs) + + +def sync_serializers(name, **kwargs): + ''' + Performs the same task as saltutil.sync_serializers module + See :mod:`saltutil module for full list of options ` + + .. code-block:: yaml + + sync_everything: + saltutil.sync_serializers: + - refresh: True + ''' + return _sync_single(name, "serializers", **kwargs) diff --git a/salt/states/slack.py b/salt/states/slack.py index f3545c795038..8eb0b823e33e 100644 --- a/salt/states/slack.py +++ b/salt/states/slack.py @@ -39,12 +39,7 @@ def __virtual__(): return 'slack' if 'slack.post_message' in __salt__ else False -def post_message(name, - channel, - from_name, - message, - api_key=None, - icon=None): +def post_message(name, **kwargs): ''' Send a message to a Slack channel. @@ -59,57 +54,104 @@ def post_message(name, The following parameters are required: - name - The unique name for this event. + api_key parameters: + name + The unique name for this event. - channel - The channel to send the message to. Can either be the ID or the name. + channel + The channel to send the message to. Can either be the ID or the name. - from_name - The name of that is to be shown in the "from" field. + from_name + The name of that is to be shown in the "from" field. - message - The message that is to be sent to the Slack channel. + message + The message that is to be sent to the Slack channel. - The following parameters are optional: + The following parameters are optional: - api_key - The api key for Slack to use for authentication, - if not specified in the configuration options of master or minion. + api_key + The api key for Slack to use for authentication, + if not specified in the configuration options of master or minion. - icon - URL to an image to use as the icon for this message + icon + URL to an image to use as the icon for this message + + webhook parameters: + name + The unique name for this event. + + message + The message that is to be sent to the Slack channel. + + color + The color of border of left side + + short + An optional flag indicating whether the value is short + enough to be displayed side-by-side with other values. + + identifier + The identifier of WebHook. + + channel + The channel to use instead of the WebHook default. + + username + Username to use instead of WebHook default. + + icon_emoji + Icon to use instead of WebHook default. ''' ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} - if __opts__['test']: - ret['comment'] = 'The following message is to be sent to Slack: {0}'.format(message) - ret['result'] = None + if not kwargs.get('api_key') and not kwargs.get('webhook'): + ret['comment'] = 'Please specify api_key or webhook.' + return ret + + if kwargs.get('api_key') and kwargs.get('webhook'): + ret['comment'] = 'Please specify only either api_key or webhook.' return ret - if not channel: - ret['comment'] = 'Slack channel is missing: {0}'.format(channel) + if kwargs.get('api_key') and not kwargs.get('channel'): + ret['comment'] = 'Slack channel is missing.' return ret - if not from_name: - ret['comment'] = 'Slack from name is missing: {0}'.format(from_name) + if kwargs.get('api_key') and not kwargs.get('from_name'): + ret['comment'] = 'Slack from name is missing.' return ret - if not message: - ret['comment'] = 'Slack message is missing: {0}'.format(message) + if not kwargs.get('message'): + ret['comment'] = 'Slack message is missing.' + return ret + + if __opts__['test']: + ret['comment'] = 'The following message is to be sent to Slack: {0}'.format(kwargs.get('message')) + ret['result'] = None return ret try: - result = __salt__['slack.post_message']( - channel=channel, - message=message, - from_name=from_name, - api_key=api_key, - icon=icon, - ) + if kwargs.get('api_key'): + result = __salt__['slack.post_message']( + channel=kwargs.get('channel'), + message=kwargs.get('message'), + from_name=kwargs.get('from_name'), + api_key=kwargs.get('api_key'), + icon=kwargs.get('icon'), + ) + elif kwargs.get('webhook'): + result = __salt__['slack.call_hook']( + message=kwargs.get('message'), + attachment=kwargs.get('attachment'), + color=kwargs.get('color', 'good'), + short=kwargs.get('short'), + identifier=kwargs.get('webhook'), + channel=kwargs.get('channel'), + username=kwargs.get('username'), + icon_emoji=kwargs.get('icon_emoji') + ) except SaltInvocationError as sie: ret['comment'] = 'Failed to send message ({0}): {1}'.format(sie, name) else: diff --git a/salt/states/smartos.py b/salt/states/smartos.py index f28317e55677..8b5447682b38 100644 --- a/salt/states/smartos.py +++ b/salt/states/smartos.py @@ -157,8 +157,7 @@ def _split_docker_uuid(uuid): if len(uuid) == 2: tag = uuid[1] repo = uuid[0] - if len(repo.split('/')) == 2: - return repo, tag + return repo, tag return None, None @@ -810,6 +809,19 @@ def vm_present(name, vmconfig, config=None): ret['result'] = False ret['comment'] = 'image {0} not installed'.format(vmconfig['image_uuid']) + # prepare disk.*.image_uuid + for disk in vmconfig['disks'] if 'disks' in vmconfig else []: + if 'image_uuid' in disk and disk['image_uuid'] not in __salt__['imgadm.list'](): + if config['auto_import']: + if not __opts__['test']: + res = __salt__['imgadm.import'](disk['image_uuid']) + if disk['image_uuid'] not in res: + ret['result'] = False + ret['comment'] = 'failed to import image {0}'.format(disk['image_uuid']) + else: + ret['result'] = False + ret['comment'] = 'image {0} not installed'.format(disk['image_uuid']) + # docker json-array handling if 'internal_metadata' in vmconfig: for var in vmconfig_docker_array: diff --git a/salt/states/ssh_auth.py b/salt/states/ssh_auth.py index 2aff1dc1e9d4..d73330dff175 100644 --- a/salt/states/ssh_auth.py +++ b/salt/states/ssh_auth.py @@ -44,6 +44,20 @@ - ssh-dss AAAAB3NzaCL0sQ9fJ5bYTEyY== user@domain - option3="value3" ssh-dss AAAAB3NzaC1kcQ9J5bYTEyY== other@testdomain - AAAAB3NzaC1kcQ9fJFF435bYTEyY== newcomment + + sshkeys: + ssh_auth.manage: + - user: root + - enc: ssh-rsa + - options: + - option1="value1" + - option2="value2 flag2" + - comment: myuser + - ssh_keys: + - AAAAB3NzaC1kc3MAAACBAL0sQ9fJ5bYTEyY== + - ssh-dss AAAAB3NzaCL0sQ9fJ5bYTEyY== user@domain + - option3="value3" ssh-dss AAAAB3NzaC1kcQ9J5bYTEyY== other@testdomain + - AAAAB3NzaC1kcQ9fJFF435bYTEyY== newcomment ''' # Import python libs @@ -125,7 +139,7 @@ def _present_test(user, name, enc, comment, options, source, config, fingerprint elif check == 'exists': result = True comment = ('The authorized host key {0} is already present ' - 'for user {1}'.format(name, user)) + 'for user {1}'.format(name, user)) return result, comment @@ -251,14 +265,7 @@ def present( fingerprint_hash_type The public key fingerprint hash type that the public key fingerprint - was originally hashed with. This defaults to ``md5`` if not specified. - - .. versionadded:: 2016.11.7 - - .. note:: - - The default value of the ``fingerprint_hash_type`` will change to - ``sha256`` in Salt 2017.7.0. + was originally hashed with. This defaults to ``sha256`` if not specified. ''' ret = {'name': name, 'changes': {}, @@ -325,7 +332,7 @@ def present( saltenv=__env__, fingerprint_hash_type=fingerprint_hash_type) else: - # Split keyline to get key und comment + # Split keyline to get key and comment keyline = keyline.split(' ') key_type = keyline[0] key_value = keyline[1] @@ -423,15 +430,9 @@ def absent(name, fingerprint_hash_type The public key fingerprint hash type that the public key fingerprint - was originally hashed with. This defaults to ``md5`` if not specified. + was originally hashed with. This defaults to ``sha256`` if not specified. .. versionadded:: 2016.11.7 - - .. note:: - - The default value of the ``fingerprint_hash_type`` will change to - ``sha256`` in Salt 2017.7.0. - ''' ret = {'name': name, 'changes': {}, @@ -506,3 +507,99 @@ def absent(name, ret['changes'][name] = 'Removed' return ret + + +def manage( + name, + ssh_keys, + user, + enc='ssh-rsa', + comment='', + source='', + options=None, + config='.ssh/authorized_keys', + fingerprint_hash_type=None, + **kwargs): + ''' + .. versionadded:: Neon + + Ensures that only the specified ssh_keys are present for the specified user + + ssh_keys + The SSH key to manage + + user + The user who owns the SSH authorized keys file to modify + + enc + Defines what type of key is being used; can be ed25519, ecdsa, ssh-rsa + or ssh-dss + + comment + The comment to be placed with the SSH public key + + source + The source file for the key(s). Can contain any number of public keys, + in standard "authorized_keys" format. If this is set, comment and enc + will be ignored. + + .. note:: + The source file must contain keys in the format `` + ``. If you have generated a keypair using PuTTYgen, then you + will need to do the following to retrieve an OpenSSH-compatible public + key. + + 1. In PuTTYgen, click ``Load``, and select the *private* key file (not + the public key), and click ``Open``. + 2. Copy the public key from the box labeled ``Public key for pasting + into OpenSSH authorized_keys file``. + 3. Paste it into a new file. + + options + The options passed to the keys, pass a list object + + config + The location of the authorized keys file relative to the user's home + directory, defaults to ".ssh/authorized_keys". Token expansion %u and + %h for username and home path supported. + + fingerprint_hash_type + The public key fingerprint hash type that the public key fingerprint + was originally hashed with. This defaults to ``sha256`` if not specified. + ''' + ret = {'name': '', + 'changes': {}, + 'result': True, + 'comment': ''} + + all_potential_keys = [] + for ssh_key in ssh_keys: + # gather list potential ssh keys for removal comparison + # options, enc, and comments could be in the mix + all_potential_keys.extend(ssh_key.split(' ')) + existing_keys = __salt__['ssh.auth_keys'](user=user).keys() + remove_keys = set(existing_keys).difference(all_potential_keys) + for remove_key in remove_keys: + if __opts__['test']: + remove_comment = '{0} Key set for removal'.format(remove_key) + ret['comment'] = remove_comment + ret['result'] = None + else: + remove_comment = absent(remove_key, user)['comment'] + ret['changes'][remove_key] = remove_comment + + for ssh_key in ssh_keys: + run_return = present(ssh_key, user, enc, comment, source, + options, config, fingerprint_hash_type, **kwargs) + if run_return['changes']: + ret['changes'].update(run_return['changes']) + else: + ret['comment'] += '\n' + run_return['comment'] + ret['comment'] = ret['comment'].strip() + + if run_return['result'] is None: + ret['result'] = None + elif not run_return['result']: + ret['result'] = False + + return ret diff --git a/salt/states/user.py b/salt/states/user.py index 4ba985b10268..7b05e277c749 100644 --- a/salt/states/user.py +++ b/salt/states/user.py @@ -161,13 +161,13 @@ def _changes(name, if fullname is not None and lusr['fullname'] != fullname: change['fullname'] = fullname if win_homedrive and lusr['homedrive'] != win_homedrive: - change['homedrive'] = win_homedrive + change['win_homedrive'] = win_homedrive if win_profile and lusr['profile'] != win_profile: - change['profile'] = win_profile + change['win_profile'] = win_profile if win_logonscript and lusr['logonscript'] != win_logonscript: - change['logonscript'] = win_logonscript + change['win_logonscript'] = win_logonscript if win_description and lusr['description'] != win_description: - change['description'] = win_description + change['win_description'] = win_description # MacOS doesn't have full GECOS support, so check for the "ch" functions # and ignore these parameters if these functions do not exist. @@ -270,7 +270,7 @@ def present(name, gid The id of the default group to assign to the user. Either a group name or gid can be used. If not specified, and the user does not exist, then - he next available gid will be assigned. + the next available gid will be assigned. gid_from_name : False If ``True``, the default group id will be set to the id of the group diff --git a/salt/states/virt.py b/salt/states/virt.py index d411f864cd68..8504d7609125 100644 --- a/salt/states/virt.py +++ b/salt/states/virt.py @@ -145,35 +145,45 @@ def keys(name, basepath='/etc/pki', **kwargs): return ret -def _virt_call(domain, function, section, comment, +def _virt_call(domain, function, section, comment, state=None, connection=None, username=None, password=None, **kwargs): ''' Helper to call the virt functions. Wildcards supported. - :param domain: - :param function: - :param section: - :param comment: - :return: + :param domain: the domain to apply the function on. Can contain wildcards. + :param function: virt function to call + :param section: key for the changed domains in the return changes dictionary + :param comment: comment to return + :param state: the expected final state of the VM. If None the VM state won't be checked. + :return: the salt state return ''' ret = {'name': domain, 'changes': {}, 'result': True, 'comment': ''} targeted_domains = fnmatch.filter(__salt__['virt.list_domains'](), domain) changed_domains = list() ignored_domains = list() + noaction_domains = list() for targeted_domain in targeted_domains: try: - response = __salt__['virt.{0}'.format(function)](targeted_domain, - connection=connection, - username=username, - password=password, - **kwargs) - if isinstance(response, dict): - response = response['name'] - changed_domains.append({'domain': targeted_domain, function: response}) + action_needed = True + # If a state has been provided, use it to see if we have something to do + if state is not None: + domain_state = __salt__['virt.vm_state'](targeted_domain) + action_needed = domain_state.get(targeted_domain) != state + if action_needed: + response = __salt__['virt.{0}'.format(function)](targeted_domain, + connection=connection, + username=username, + password=password, + **kwargs) + if isinstance(response, dict): + response = response['name'] + changed_domains.append({'domain': targeted_domain, function: response}) + else: + noaction_domains.append(targeted_domain) except libvirt.libvirtError as err: ignored_domains.append({'domain': targeted_domain, 'issue': six.text_type(err)}) if not changed_domains: - ret['result'] = False + ret['result'] = not ignored_domains and bool(targeted_domains) ret['comment'] = 'No changes had happened' if ignored_domains: ret['changes'] = {'ignored': ignored_domains} @@ -206,7 +216,7 @@ def stopped(name, connection=None, username=None, password=None): virt.stopped ''' - return _virt_call(name, 'shutdown', 'stopped', "Machine has been shut down", + return _virt_call(name, 'shutdown', 'stopped', 'Machine has been shut down', state='shutdown', connection=connection, username=username, password=password) @@ -231,8 +241,7 @@ def powered_off(name, connection=None, username=None, password=None): domain_name: virt.stopped ''' - - return _virt_call(name, 'stop', 'unpowered', 'Machine has been powered off', + return _virt_call(name, 'stop', 'unpowered', 'Machine has been powered off', state='shutdown', connection=connection, username=username, password=password) @@ -389,8 +398,8 @@ def running(name, try: try: - __salt__['virt.vm_state'](name) - if __salt__['virt.vm_state'](name) != 'running': + domain_state = __salt__['virt.vm_state'](name) + if domain_state.get(name) != 'running': action_msg = 'started' if update: status = __salt__['virt.update'](name, @@ -670,7 +679,7 @@ def network_running(name, try: info = __salt__['virt.network_info'](name, connection=connection, username=username, password=password) if info: - if info['active']: + if info[name]['active']: ret['comment'] = 'Network {0} exists and is running'.format(name) else: __salt__['virt.network_start'](name, connection=connection, username=username, password=password) @@ -680,7 +689,7 @@ def network_running(name, __salt__['virt.network_define'](name, bridge, forward, - vport, + vport=vport, tag=tag, autostart=autostart, start=True, @@ -744,11 +753,11 @@ def pool_running(name, - owner: 1000 - group: 100 - source: - - dir: samba_share - - hosts: - one.example.com - two.example.com - - format: cifs + dir: samba_share + hosts: + - one.example.com + - two.example.com + format: cifs - autostart: True ''' @@ -761,7 +770,7 @@ def pool_running(name, try: info = __salt__['virt.pool_info'](name, connection=connection, username=username, password=password) if info: - if info['state'] == 'running': + if info[name]['state'] == 'running': ret['comment'] = 'Pool {0} exists and is running'.format(name) else: __salt__['virt.pool_start'](name, connection=connection, username=username, password=password) @@ -795,6 +804,12 @@ def pool_running(name, connection=connection, username=username, password=password) + + __salt__['virt.pool_start'](name, + connection=connection, + username=username, + password=password) + ret['changes'][name] = 'Pool defined and started' ret['comment'] = 'Pool {0} defined and started'.format(name) except libvirt.libvirtError as err: @@ -802,3 +817,113 @@ def pool_running(name, ret['result'] = False return ret + + +def pool_deleted(name, + purge=False, + connection=None, + username=None, + password=None): + ''' + Deletes a virtual storage pool. + + :param name: the name of the pool to delete. + :param purge: + if ``True``, the volumes contained in the pool will be deleted as well as the pool itself. + Note that these will be lost for ever. If ``False`` the pool will simply be undefined. + (Default: ``False``) + :param connection: libvirt connection URI, overriding defaults + :param username: username to connect with, overriding defaults + :param password: password to connect with, overriding defaults + + In order to be purged a storage pool needs to be running to get the list of volumes to delete. + + Some libvirt storage drivers may not implement deleting, those actions are implemented on a + best effort idea. In any case check the result's comment property to see if any of the action + was unsupported. + + .. code-block::yaml + + pool_name: + uyuni_virt.pool_deleted: + - purge: True + + .. versionadded:: Neon + ''' + ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + + try: + info = __salt__['virt.pool_info'](name, connection=connection, username=username, password=password) + if info: + ret['changes']['stopped'] = False + ret['changes']['deleted'] = False + ret['changes']['undefined'] = False + ret['changes']['deleted_volumes'] = [] + unsupported = [] + + if info[name]['state'] == 'running': + if purge: + unsupported_volume_delete = ['iscsi', 'iscsi-direct', 'mpath', 'scsi'] + if info[name]['type'] not in unsupported_volume_delete: + __salt__['virt.pool_refresh'](name, + connection=connection, + username=username, + password=password) + volumes = __salt__['virt.pool_list_volumes'](name, + connection=connection, + username=username, + password=password) + for volume in volumes: + # Not supported for iSCSI and SCSI drivers + deleted = __opts__['test'] + if not __opts__['test']: + deleted = __salt__['virt.volume_delete'](name, + volume, + connection=connection, + username=username, + password=password) + if deleted: + ret['changes']['deleted_volumes'].append(volume) + else: + unsupported.append('deleting volume') + + if not __opts__['test']: + ret['changes']['stopped'] = __salt__['virt.pool_stop'](name, + connection=connection, + username=username, + password=password) + else: + ret['changes']['stopped'] = True + + if purge: + supported_pool_delete = ['dir', 'fs', 'netfs', 'logical', 'vstorage', 'zfs'] + if info[name]['type'] in supported_pool_delete: + if not __opts__['test']: + ret['changes']['deleted'] = __salt__['virt.pool_delete'](name, + connection=connection, + username=username, + password=password) + else: + ret['changes']['deleted'] = True + else: + unsupported.append('deleting pool') + + if not __opts__['test']: + ret['changes']['undefined'] = __salt__['virt.pool_undefine'](name, + connection=connection, + username=username, + password=password) + else: + ret['changes']['undefined'] = True + ret['result'] = None + + if unsupported: + ret['comment'] = 'Unsupported actions for pool of type "{0}": {1}'.format(info[name]['type'], + ', '.join(unsupported)) + else: + ret['comment'] = 'Storage pool could not be found: {0}'.format(name) + except libvirt.libvirtError as err: + ret['comment'] = 'Failed deleting pool: {0}'.format(err.get_error_message()) + ret['result'] = False + + return ret diff --git a/salt/states/win_iis.py b/salt/states/win_iis.py index e1ea140bf5f4..663d88e16af2 100644 --- a/salt/states/win_iis.py +++ b/salt/states/win_iis.py @@ -11,6 +11,7 @@ # Import python libs from __future__ import absolute_import, unicode_literals, print_function +from salt.ext.six.moves import map # Define the module's virtual name @@ -778,7 +779,7 @@ def remove_vdir(name, site, app='/'): def set_app(name, site, settings=None): # pylint: disable=anomalous-backslash-in-string - ''' + r''' .. versionadded:: 2017.7.0 Set the value of the setting for an IIS web application. @@ -865,3 +866,125 @@ def set_app(name, site, settings=None): ret['result'] = True return ret + + +def webconfiguration_settings(name, settings=None): + r''' + Set the value of webconfiguration settings. + + :param str name: The name of the IIS PSPath containing the settings. + Possible PSPaths are : + MACHINE, MACHINE/WEBROOT, IIS:\, IIS:\Sites\sitename, ... + :param dict settings: Dictionaries of dictionaries. + You can match a specific item in a collection with this syntax inside a key: + 'Collection[{name: site0}].logFile.directory' + + Example of usage for the ``MACHINE/WEBROOT`` PSPath: + + .. code-block:: yaml + + MACHINE-WEBROOT-level-security: + win_iis.webconfiguration_settings: + - name: 'MACHINE/WEBROOT' + - settings: + system.web/authentication/forms: + requireSSL: True + protection: "All" + credentials.passwordFormat: "SHA1" + system.web/httpCookies: + httpOnlyCookies: True + + Example of usage for the ``IIS:\Sites\site0`` PSPath: + + .. code-block:: yaml + + site0-IIS-Sites-level-security: + win_iis.webconfiguration_settings: + - name: 'IIS:\Sites\site0' + - settings: + system.webServer/httpErrors: + errorMode: "DetailedLocalOnly" + system.webServer/security/requestFiltering: + allowDoubleEscaping: False + verbs.Collection: + - verb: TRACE + allowed: False + fileExtensions.allowUnlisted: False + + Example of usage for the ``IIS:\`` PSPath with a collection matching: + + .. code-block:: yaml + + site0-IIS-level-security: + win_iis.webconfiguration_settings: + - name: 'IIS:\' + - settings: + system.applicationHost/sites: + 'Collection[{name: site0}].logFile.directory': 'C:\logs\iis\site0' + + ''' + + ret = {'name': name, + 'changes': {}, + 'comment': str(), + 'result': None} + + if not settings: + ret['comment'] = 'No settings to change provided.' + ret['result'] = True + return ret + + ret_settings = { + 'changes': {}, + 'failures': {}, + } + + settings_list = list() + + for filter, filter_settings in settings.items(): + for setting_name, value in filter_settings.items(): + settings_list.append({'filter': filter, 'name': setting_name, 'value': value}) + + current_settings_list = __salt__['win_iis.get_webconfiguration_settings'](name=name, settings=settings_list) + for idx, setting in enumerate(settings_list): + + is_collection = setting['name'].split('.')[-1] == 'Collection' + # If this is a new setting and not an update to an existing setting + if len(current_settings_list) <= idx: + ret_settings['changes'][setting['filter'] + '.' + setting['name']] = {'old': {}, + 'new': settings_list[idx]['value']} + elif ((is_collection and list(map(dict, setting['value'])) != list(map(dict, current_settings_list[idx]['value']))) + or (not is_collection and str(setting['value']) != str(current_settings_list[idx]['value']))): + ret_settings['changes'][setting['filter'] + '.' + setting['name']] = {'old': current_settings_list[idx]['value'], + 'new': settings_list[idx]['value']} + if not ret_settings['changes']: + ret['comment'] = 'Settings already contain the provided values.' + ret['result'] = True + return ret + elif __opts__['test']: + ret['comment'] = 'Settings will be changed.' + ret['changes'] = ret_settings + return ret + + success = __salt__['win_iis.set_webconfiguration_settings'](name=name, settings=settings_list) + + new_settings_list = __salt__['win_iis.get_webconfiguration_settings'](name=name, settings=settings_list) + for idx, setting in enumerate(settings_list): + + is_collection = setting['name'].split('.')[-1] == 'Collection' + if ((is_collection and setting['value'] != new_settings_list[idx]['value']) + or (not is_collection and str(setting['value']) != str(new_settings_list[idx]['value']))): + ret_settings['failures'][setting['filter'] + '.' + setting['name']] = {'old': current_settings_list[idx]['value'], + 'new': new_settings_list[idx]['value']} + ret_settings['changes'].get(setting['filter'] + '.' + setting['name'], None) + + if ret_settings['failures']: + ret['comment'] = 'Some settings failed to change.' + ret['changes'] = ret_settings + ret['result'] = False + else: + ret['comment'] = 'Set settings to contain the provided values.' + ret['changes'] = ret_settings['changes'] + ret['result'] = success + + return ret diff --git a/salt/states/win_path.py b/salt/states/win_path.py index af956d59b89c..133378a0dcf2 100644 --- a/salt/states/win_path.py +++ b/salt/states/win_path.py @@ -27,8 +27,6 @@ def absent(name): ''' Remove the directory from the SYSTEM path - index: where the directory should be placed in the PATH (default: 0) - Example: .. code-block:: yaml diff --git a/salt/states/zabbix_host.py b/salt/states/zabbix_host.py index 23220ad4143b..3e3ade5e5dda 100644 --- a/salt/states/zabbix_host.py +++ b/salt/states/zabbix_host.py @@ -7,9 +7,9 @@ ''' from __future__ import absolute_import, print_function, unicode_literals -from json import loads, dumps from copy import deepcopy from salt.ext import six +import salt.utils.json def __virtual__(): @@ -101,7 +101,7 @@ def _interface_format(interfaces_data): return list() interface_attrs = ('ip', 'dns', 'main', 'type', 'useip', 'port') - interfaces_json = loads(dumps(interfaces_data)) + interfaces_json = salt.utils.json.loads(salt.utils.json.dumps(interfaces_data)) interfaces_dict = dict() for interface in interfaces_json: @@ -122,7 +122,7 @@ def _interface_format(interfaces_data): interface_type = interface_ports[value['type'].lower()][0] main = '1' if six.text_type(value.get('main', 'true')).lower() == 'true' else '0' useip = '1' if six.text_type(value.get('useip', 'true')).lower() == 'true' else '0' - interface_ip = value.get('ip') + interface_ip = value.get('ip', '') dns = value.get('dns', key) port = six.text_type(value.get('port', interface_ports[value['type'].lower()][1])) diff --git a/salt/states/zabbix_user.py b/salt/states/zabbix_user.py index 0d6178104b8f..632390b30b85 100644 --- a/salt/states/zabbix_user.py +++ b/salt/states/zabbix_user.py @@ -9,10 +9,10 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -from json import loads, dumps from copy import deepcopy # Import Salt libs +import salt.utils.json from salt.ext import six from salt.exceptions import SaltException @@ -196,7 +196,7 @@ def _media_format(medias_data): ''' if not medias_data: return list() - medias_json = loads(dumps(medias_data)) + medias_json = salt.utils.json.loads(salt.utils.json.dumps(medias_data)) medias_attr = ('active', 'mediatype', 'period', 'severity', 'sendto') media_type = {'mail': 1, 'jabber': 2, 'sms': 3} media_severities = ('D', 'H', 'A', 'W', 'I', 'N') diff --git a/salt/states/zpool.py b/salt/states/zpool.py index ca8dd86dc9f2..683f95f92ec2 100644 --- a/salt/states/zpool.py +++ b/salt/states/zpool.py @@ -9,8 +9,8 @@ .. versionadded:: 2016.3.0 .. versionchanged:: 2018.3.1 - Big refactor to remove duplicate code, better type converions and improved - consistancy in output. + Big refactor to remove duplicate code, better type conversions and improved + consistency in output. .. code-block:: yaml @@ -387,7 +387,7 @@ def absent(name, export=False, force=False): name : string name of storage pool export : boolean - export instread of destroy the zpool if present + export instead of destroy the zpool if present force : boolean force destroy or export diff --git a/salt/templates/rh_ip/rh8_eth.jinja b/salt/templates/rh_ip/rh8_eth.jinja new file mode 100644 index 000000000000..83604a214ebf --- /dev/null +++ b/salt/templates/rh_ip/rh8_eth.jinja @@ -0,0 +1,56 @@ +DEVICE="{{name}}" +{% if nickname %}NAME="{{nickname}}" +{%endif%}{% if hwaddr %}HWADDR="{{hwaddr}}" +{%endif%}{% if macaddr %}MACADDR="{{macaddr}}" +{%endif%}{% if uuid %}UUID="{{uuid}}" +{%endif%}{% if userctl %}USERCTL="{{userctl}}" +{%endif%}{% if master %}MASTER="{{master}}" +{%endif%}{% if slave %}SLAVE="{{slave}}" +{%endif%}{% if vlan %}VLAN="{{vlan}}" +{% if reorder_hdr %}REORDER_HDR="{{reorder_hdr}}" +{%endif%}{% if vlan_id %}VID="{{vlan_id}}" +{%endif%}{% if phys_dev %}PHYSDEV="{{phys_dev}}" +{%endif%}{%endif%}{% if devtype %}TYPE="{{devtype}}" +{%endif%}{% if proto %}BOOTPROTO="{{proto}}" +{%endif%}{% if onboot %}ONBOOT="{{onboot}}" +{%endif%}{% if onparent %}ONPARENT={{onparent}} +{%endif%}{% if ipv4_failure_fatal %}IPV4_FAILURE_FATAL="{{ipv4_failure_fatal}}" +{%endif%}{% if ipaddr %}IPADDR="{{ipaddr}}" +{%endif%}{% if ipaddr_start %}IPADDR_START="{{ipaddr_start}}" +{%endif%}{% if ipaddr_end %}IPADDR_END="{{ipaddr_end}}" +{%endif%}{% if clonenum_start %}CLONENUM_START="{{clonenum_start}}" +{%endif%}{% if netmask %}NETMASK="{{netmask}}" +{%endif%}{% if prefix %}PREFIX="{{prefix}}" +{%endif%}{% if ipaddrs %}{% for i in ipaddrs -%} +IPADDR{{loop.index}}="{{i['ipaddr']}}" +PREFIX{{loop.index}}="{{i['prefix']}}" +{% endfor -%} +{%endif%}{% if gateway %}GATEWAY="{{gateway}}" +{%endif%}{% if enable_ipv6 %}IPV6INIT="yes" +{% if ipv6_autoconf %}IPV6_AUTOCONF="{{ipv6_autoconf}}" +{%endif%}{% if dhcpv6c %}DHCPV6C="{{dhcpv6c}}" +{%endif%}{% if ipv6addr %}IPV6ADDR="{{ipv6addr}}" +{%endif%}{% if ipv6gateway %}IPV6_DEFAULTGW="{{ipv6gateway}}" +{%endif%}{% if ipv6addrs %}IPV6ADDR_SECONDARIES="{{ ipv6addrs|join(' ') }}" +{%endif%}{% if ipv6_peerdns %}IPV6_PEERDNS="{{ipv6_peerdns}}" +{%endif%}{% if ipv6_defroute %}IPV6_DEFROUTE="{{ipv6_defroute}}" +{%endif%}{% if ipv6_peerroutes %}IPV6_PEERROUTES="{{ipv6_peerroutes}}" +{%endif%}{%endif%}{% if srcaddr %}SRCADDR="{{srcaddr}}" +{%endif%}{% if peerdns %}PEERDNS="{{peerdns}}" +{%endif%}{% if peerroutes %}PEERROUTES="{{peerroutes}}" +{%endif%}{% if peerntp %}PEERNTP="{{peerntp}}" +{%endif%}{% if defroute %}DEFROUTE="{{defroute}}" +{%endif%}{% if bridge %}BRIDGE="{{bridge}}" +{%endif%}{% if stp %}STP="{{stp}}" +{%endif%}{% if delay or delay == 0 %}DELAY="{{delay}}" +{%endif%}{% if mtu %}MTU="{{mtu}}" +{%endif%}{% if zone %}ZONE="{{zone}}" +{%endif%}{% if my_inner_ipaddr %}MY_INNER_IPADDR={{my_inner_ipaddr}} +{%endif%}{% if my_outer_ipaddr %}MY_OUTER_IPADDR={{my_outer_ipaddr}} +{%endif%}{% if bonding %}BONDING_OPTS="{%for item in bonding %}{{item}}={{bonding[item]}} {%endfor%}" +{%endif%}{% if ethtool %}ETHTOOL_OPTS="{%for item in ethtool %}{{item}} {{ethtool[item]}} {%endfor%}" +{%endif%}{% if domain %}DOMAIN="{{ domain|join(' ') }}" +{%endif%}{% if nm_controlled %}NM_CONTROLLED="{{nm_controlled}}" +{% endif %}{% for server in dns -%} +DNS{{loop.index}}="{{server}}" +{% endfor -%} diff --git a/salt/transport/client.py b/salt/transport/client.py index 3daf3a5e0099..c5540ac3e04f 100644 --- a/salt/transport/client.py +++ b/salt/transport/client.py @@ -25,6 +25,12 @@ def factory(opts, **kwargs): sync = SyncWrapper(AsyncReqChannel.factory, (opts,), kwargs) return sync + def close(self): + ''' + Close the channel + ''' + raise NotImplementedError() + def send(self, load, tries=3, timeout=60, raw=False): ''' Send "load" to the master. @@ -38,6 +44,12 @@ def crypted_transfer_decode_dictentry(self, load, dictkey=None, tries=3, timeout ''' raise NotImplementedError() + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + class PushChannel(object): ''' @@ -113,8 +125,11 @@ def factory(cls, opts, **kwargs): import salt.transport.tcp return salt.transport.tcp.AsyncTCPReqChannel(opts, **kwargs) elif ttype == 'local': - import salt.transport.local - return salt.transport.local.AsyncLocalChannel(opts, **kwargs) + raise Exception( + 'There\'s no AsyncLocalChannel implementation yet' + ) + # import salt.transport.local + # return salt.transport.local.AsyncLocalChannel(opts, **kwargs) else: raise Exception( 'Channels are only defined for tcp, zeromq, and local' @@ -134,6 +149,18 @@ def crypted_transfer_decode_dictentry(self, load, dictkey=None, tries=3, timeout ''' raise NotImplementedError() + def close(self): + ''' + Close the channel + ''' + raise NotImplementedError() + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + class AsyncPubChannel(AsyncChannel): ''' @@ -164,8 +191,11 @@ def factory(cls, opts, **kwargs): import salt.transport.tcp return salt.transport.tcp.AsyncTCPPubChannel(opts, **kwargs) elif ttype == 'local': # TODO: - import salt.transport.local - return salt.transport.local.AsyncLocalPubChannel(opts, **kwargs) + raise Exception( + 'There\'s no AsyncLocalPubChannel implementation yet' + ) + # import salt.transport.local + # return salt.transport.local.AsyncLocalPubChannel(opts, **kwargs) else: raise Exception( 'Channels are only defined for tcp, zeromq, and local' @@ -178,12 +208,24 @@ def connect(self): ''' raise NotImplementedError() + def close(self): + ''' + Close the channel + ''' + raise NotImplementedError() + def on_recv(self, callback): ''' When jobs are received pass them (decoded) to callback ''' raise NotImplementedError() + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + class AsyncPushChannel(object): ''' diff --git a/salt/transport/frame.py b/salt/transport/frame.py index 33d0c0d91703..88b595184ec7 100644 --- a/salt/transport/frame.py +++ b/salt/transport/frame.py @@ -4,7 +4,7 @@ ''' # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import msgpack +import salt.utils.msgpack from salt.ext import six @@ -18,7 +18,7 @@ def frame_msg(body, header=None, raw_body=False): # pylint: disable=unused-argu framed_msg['head'] = header framed_msg['body'] = body - return msgpack.dumps(framed_msg) + return salt.utils.msgpack.dumps(framed_msg) def frame_msg_ipc(body, header=None, raw_body=False): # pylint: disable=unused-argument @@ -35,9 +35,9 @@ def frame_msg_ipc(body, header=None, raw_body=False): # pylint: disable=unused- framed_msg['head'] = header framed_msg['body'] = body if six.PY2: - return msgpack.dumps(framed_msg) + return salt.utils.msgpack.dumps(framed_msg) else: - return msgpack.dumps(framed_msg, use_bin_type=True) + return salt.utils.msgpack.dumps(framed_msg, use_bin_type=True) def _decode_embedded_list(src): diff --git a/salt/transport/ipc.py b/salt/transport/ipc.py index dbcba18e5c5d..536b136a2342 100644 --- a/salt/transport/ipc.py +++ b/salt/transport/ipc.py @@ -5,14 +5,12 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import sys import errno import logging import socket import time -# Import 3rd-party libs -import msgpack - # Import Tornado libs import tornado import tornado.gen @@ -22,6 +20,7 @@ from tornado.ioloop import IOLoop, TimeoutError as TornadoTimeoutError from tornado.iostream import IOStream, StreamClosedError # Import Salt libs +import salt.utils.msgpack import salt.transport.client import salt.transport.frame from salt.ext import six @@ -165,7 +164,7 @@ def return_message(msg): else: return _null # msgpack deprecated `encoding` starting with version 0.5.2 - if msgpack.version >= (0, 5, 2): + if salt.utils.msgpack.version >= (0, 5, 2): # Under Py2 we still want raw to be set to True msgpack_kwargs = {'raw': six.PY2} else: @@ -173,7 +172,7 @@ def return_message(msg): msgpack_kwargs = {'encoding': None} else: msgpack_kwargs = {'encoding': 'utf-8'} - unpacker = msgpack.Unpacker(**msgpack_kwargs) + unpacker = salt.utils.msgpack.Unpacker(**msgpack_kwargs) while not stream.closed(): try: wire_bytes = yield stream.read_bytes(4096, partial=True) @@ -221,6 +220,7 @@ def close(self): if hasattr(self.sock, 'close'): self.sock.close() + # pylint: disable=W1701 def __del__(self): try: self.close() @@ -228,6 +228,7 @@ def __del__(self): # This is raised when Python's GC has collected objects which # would be needed when calling self.close() pass + # pylint: enable=W1701 class IPCClient(object): @@ -260,7 +261,7 @@ def __init__(self, socket_path, io_loop=None): self._closing = False self.stream = None # msgpack deprecated `encoding` starting with version 0.5.2 - if msgpack.version >= (0, 5, 2): + if salt.utils.msgpack.version >= (0, 5, 2): # Under Py2 we still want raw to be set to True msgpack_kwargs = {'raw': six.PY2} else: @@ -268,7 +269,7 @@ def __init__(self, socket_path, io_loop=None): msgpack_kwargs = {'encoding': None} else: msgpack_kwargs = {'encoding': 'utf-8'} - self.unpacker = msgpack.Unpacker(**msgpack_kwargs) + self.unpacker = salt.utils.msgpack.Unpacker(**msgpack_kwargs) def connected(self): return self.stream is not None and not self.stream.closed() @@ -338,17 +339,15 @@ def _connect(self, timeout=None): yield tornado.gen.sleep(1) + # pylint: disable=W1701 def __del__(self): try: self.close() - except socket.error as exc: - if exc.errno != errno.EBADF: - # If its not a bad file descriptor error, raise - raise except TypeError: # This is raised when Python's GC has collected objects which # would be needed when calling self.close() pass + # pylint: enable=W1701 def close(self): ''' @@ -364,7 +363,12 @@ def close(self): log.debug('Closing %s instance', self.__class__.__name__) if self.stream is not None and not self.stream.closed(): - self.stream.close() + try: + self.stream.close() + except socket.error as exc: + if exc.errno != errno.EBADF: + # If its not a bad file descriptor error, raise + six.reraise(*sys.exc_info()) class IPCMessageClient(IPCClient): @@ -567,6 +571,7 @@ def close(self): if hasattr(self.sock, 'close'): self.sock.close() + # pylint: disable=W1701 def __del__(self): try: self.close() @@ -574,6 +579,7 @@ def __del__(self): # This is raised when Python's GC has collected objects which # would be needed when calling self.close() pass + # pylint: enable=W1701 class IPCMessageSubscriber(IPCClient): @@ -724,6 +730,8 @@ def close(self): if exc and not isinstance(exc, StreamClosedError): log.error("Read future returned exception %r", exc) + # pylint: disable=W1701 def __del__(self): if IPCMessageSubscriber in globals(): self.close() + # pylint: enable=W1701 diff --git a/salt/transport/local.py b/salt/transport/local.py index 24871458c6db..25e521cde364 100644 --- a/salt/transport/local.py +++ b/salt/transport/local.py @@ -20,6 +20,13 @@ def __init__(self, opts, **kwargs): self.kwargs = kwargs self.tries = 0 + def close(self): + ''' + Close the local channel. + + Currently a NOOP + ''' + def send(self, load, tries=3, timeout=60, raw=False): if self.tries == 0: diff --git a/salt/transport/tcp.py b/salt/transport/tcp.py index 55421b9dcb77..7e9250668673 100644 --- a/salt/transport/tcp.py +++ b/salt/transport/tcp.py @@ -10,18 +10,20 @@ from __future__ import absolute_import, print_function, unicode_literals import errno import logging -import socket import os -import weakref +import socket +import sys import time import threading import traceback +import weakref # Import Salt Libs import salt.crypt import salt.utils.asynchronous import salt.utils.event import salt.utils.files +import salt.utils.msgpack import salt.utils.platform import salt.utils.process import salt.utils.verify @@ -54,7 +56,6 @@ # pylint: enable=import-error,no-name-in-module # Import third party libs -import msgpack try: from M2Crypto import RSA HAS_M2 = True @@ -74,7 +75,7 @@ import threading import multiprocessing import tornado.util - from salt.utils.process import SignalHandlingMultiprocessingProcess + from salt.utils.process import SignalHandlingProcess log = logging.getLogger(__name__) @@ -135,7 +136,7 @@ def _set_tcp_keepalive(sock, opts): if USE_LOAD_BALANCER: - class LoadBalancerServer(SignalHandlingMultiprocessingProcess): + class LoadBalancerServer(SignalHandlingProcess): ''' Raw TCP server which runs in its own process and will listen for incoming connections. Each incoming connection will be @@ -158,7 +159,6 @@ def __init__(self, opts, socket_queue, **kwargs): # process so that a register_after_fork() equivalent will work on # Windows. def __setstate__(self, state): - self._is_child = True self.__init__( state['opts'], state['socket_queue'], @@ -180,8 +180,10 @@ def close(self): self._socket.close() self._socket = None + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def run(self): ''' @@ -321,6 +323,7 @@ def close(self): if not loop_instance_map: del self.__class__.instance_map[self.io_loop] + # pylint: disable=W1701 def __del__(self): with self._refcount_lock: # Make sure we actually close no matter if something @@ -332,6 +335,7 @@ def __del__(self): if exc.errno != errno.EBADF: # If its not a bad file descriptor error, raise raise + # pylint: enable=W1701 def _package_load(self, load): return { @@ -437,8 +441,10 @@ def close(self): if hasattr(self, 'message_client'): self.message_client.close() + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def _package_load(self, load): return { @@ -580,7 +586,7 @@ def wrap_callback(body): if not isinstance(body, dict): # TODO: For some reason we need to decode here for things # to work. Fix this. - body = msgpack.loads(body) + body = salt.utils.msgpack.loads(body) if six.PY3: body = salt.transport.frame.decode_embedded_strs(body) ret = yield self._decode_payload(body) @@ -610,7 +616,7 @@ def close(self): # Ignore this condition and continue. pass else: - raise exc + six.reraise(*sys.exc_info()) self._socket.close() self._socket = None if hasattr(self.req_server, 'shutdown'): @@ -626,8 +632,10 @@ def close(self): raise log.exception('TCPReqServerChannel close generated an exception: %s', str(exc)) + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def pre_fork(self, process_manager): ''' @@ -770,7 +778,7 @@ def handle_stream(self, stream, address): ''' log.trace('Req client %s connected', address) self.clients.append((stream, address)) - unpacker = msgpack.Unpacker() + unpacker = salt.utils.msgpack.Unpacker() try: while True: wire_bytes = yield stream.read_bytes(4096, partial=True) @@ -888,8 +896,10 @@ class SaltMessageClientPool(salt.transport.MessageClientPool): def __init__(self, opts, args=None, kwargs=None): super(SaltMessageClientPool, self).__init__(SaltMessageClient, opts, args=args, kwargs=kwargs) + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def close(self): for message_client in self.message_clients: @@ -1003,8 +1013,10 @@ def close(self): self.connect_callback = None self.disconnect_callback = None + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def connect(self): ''' @@ -1065,7 +1077,7 @@ def _stream_return(self): not self._connecting_future.done() or self._connecting_future.result() is not True): yield self._connecting_future - unpacker = msgpack.Unpacker() + unpacker = salt.utils.msgpack.Unpacker() while not self._closing: try: self._read_until_future = self._stream.read_bytes(4096, partial=True) @@ -1245,8 +1257,10 @@ def close(self): # 'StreamClosedError' when the stream is closed. self._read_until_future.exception() + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 class PubServer(tornado.tcpserver.TCPServer, object): @@ -1285,8 +1299,10 @@ def close(self): return self._closing = True + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def _add_client_present(self, client): id_ = client.id_ @@ -1341,7 +1357,7 @@ def _remove_client_present(self, client): @tornado.gen.coroutine def _stream_read(self, client): - unpacker = msgpack.Unpacker() + unpacker = salt.utils.msgpack.Unpacker() while not self._closing: try: client._read_until_future = client.stream.read_bytes(4096, partial=True) @@ -1528,7 +1544,7 @@ def publish(self, load): int_payload = {'payload': self.serial.dumps(payload)} # add some targeting stuff for lists only (for now) - if load['tgt_type'] == 'list': + if load['tgt_type'] == 'list' and not self.opts.get("order_masters", False): if isinstance(load['tgt'], six.string_types): # Fetch a list of minions that match _res = self.ckminions.check_minions(load['tgt'], diff --git a/salt/transport/zeromq.py b/salt/transport/zeromq.py index 1d9a4689e9ed..8197a8746b02 100644 --- a/salt/transport/zeromq.py +++ b/salt/transport/zeromq.py @@ -246,6 +246,7 @@ def close(self): if not loop_instance_map: del self.__class__.instance_map[self._io_loop] + # pylint: disable=W1701 def __del__(self): with self._refcount_lock: # Make sure we actually close no matter if something @@ -257,6 +258,7 @@ def __del__(self): if exc.errno != errno.EBADF: # If its not a bad file descriptor error, raise raise + # pylint: enable=W1701 @property def master_uri(self): @@ -497,8 +499,10 @@ def destroy(self): ) self.close() + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 # TODO: this is the time to see if we are connected, maybe use the req channel to guess? @tornado.gen.coroutine @@ -617,7 +621,7 @@ def zmq_device(self): except zmq.ZMQError as exc: if exc.errno == errno.EINTR: continue - raise exc + six.reraise(*sys.exc_info()) except (KeyboardInterrupt, SystemExit): break @@ -942,7 +946,7 @@ def _publish_daemon(self, log_queue=None): except zmq.ZMQError as exc: if exc.errno == errno.EINTR: continue - raise exc + six.reraise(*sys.exc_info()) except KeyboardInterrupt: log.trace('Publish daemon caught Keyboard interupt, tearing down') @@ -1082,8 +1086,10 @@ def destroy(self): ) self.close() + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 # TODO: unit tests! @@ -1160,8 +1166,10 @@ def destroy(self): ) self.close() + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def _init_socket(self): if hasattr(self, 'stream'): diff --git a/salt/utils/asynchronous.py b/salt/utils/asynchronous.py index 3f39b0965782..68b915eeb5a6 100644 --- a/salt/utils/asynchronous.py +++ b/salt/utils/asynchronous.py @@ -5,9 +5,12 @@ from __future__ import absolute_import, print_function, unicode_literals +import sys + import tornado.ioloop import tornado.concurrent import contextlib +from salt.ext import six from salt.utils import zeromq @@ -51,9 +54,9 @@ def __init__(self, method, args=tuple(), kwargs=None): def __getattribute__(self, key): try: return object.__getattribute__(self, key) - except AttributeError as ex: + except AttributeError: if key == 'asynchronous': - raise ex + six.reraise(*sys.exc_info()) attr = getattr(self.asynchronous, key) if hasattr(attr, '__call__'): def wrap(*args, **kwargs): @@ -64,19 +67,14 @@ def wrap(*args, **kwargs): ret = self._block_future(ret) return ret return wrap - - else: - return attr + return attr def _block_future(self, future): self.io_loop.add_future(future, lambda future: self.io_loop.stop()) self.io_loop.start() return future.result() - def __del__(self): - ''' - On deletion of the asynchronous wrapper, make sure to clean up the asynchronous stuff - ''' + def close(self): if hasattr(self, 'asynchronous'): if hasattr(self.asynchronous, 'close'): # Certain things such as streams should be closed before @@ -94,3 +92,17 @@ def __del__(self): elif hasattr(self, 'io_loop'): self.io_loop.close() del self.io_loop + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + # pylint: disable=W1701 + def __del__(self): + ''' + On deletion of the asynchronous wrapper, make sure to clean up the asynchronous stuff + ''' + self.close() + # pylint: enable=W1701 diff --git a/salt/utils/azurearm.py b/salt/utils/azurearm.py index 8bae9761598e..ecb4962c6d3c 100644 --- a/salt/utils/azurearm.py +++ b/salt/utils/azurearm.py @@ -4,7 +4,7 @@ .. versionadded:: 2019.2.0 -:maintainer: +:maintainer: :maturity: new :depends: * `azure `_ >= 2.0.0rc6 @@ -47,6 +47,9 @@ UserPassCredentials, ServicePrincipalCredentials, ) + from msrestazure.azure_active_directory import ( + MSIAuthentication + ) from msrestazure.azure_cloud import ( MetadataEndpointError, get_cloud_from_metadata_endpoint, @@ -110,6 +113,9 @@ def _determine_auth(**kwargs): credentials = UserPassCredentials(kwargs['username'], kwargs['password'], cloud_environment=cloud_env) + elif 'subscription_id' in kwargs: + credentials = MSIAuthentication(cloud_environment=cloud_env) + else: raise SaltInvocationError( 'Unable to determine credentials. ' @@ -133,7 +139,11 @@ def get_client(client_type, **kwargs): Dynamically load the selected client and return a management client object ''' client_map = {'compute': 'ComputeManagement', + 'authorization': 'AuthorizationManagement', + 'dns': 'DnsManagement', 'storage': 'StorageManagement', + 'managementlock': 'ManagementLock', + 'monitor': 'MonitorManagement', 'network': 'NetworkManagement', 'policy': 'Policy', 'resource': 'ResourceManagement', @@ -148,8 +158,10 @@ def get_client(client_type, **kwargs): map_value = client_map[client_type] - if client_type == 'subscription' or client_type == 'policy': + if client_type in ['policy', 'subscription']: module_name = 'resource' + elif client_type in ['managementlock']: + module_name = 'resource.locks' else: module_name = client_type @@ -164,6 +176,7 @@ def get_client(client_type, **kwargs): ) credentials, subscription_id, cloud_env = _determine_auth(**kwargs) + if client_type == 'subscription': client = Client( credentials=credentials, diff --git a/salt/utils/cache.py b/salt/utils/cache.py index 6581af9f870d..030a6e63a5a1 100644 --- a/salt/utils/cache.py +++ b/salt/utils/cache.py @@ -8,11 +8,6 @@ import re import time import logging -try: - import msgpack - HAS_MSGPACK = True -except ImportError: - HAS_MSGPACK = False # Import salt libs import salt.config @@ -20,6 +15,7 @@ import salt.utils.data import salt.utils.dictupdate import salt.utils.files +import salt.utils.msgpack # Import third party libs from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin @@ -136,10 +132,10 @@ def _read(self): ''' Read in from disk ''' - if not HAS_MSGPACK or not os.path.exists(self._path): + if not salt.utils.msgpack.HAS_MSGPACK or not os.path.exists(self._path): return with salt.utils.files.fopen(self._path, 'rb') as fp_: - cache = salt.utils.data.decode(msgpack.load(fp_, encoding=__salt_system_encoding__)) + cache = salt.utils.data.decode(salt.utils.msgpack.load(fp_, encoding=__salt_system_encoding__)) if "CacheDisk_cachetime" in cache: # new format self._dict = cache["CacheDisk_data"] self._key_cache_time = cache["CacheDisk_cachetime"] @@ -155,7 +151,7 @@ def _write(self): ''' Write out to disk ''' - if not HAS_MSGPACK: + if not salt.utils.msgpack.HAS_MSGPACK: return # TODO Add check into preflight to ensure dir exists # TODO Dir hashing? @@ -164,7 +160,7 @@ def _write(self): "CacheDisk_data": self._dict, "CacheDisk_cachetime": self._key_cache_time } - msgpack.dump(cache, fp_, use_bin_type=True) + salt.utils.msgpack.dump(cache, fp_, use_bin_type=True) class CacheCli(object): diff --git a/salt/utils/cloud.py b/salt/utils/cloud.py index 3f063e8c3b7f..52519375059b 100644 --- a/salt/utils/cloud.py +++ b/salt/utils/cloud.py @@ -5,24 +5,23 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals +import codecs +import copy import errno +import hashlib +import logging +import multiprocessing import os -import stat -import codecs +import pipes +import re import shutil -import uuid -import hashlib import socket +import stat +import subprocess +import sys import tempfile import time -import subprocess -import multiprocessing -import logging -import pipes -import msgpack import traceback -import copy -import re import uuid @@ -64,6 +63,7 @@ import salt.utils.event import salt.utils.files import salt.utils.path +import salt.utils.msgpack import salt.utils.platform import salt.utils.stringutils import salt.utils.versions @@ -1981,25 +1981,20 @@ def fire_event(key, msg, tag, sock_dir, args=None, transport='zeromq'): ''' Fire deploy action ''' - event = salt.utils.event.get_event( - 'master', - sock_dir, - transport, - listen=False) - - try: - event.fire_event(msg, tag) - except ValueError: - # We're using at least a 0.17.x version of salt - if isinstance(args, dict): - args[key] = msg - else: - args = {key: msg} - event.fire_event(args, tag) + with salt.utils.event.get_event('master', sock_dir, transport, listen=False) as event: + try: + event.fire_event(msg, tag) + except ValueError: + # We're using at least a 0.17.x version of salt + if isinstance(args, dict): + args[key] = msg + else: + args = {key: msg} + event.fire_event(args, tag) - # https://github.com/zeromq/pyzmq/issues/173#issuecomment-4037083 - # Assertion failed: get_load () == 0 (poller_base.cpp:32) - time.sleep(0.025) + # https://github.com/zeromq/pyzmq/issues/173#issuecomment-4037083 + # Assertion failed: get_load () == 0 (poller_base.cpp:32) + time.sleep(0.025) def _exec_ssh_cmd(cmd, error_msg=None, allow_failure=False, **kwargs): @@ -2072,7 +2067,7 @@ def scp_file(dest_path, contents=None, kwargs=None, local_file=None): os.close(tmpfd) except OSError as exc: if exc.errno != errno.EBADF: - raise exc + six.reraise(*sys.exc_info()) log.debug('Uploading %s to %s', dest_path, kwargs['hostname']) @@ -2143,7 +2138,7 @@ def scp_file(dest_path, contents=None, kwargs=None, local_file=None): os.remove(file_to_upload) except OSError as exc: if exc.errno != errno.ENOENT: - raise exc + six.reraise(*sys.exc_info()) return retcode @@ -2180,7 +2175,7 @@ def sftp_file(dest_path, contents=None, kwargs=None, local_file=None): os.close(tmpfd) except OSError as exc: if exc.errno != errno.EBADF: - raise exc + six.reraise(*sys.exc_info()) if local_file is not None: file_to_upload = local_file @@ -2245,7 +2240,7 @@ def sftp_file(dest_path, contents=None, kwargs=None, local_file=None): os.remove(file_to_upload) except OSError as exc: if exc.errno != errno.ENOENT: - raise exc + six.reraise(*sys.exc_info()) return retcode @@ -2378,19 +2373,19 @@ def check_auth(name, sock_dir=None, queue=None, timeout=300): This function is called from a multiprocess instance, to wait for a minion to become available to receive salt commands ''' - event = salt.utils.event.SaltEvent('master', sock_dir, listen=True) - starttime = time.mktime(time.localtime()) - newtimeout = timeout - log.debug('In check_auth, waiting for %s to become available', name) - while newtimeout > 0: - newtimeout = timeout - (time.mktime(time.localtime()) - starttime) - ret = event.get_event(full=True) - if ret is None: - continue - if ret['tag'] == 'salt/minion/{0}/start'.format(name): - queue.put(name) - newtimeout = 0 - log.debug('Minion %s is ready to receive commands', name) + with salt.utils.event.SaltEvent('master', sock_dir, listen=True) as event: + starttime = time.mktime(time.localtime()) + newtimeout = timeout + log.debug('In check_auth, waiting for %s to become available', name) + while newtimeout > 0: + newtimeout = timeout - (time.mktime(time.localtime()) - starttime) + ret = event.get_event(full=True) + if ret is None: + continue + if ret['tag'] == 'salt/minion/{0}/start'.format(name): + queue.put(name) + newtimeout = 0 + log.debug('Minion %s is ready to receive commands', name) def ip_to_int(ip): @@ -2634,7 +2629,7 @@ def cachedir_index_add(minion_id, profile, driver, provider, base=None): if os.path.exists(index_file): mode = 'rb' if six.PY3 else 'r' with salt.utils.files.fopen(index_file, mode) as fh_: - index = salt.utils.data.decode(msgpack.load(fh_, encoding=MSGPACK_ENCODING)) + index = salt.utils.data.decode(salt.utils.msgpack.msgpack.load(fh_, encoding=MSGPACK_ENCODING)) else: index = {} @@ -2651,7 +2646,7 @@ def cachedir_index_add(minion_id, profile, driver, provider, base=None): mode = 'wb' if six.PY3 else 'w' with salt.utils.files.fopen(index_file, mode) as fh_: - msgpack.dump(index, fh_, encoding=MSGPACK_ENCODING) + salt.utils.msgpack.dump(index, fh_, encoding=MSGPACK_ENCODING) unlock_file(index_file) @@ -2668,7 +2663,7 @@ def cachedir_index_del(minion_id, base=None): if os.path.exists(index_file): mode = 'rb' if six.PY3 else 'r' with salt.utils.files.fopen(index_file, mode) as fh_: - index = salt.utils.data.decode(msgpack.load(fh_, encoding=MSGPACK_ENCODING)) + index = salt.utils.data.decode(salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING)) else: return @@ -2677,7 +2672,7 @@ def cachedir_index_del(minion_id, base=None): mode = 'wb' if six.PY3 else 'w' with salt.utils.files.fopen(index_file, mode) as fh_: - msgpack.dump(index, fh_, encoding=MSGPACK_ENCODING) + salt.utils.msgpack.dump(index, fh_, encoding=MSGPACK_ENCODING) unlock_file(index_file) @@ -2735,7 +2730,7 @@ def request_minion_cachedir( path = os.path.join(base, 'requested', fname) mode = 'wb' if six.PY3 else 'w' with salt.utils.files.fopen(path, mode) as fh_: - msgpack.dump(data, fh_, encoding=MSGPACK_ENCODING) + salt.utils.msgpack.dump(data, fh_, encoding=MSGPACK_ENCODING) def change_minion_cachedir( @@ -2767,12 +2762,13 @@ def change_minion_cachedir( path = os.path.join(base, cachedir, fname) with salt.utils.files.fopen(path, 'r') as fh_: - cache_data = salt.utils.data.decode(msgpack.load(fh_, encoding=MSGPACK_ENCODING)) + cache_data = salt.utils.data.decode( + salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING)) cache_data.update(data) with salt.utils.files.fopen(path, 'w') as fh_: - msgpack.dump(cache_data, fh_, encoding=MSGPACK_ENCODING) + salt.utils.msgpack.dump(cache_data, fh_, encoding=MSGPACK_ENCODING) def activate_minion_cachedir(minion_id, base=None): @@ -2846,7 +2842,8 @@ def list_cache_nodes_full(opts=None, provider=None, base=None): minion_id = fname[:-2] # strip '.p' from end of msgpack filename mode = 'rb' if six.PY3 else 'r' with salt.utils.files.fopen(fpath, mode) as fh_: - minions[driver][prov][minion_id] = salt.utils.data.decode(msgpack.load(fh_, encoding=MSGPACK_ENCODING)) + minions[driver][prov][minion_id] = salt.utils.data.decode( + salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING)) return minions @@ -3007,7 +3004,7 @@ def cache_node_list(nodes, provider, opts): path = os.path.join(prov_dir, '{0}.p'.format(node)) mode = 'wb' if six.PY3 else 'w' with salt.utils.files.fopen(path, mode) as fh_: - msgpack.dump(nodes[node], fh_, encoding=MSGPACK_ENCODING) + salt.utils.msgpack.dump(nodes[node], fh_, encoding=MSGPACK_ENCODING) def cache_node(node, provider, opts): @@ -3033,7 +3030,7 @@ def cache_node(node, provider, opts): path = os.path.join(prov_dir, '{0}.p'.format(node['name'])) mode = 'wb' if six.PY3 else 'w' with salt.utils.files.fopen(path, mode) as fh_: - msgpack.dump(node, fh_, encoding=MSGPACK_ENCODING) + salt.utils.msgpack.dump(node, fh_, encoding=MSGPACK_ENCODING) def missing_node_cache(prov_dir, node_list, provider, opts): @@ -3108,7 +3105,8 @@ def diff_node_cache(prov_dir, node, new_data, opts): with salt.utils.files.fopen(path, 'r') as fh_: try: - cache_data = salt.utils.data.decode(msgpack.load(fh_, encoding=MSGPACK_ENCODING)) + cache_data = salt.utils.data.decode( + salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING)) except ValueError: log.warning('Cache for %s was corrupt: Deleting', node) cache_data = {} diff --git a/salt/utils/color.py b/salt/utils/color.py index bbbbd345a892..7ae63040abc9 100644 --- a/salt/utils/color.py +++ b/salt/utils/color.py @@ -10,7 +10,7 @@ # Import Salt libs from salt.ext import six -from salt.textformat import TextFormat +from salt.utils.textformat import TextFormat log = logging.getLogger(__name__) diff --git a/salt/utils/data.py b/salt/utils/data.py index 4ec7b3126246..848c73ef70a6 100644 --- a/salt/utils/data.py +++ b/salt/utils/data.py @@ -143,13 +143,13 @@ def compare_lists(old=None, new=None): Compare before and after results from various salt functions, returning a dict describing the changes that were made ''' - ret = dict() + ret = {} for item in new: if item not in old: - ret['new'] = item + ret.setdefault('new', []).append(item) for item in old: if item not in new: - ret['old'] = item + ret.setdefault('old', []).append(item) return ret diff --git a/salt/utils/decorators/__init__.py b/salt/utils/decorators/__init__.py index a6da91447a1c..ef87cee19940 100644 --- a/salt/utils/decorators/__init__.py +++ b/salt/utils/decorators/__init__.py @@ -331,7 +331,7 @@ def _call_function(self, kwargs): 'Unhandled exception occurred in function "%s: %s', self._function.__name__, error ) - raise error + six.reraise(*sys.exc_info()) else: raise CommandExecutionError("Function is deprecated, but the successor function was not found.") diff --git a/salt/utils/dictupdate.py b/salt/utils/dictupdate.py index 70b7a9c4b895..456e71337303 100644 --- a/salt/utils/dictupdate.py +++ b/salt/utils/dictupdate.py @@ -15,7 +15,14 @@ # Import 3rd-party libs import copy import logging + +# Import salt libs import salt.ext.six as six +from salt.defaults import DEFAULT_TARGET_DELIM +import salt.utils.data +from salt.exceptions import SaltInvocationError +from salt.utils.odict import OrderedDict +from salt.utils.decorators.jinja import jinja_filter log = logging.getLogger(__name__) @@ -55,8 +62,7 @@ def update(dest, upd, recursive_update=True, merge_lists=False): and isinstance(val, Mapping): ret = update(dest_subkey, val, merge_lists=merge_lists) dest[key] = ret - elif isinstance(dest_subkey, list) \ - and isinstance(val, list): + elif isinstance(dest_subkey, list) and isinstance(val, list): if merge_lists: merged = copy.deepcopy(dest_subkey) merged.extend([x for x in val if x not in merged]) @@ -66,15 +72,14 @@ def update(dest, upd, recursive_update=True, merge_lists=False): else: dest[key] = upd[key] return dest - else: - try: - for k in upd: - dest[k] = upd[k] - except AttributeError: - # this mapping is not a dict - for k in upd: - dest[k] = upd[k] - return dest + try: + for k in upd: + dest[k] = upd[k] + except AttributeError: + # this mapping is not a dict + for k in upd: + dest[k] = upd[k] + return dest def merge_list(obj_a, obj_b): @@ -132,3 +137,206 @@ def merge(obj_a, obj_b, strategy='smart', renderer='yaml', merge_lists=False): merged = merge_recurse(obj_a, obj_b) return merged + + +def ensure_dict_key( + in_dict, + keys, + delimiter=DEFAULT_TARGET_DELIM, + ordered_dict=False): + ''' + Ensures that in_dict contains the series of recursive keys defined in keys. + + :param dict in_dict: The dict to work with. + :param str keys: The delimited string with one or more keys. + :param str delimiter: The delimiter to use in `keys`. Defaults to ':'. + :param bool ordered_dict: Create OrderedDicts if keys are missing. + Default: create regular dicts. + :rtype: dict + :return: Returns the modified in-place `in_dict`. + ''' + if delimiter in keys: + a_keys = keys.split(delimiter) + else: + a_keys = [keys] + dict_pointer = in_dict + while a_keys: + current_key = a_keys.pop(0) + if current_key not in dict_pointer or not isinstance(dict_pointer[current_key], dict): + dict_pointer[current_key] = OrderedDict() if ordered_dict else {} + dict_pointer = dict_pointer[current_key] + return in_dict + + +def _dict_rpartition( + in_dict, + keys, + delimiter=DEFAULT_TARGET_DELIM, + ordered_dict=False): + ''' + Helper function to: + - Ensure all but the last key in `keys` exist recursively in `in_dict`. + - Return the dict at the one-to-last key, and the last key + + :param dict in_dict: The dict to work with. + :param str keys: The delimited string with one or more keys. + :param str delimiter: The delimiter to use in `keys`. Defaults to ':'. + :param bool ordered_dict: Create OrderedDicts if keys are missing. + Default: create regular dicts. + :rtype: tuple(dict, str) + :return: (The dict at the one-to-last key, the last key) + ''' + if delimiter in keys: + all_but_last_keys, _, last_key = keys.rpartition(delimiter) + ensure_dict_key(in_dict, + all_but_last_keys, + delimiter=delimiter, + ordered_dict=ordered_dict) + dict_pointer = salt.utils.data.traverse_dict(in_dict, + all_but_last_keys, + default=None, + delimiter=delimiter) + else: + dict_pointer = in_dict + last_key = keys + return dict_pointer, last_key + + +@jinja_filter('set_dict_key_value') +def set_dict_key_value( + in_dict, + keys, + value, + delimiter=DEFAULT_TARGET_DELIM, + ordered_dict=False): + ''' + Ensures that in_dict contains the series of recursive keys defined in keys. + Also sets whatever is at the end of `in_dict` traversed with `keys` to `value`. + + :param dict in_dict: The dictionary to work with + :param str keys: The delimited string with one or more keys. + :param any value: The value to assign to the nested dict-key. + :param str delimiter: The delimiter to use in `keys`. Defaults to ':'. + :param bool ordered_dict: Create OrderedDicts if keys are missing. + Default: create regular dicts. + :rtype: dict + :return: Returns the modified in-place `in_dict`. + ''' + dict_pointer, last_key = _dict_rpartition(in_dict, + keys, + delimiter=delimiter, + ordered_dict=ordered_dict) + dict_pointer[last_key] = value + return in_dict + + +@jinja_filter('update_dict_key_value') +def update_dict_key_value( + in_dict, + keys, + value, + delimiter=DEFAULT_TARGET_DELIM, + ordered_dict=False): + ''' + Ensures that in_dict contains the series of recursive keys defined in keys. + Also updates the dict, that is at the end of `in_dict` traversed with `keys`, + with `value`. + + :param dict in_dict: The dictionary to work with + :param str keys: The delimited string with one or more keys. + :param any value: The value to update the nested dict-key with. + :param str delimiter: The delimiter to use in `keys`. Defaults to ':'. + :param bool ordered_dict: Create OrderedDicts if keys are missing. + Default: create regular dicts. + :rtype: dict + :return: Returns the modified in-place `in_dict`. + ''' + dict_pointer, last_key = _dict_rpartition(in_dict, + keys, + delimiter=delimiter, + ordered_dict=ordered_dict) + if last_key not in dict_pointer or dict_pointer[last_key] is None: + dict_pointer[last_key] = OrderedDict() if ordered_dict else {} + try: + dict_pointer[last_key].update(value) + except AttributeError: + raise SaltInvocationError('The last key contains a {}, which cannot update.' + ''.format(type(dict_pointer[last_key]))) + except (ValueError, TypeError): + raise SaltInvocationError('Cannot update {} with a {}.' + ''.format(type(dict_pointer[last_key]), type(value))) + return in_dict + + +@jinja_filter('append_dict_key_value') +def append_dict_key_value( + in_dict, + keys, + value, + delimiter=DEFAULT_TARGET_DELIM, + ordered_dict=False): + ''' + Ensures that in_dict contains the series of recursive keys defined in keys. + Also appends `value` to the list that is at the end of `in_dict` traversed + with `keys`. + + :param dict in_dict: The dictionary to work with + :param str keys: The delimited string with one or more keys. + :param any value: The value to append to the nested dict-key. + :param str delimiter: The delimiter to use in `keys`. Defaults to ':'. + :param bool ordered_dict: Create OrderedDicts if keys are missing. + Default: create regular dicts. + :rtype: dict + :return: Returns the modified in-place `in_dict`. + ''' + dict_pointer, last_key = _dict_rpartition(in_dict, + keys, + delimiter=delimiter, + ordered_dict=ordered_dict) + if last_key not in dict_pointer or dict_pointer[last_key] is None: + dict_pointer[last_key] = [] + try: + dict_pointer[last_key].append(value) + except AttributeError: + raise SaltInvocationError('The last key contains a {}, which cannot append.' + ''.format(type(dict_pointer[last_key]))) + return in_dict + + +@jinja_filter('extend_dict_key_value') +def extend_dict_key_value( + in_dict, + keys, + value, + delimiter=DEFAULT_TARGET_DELIM, + ordered_dict=False): + ''' + Ensures that in_dict contains the series of recursive keys defined in keys. + Also extends the list, that is at the end of `in_dict` traversed with `keys`, + with `value`. + + :param dict in_dict: The dictionary to work with + :param str keys: The delimited string with one or more keys. + :param any value: The value to extend the nested dict-key with. + :param str delimiter: The delimiter to use in `keys`. Defaults to ':'. + :param bool ordered_dict: Create OrderedDicts if keys are missing. + Default: create regular dicts. + :rtype: dict + :return: Returns the modified in-place `in_dict`. + ''' + dict_pointer, last_key = _dict_rpartition( + in_dict, + keys, + delimiter=delimiter, + ordered_dict=ordered_dict) + if last_key not in dict_pointer or dict_pointer[last_key] is None: + dict_pointer[last_key] = [] + try: + dict_pointer[last_key].extend(value) + except AttributeError: + raise SaltInvocationError('The last key contains a {}, which cannot extend.' + ''.format(type(dict_pointer[last_key]))) + except TypeError: + raise SaltInvocationError('Cannot extend {} with a {}.' + ''.format(type(dict_pointer[last_key]), type(value))) + return in_dict diff --git a/salt/utils/event.py b/salt/utils/event.py index be82604a99bd..f37b65021a2f 100644 --- a/salt/utils/event.py +++ b/salt/utils/event.py @@ -418,6 +418,17 @@ def connect_pull(self, timeout=1): self.cpush = True return self.cpush + def close_pull(self): + ''' + Close the pusher connection (if established) + ''' + if not self.cpush: + return + + self.pusher.close() + self.pusher = None + self.cpush = False + @classmethod def unpack(cls, raw, serial=None): if serial is None: @@ -756,9 +767,9 @@ def fire_master(self, data, tag, timeout=1000): def destroy(self): if self.subscriber is not None: - self.subscriber.close() + self.close_pub() if self.pusher is not None: - self.pusher.close() + self.close_pull() if self._run_io_loop_sync and not self.keep_loop: self.io_loop.close() @@ -857,6 +868,7 @@ def set_event_handler(self, event_handler): # This will handle reconnects return self.subscriber.read_async(event_handler) + # pylint: disable=W1701 def __del__(self): # skip exceptions in destroy-- since destroy() doesn't cover interpreter # shutdown-- where globals start going missing @@ -864,6 +876,13 @@ def __del__(self): self.destroy() except Exception: pass + # pylint: enable=W1701 + + def __enter__(self): + return self + + def __exit__(self, *args): + self.destroy() class MasterEvent(SaltEvent): @@ -913,6 +932,15 @@ def fire_event(self, data, tag): if self.print_func is not None: self.print_func(tag, data) + def destroy(self): + self.event.destroy() + + def __enter__(self): + return self + + def __exit__(self, *args): + self.destroy() + class MinionEvent(SaltEvent): ''' @@ -1031,11 +1059,13 @@ def close(self): if hasattr(self, 'puller'): self.puller.close() + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 -class EventPublisher(salt.utils.process.SignalHandlingMultiprocessingProcess): +class EventPublisher(salt.utils.process.SignalHandlingProcess): ''' The interface that takes master events and republishes them out to anyone who wants to listen @@ -1050,7 +1080,6 @@ def __init__(self, opts, **kwargs): # We do this so that __init__ will be invoked on Windows in the child # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): - self._is_child = True self.__init__( state['opts'], log_queue=state['log_queue'], @@ -1140,11 +1169,13 @@ def _handle_signals(self, signum, sigframe): self.close() super(EventPublisher, self)._handle_signals(signum, sigframe) + # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 -class EventReturn(salt.utils.process.SignalHandlingMultiprocessingProcess): +class EventReturn(salt.utils.process.SignalHandlingProcess): ''' A dedicated process which listens to the master event bus and queues and forwards events to the specified returner. @@ -1174,7 +1205,6 @@ def __init__(self, opts, **kwargs): # We do this so that __init__ will be invoked on Windows in the child # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): - self._is_child = True self.__init__( state['opts'], log_queue=state['log_queue'], @@ -1342,13 +1372,11 @@ def fire_master(self, data, tag, preload=None): 'tok': self.auth.gen_token(b'salt'), }) - channel = salt.transport.client.ReqChannel.factory(self.opts) - try: - channel.send(load) - except Exception: - pass - finally: - channel.close() + with salt.transport.client.ReqChannel.factory(self.opts) as channel: + try: + channel.send(load) + except Exception as exc: + log.info('An exception occurred on fire_master: %s', exc, exc_info_on_loglevel=logging.DEBUG) return True def fire_running(self, running): @@ -1374,11 +1402,9 @@ def fire_running(self, running): 'tag': tag, 'data': running[stag], }) - channel = salt.transport.client.ReqChannel.factory(self.opts) - try: - channel.send(load) - except Exception: - pass - finally: - channel.close() + with salt.transport.client.ReqChannel.factory(self.opts) as channel: + try: + channel.send(load) + except Exception as exc: + log.info('An exception occurred on fire_master: %s', exc, exc_info_on_loglevel=logging.DEBUG) return True diff --git a/salt/utils/gitfs.py b/salt/utils/gitfs.py index 98ae03097b13..0dcb29d6b1fc 100644 --- a/salt/utils/gitfs.py +++ b/salt/utils/gitfs.py @@ -5,8 +5,8 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import copy import contextlib +import copy import errno import fnmatch import glob @@ -17,6 +17,7 @@ import shutil import stat import subprocess +import sys import time import tornado.ioloop import weakref @@ -823,7 +824,7 @@ def _lock(self, lock_type='update', failhard=False): 'by another master.') log.warning(msg) if failhard: - raise exc + six.reraise(*sys.exc_info()) return elif pid and pid_exists(pid): log.warning('Process %d has a %s %s lock (%s)', @@ -2819,7 +2820,7 @@ def _add_file_stat(fnd, mode): return _add_file_stat(fnd, blob_mode) except IOError as exc: if exc.errno != errno.ENOENT: - raise exc + six.reraise(*sys.exc_info()) with salt.utils.files.fopen(lk_fn, 'w'): pass @@ -2901,13 +2902,13 @@ def file_hash(self, load, fnd): return ret except IOError as exc: if exc.errno != errno.ENOENT: - raise exc + six.reraise(*sys.exc_info()) try: os.makedirs(os.path.dirname(hashdest)) except OSError as exc: if exc.errno != errno.EEXIST: - raise exc + six.reraise(*sys.exc_info()) ret['hsum'] = salt.utils.hashutils.get_hash(path, self.opts['hash_type']) with salt.utils.files.fopen(hashdest, 'w+') as fp_: @@ -2943,14 +2944,21 @@ def _file_lists(self, load, form): if cache_match is not None: return cache_match if refresh_cache: + log.trace('Start rebuilding gitfs file_list cache') ret = {'files': set(), 'symlinks': {}, 'dirs': set()} if salt.utils.stringutils.is_hex(load['saltenv']) \ or load['saltenv'] in self.envs(): for repo in self.remotes: + start = time.time() repo_files, repo_symlinks = repo.file_list(load['saltenv']) ret['files'].update(repo_files) ret['symlinks'].update(repo_symlinks) ret['dirs'].update(repo.dir_list(load['saltenv'])) + log.profile( + 'gitfs file_name cache rebuild repo=%s duration=%s seconds', + repo.id, + time.time() - start + ) ret['files'] = sorted(ret['files']) ret['dirs'] = sorted(ret['dirs']) @@ -2961,6 +2969,7 @@ def _file_lists(self, load, form): # NOTE: symlinks are organized in a dict instead of a list, however # the 'symlinks' key will be defined above so it will never get to # the default value in the call to ret.get() below. + log.trace('Finished rebuilding gitfs file_list cache') return ret.get(form, []) # Shouldn't get here, but if we do, this prevents a TypeError return {} if form == 'symlinks' else [] diff --git a/salt/utils/hashutils.py b/salt/utils/hashutils.py index cff79b2b8693..cc24d5fd0759 100644 --- a/salt/utils/hashutils.py +++ b/salt/utils/hashutils.py @@ -51,29 +51,39 @@ def base64_b64decode(instr): def base64_encodestring(instr): ''' - Encode a string as base64 using the "legacy" Python interface. + Encode a byte-like object as base64 using the "modern" Python interface. - Among other possible differences, the "legacy" encoder includes + Among other possible differences, the "modern" encoder includes a newline ('\\n') character after every 76 characters and always at the end of the encoded string. ''' + # Handles PY2 + if six.PY2: + return salt.utils.stringutils.to_unicode( + base64.encodestring(salt.utils.stringutils.to_bytes(instr)), + encoding='utf8' if salt.utils.platform.is_windows() else None + ) + + # Handles PY3 return salt.utils.stringutils.to_unicode( - base64.encodestring(salt.utils.stringutils.to_bytes(instr)), + base64.encodebytes(salt.utils.stringutils.to_bytes(instr)), encoding='utf8' if salt.utils.platform.is_windows() else None ) def base64_decodestring(instr): ''' - Decode a base64-encoded string using the "legacy" Python interface. + Decode a base64-encoded byte-like object using the "modern" Python interface. ''' - b = salt.utils.stringutils.to_bytes(instr) - try: - # PY3 - decoded = base64.decodebytes(b) - except AttributeError: - # PY2 - decoded = base64.decodestring(b) + bvalue = salt.utils.stringutils.to_bytes(instr) + + if six.PY3: + # Handle PY3 + decoded = base64.decodebytes(bvalue) + else: + # Handle PY2 + decoded = base64.decodestring(bvalue) + try: return salt.utils.stringutils.to_unicode( decoded, @@ -93,6 +103,7 @@ def md5_digest(instr): ) +@jinja_filter('sha1') def sha1_digest(instr): ''' Generate an sha1 hash of a given string. @@ -137,7 +148,17 @@ def hmac_signature(string, shared_secret, challenge_hmac): return valid_hmac == challenge -@jinja_filter('rand_str') # Remove this for Neon +@jinja_filter('hmac_compute') +def hmac_compute(string, shared_secret): + ''' + Create an hmac digest. + ''' + msg = salt.utils.stringutils.to_bytes(string) + key = salt.utils.stringutils.to_bytes(shared_secret) + hmac_hash = hmac.new(key, msg, hashlib.sha256).hexdigest() + return hmac_hash + + @jinja_filter('random_hash') def random_hash(size=9999999999, hash_type=None): ''' diff --git a/salt/utils/http.py b/salt/utils/http.py index c75ca73a8572..e86b042db26a 100644 --- a/salt/utils/http.py +++ b/salt/utils/http.py @@ -44,6 +44,7 @@ import salt.utils.data import salt.utils.files import salt.utils.json +import salt.utils.msgpack import salt.utils.network import salt.utils.platform import salt.utils.stringutils @@ -60,6 +61,7 @@ import salt.ext.six.moves.http_client import salt.ext.six.moves.http_cookiejar import salt.ext.six.moves.urllib.request as urllib_request +from salt.ext.six.moves import StringIO from salt.ext.six.moves.urllib.error import URLError from salt.ext.six.moves.urllib.parse import splitquery, urlparse from salt.ext.six.moves.urllib.parse import urlencode as _urlencode @@ -82,12 +84,6 @@ except ImportError: HAS_REQUESTS = False -try: - import msgpack - HAS_MSGPACK = True -except ImportError: - HAS_MSGPACK = False - try: import certifi HAS_CERTIFI = True @@ -104,6 +100,8 @@ def __decompressContent(coding, pgctnt): Currently supports identity/none, deflate, and gzip, which should cover 99%+ of the content on the internet. ''' + if not pgctnt: + return pgctnt log.trace("Decompressing %s byte content with compression type: %s", len(pgctnt), coding) @@ -122,9 +120,6 @@ def __decompressContent(coding, pgctnt): elif coding == "compress": raise ValueError("LZW compression is not currently supported") - elif coding == 'identity': - pass - log.trace("Content size after decompression: %s", len(pgctnt)) return pgctnt @@ -175,6 +170,9 @@ def query(url, agent=USERAGENT, hide_fields=None, raise_error=True, + formdata=False, + formdata_fieldname=None, + formdata_filename=None, **kwargs): ''' Query a resource, and decode the return data @@ -267,19 +265,19 @@ def query(url, if session_cookie_jar is None: session_cookie_jar = os.path.join(opts.get('cachedir', salt.syspaths.CACHE_DIR), 'cookies.session.p') - if persist_session is True and HAS_MSGPACK: + if persist_session is True and salt.utils.msgpack.HAS_MSGPACK: # TODO: This is hackish; it will overwrite the session cookie jar with # all cookies from this one connection, rather than behaving like a # proper cookie jar. Unfortunately, since session cookies do not # contain expirations, they can't be stored in a proper cookie jar. if os.path.isfile(session_cookie_jar): with salt.utils.files.fopen(session_cookie_jar, 'rb') as fh_: - session_cookies = msgpack.load(fh_) + session_cookies = salt.utils.msgpack.load(fh_) if isinstance(session_cookies, dict): header_dict.update(session_cookies) else: with salt.utils.files.fopen(session_cookie_jar, 'wb') as fh_: - msgpack.dump('', fh_) + salt.utils.msgpack.dump('', fh_) for header in header_list: comps = header.split(':') @@ -345,9 +343,22 @@ def query(url, log.error('The client-side certificate path that' ' was passed is not valid: %s', cert) - result = sess.request( - method, url, params=params, data=data, **req_kwargs - ) + if formdata: + if not formdata_fieldname: + ret['error'] = ('formdata_fieldname is required when formdata=True') + log.error(ret['error']) + return ret + result = sess.request( + method, + url, + params=params, + files={formdata_fieldname: (formdata_filename, StringIO(data))}, + **req_kwargs + ) + else: + result = sess.request( + method, url, params=params, data=data, **req_kwargs + ) result.raise_for_status() if stream is True: # fake a HTTP response header @@ -634,15 +645,15 @@ def query(url, if cookies is not None: sess_cookies.save() - if persist_session is True and HAS_MSGPACK: + if persist_session is True and salt.utils.msgpack.HAS_MSGPACK: # TODO: See persist_session above if 'set-cookie' in result_headers: with salt.utils.files.fopen(session_cookie_jar, 'wb') as fh_: session_cookies = result_headers.get('set-cookie', None) if session_cookies is not None: - msgpack.dump({'Cookie': session_cookies}, fh_) + salt.utils.msgpack.dump({'Cookie': session_cookies}, fh_) else: - msgpack.dump('', fh_) + salt.utils.msgpack.dump('', fh_) if status is True: ret['status'] = result_status_code diff --git a/salt/utils/immutabletypes.py b/salt/utils/immutabletypes.py index 017458402e58..b474d1bd1e97 100644 --- a/salt/utils/immutabletypes.py +++ b/salt/utils/immutabletypes.py @@ -9,6 +9,7 @@ Immutable types ''' from __future__ import absolute_import, unicode_literals +import copy # Import python libs try: @@ -37,6 +38,15 @@ def __getitem__(self, key): def __repr__(self): return '<{0} {1}>'.format(self.__class__.__name__, repr(self.__obj)) + def __deepcopy__(self, memo): + return copy.deepcopy(self.__obj) + + def copy(self): + ''' + Return an un-frozen copy of self + ''' + return copy.deepcopy(self.__obj) + class ImmutableList(Sequence): ''' @@ -64,6 +74,9 @@ def __getitem__(self, key): def __repr__(self): return '<{0} {1}>'.format(self.__class__.__name__, repr(self.__obj)) + def __deepcopy__(self, memo): + return copy.deepcopy(self.__obj) + class ImmutableSet(Set): ''' @@ -85,6 +98,9 @@ def __contains__(self, key): def __repr__(self): return '<{0} {1}>'.format(self.__class__.__name__, repr(self.__obj)) + def __deepcopy__(self, memo): + return copy.deepcopy(self.__obj) + def freeze(obj): ''' diff --git a/salt/utils/jid.py b/salt/utils/jid.py index 521cccc6ce58..7e6008df3acc 100644 --- a/salt/utils/jid.py +++ b/salt/utils/jid.py @@ -14,6 +14,13 @@ LAST_JID_DATETIME = None +def _utc_now(): + ''' + Helper method so tests do not have to patch the built-in method. + ''' + return datetime.datetime.utcnow() + + def gen_jid(opts=None): ''' Generate a jid @@ -27,9 +34,9 @@ def gen_jid(opts=None): opts = {} global LAST_JID_DATETIME # pylint: disable=global-statement + jid_dt = _utc_now() if not opts.get('unique_jid', False): - return '{0:%Y%m%d%H%M%S%f}'.format(datetime.datetime.now()) - jid_dt = datetime.datetime.now() + return '{0:%Y%m%d%H%M%S%f}'.format(jid_dt) if LAST_JID_DATETIME and LAST_JID_DATETIME >= jid_dt: jid_dt = LAST_JID_DATETIME + datetime.timedelta(microseconds=1) LAST_JID_DATETIME = jid_dt diff --git a/salt/utils/json.py b/salt/utils/json.py index e30534db09da..51628a5ee373 100644 --- a/salt/utils/json.py +++ b/salt/utils/json.py @@ -8,6 +8,7 @@ # Import Python libs import json # future lint: blacklisted-module import logging +import sys # Import Salt libs import salt.utils.data @@ -19,6 +20,10 @@ log = logging.getLogger(__name__) +# One to one mappings +JSONEncoder = json.JSONEncoder + + def __split(raw): ''' Performs a splitlines on the string. This function exists to make mocking @@ -95,7 +100,7 @@ def loads(s, **kwargs): if six.PY3 and isinstance(s, bytes): return json_module.loads(salt.utils.stringutils.to_unicode(s), **kwargs) else: - raise exc + six.reraise(*sys.exc_info()) def dump(obj, fp, **kwargs): diff --git a/salt/utils/listdiffer.py b/salt/utils/listdiffer.py index 5c1a77ea7707..96594365a5ef 100644 --- a/salt/utils/listdiffer.py +++ b/salt/utils/listdiffer.py @@ -237,7 +237,7 @@ def changed(self, selection='all'): changed.append('.'.join([self._key, key_val, change])) return changed elif selection == 'intersect': - # We want the unset values as well + # We want the unset values as well for recursive_item in self._get_recursive_difference(type='intersect'): recursive_item.ignore_unset_values = False key_val = six.text_type(recursive_item.past_dict[self._key]) \ diff --git a/salt/utils/mac_utils.py b/salt/utils/mac_utils.py index 633968cd7b73..ea63e839626d 100644 --- a/salt/utils/mac_utils.py +++ b/salt/utils/mac_utils.py @@ -314,6 +314,71 @@ def launchctl(sub_cmd, *args, **kwargs): return ret['stdout'] if return_stdout else True +def _read_plist_file(root, file_name): + ''' + :param root: The root path of the plist file + :param file_name: The name of the plist file + :return: An empty dictionary if the plist file was invalid, otherwise, a dictionary with plist data + ''' + file_path = os.path.join(root, file_name) + log.debug('read_plist: Gathering service info for {}'.format(file_path)) + + # Must be a plist file + if not file_path.lower().endswith('.plist'): + log.debug('read_plist: Not a plist file: {}'.format(file_path)) + return {} + + # ignore broken symlinks + if not os.path.exists(os.path.realpath(file_path)): + log.warning('read_plist: Ignoring broken symlink: {}'.format(file_path)) + return {} + + try: + if six.PY2: + # py2 plistlib can't read binary plists, and + # uses a different API than py3. + plist = plistlib.readPlist(file_path) + else: + with salt.utils.files.fopen(file_path, 'rb') as handle: + plist = plistlib.load(handle) + + except plistlib.InvalidFileException: + # Raised in python3 if the file is not XML. + # There's nothing we can do; move on to the next one. + log.warning('read_plist: Unable to parse "{}" as it is invalid XML: InvalidFileException.'.format(file_path)) + return {} + + except xml.parsers.expat.ExpatError: + # Raised by py2 for all errors. + # Raised by py3 if the file is XML, but with errors. + if six.PY3: + # There's an error in the XML, so move on. + log.warning('read_plist: Unable to parse "{}" as it is invalid XML: xml.parsers.expat.ExpatError.'.format(file_path)) + return {} + + # Use the system provided plutil program to attempt + # conversion from binary. + cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format(file_path) + try: + plist_xml = __salt__['cmd.run'](cmd) + plist = plistlib.readPlistFromString(plist_xml) + except xml.parsers.expat.ExpatError: + # There's still an error in the XML, so move on. + log.warning('read_plist: Unable to parse "{}" as it is invalid XML: xml.parsers.expat.ExpatError.'.format(file_path)) + return {} + + if 'Label' not in plist: + # not all launchd plists contain a Label key + log.debug('read_plist: Service does not contain a Label key. Skipping {}.'.format(file_path)) + return {} + + return { + 'file_name': file_name, + 'file_path': file_path, + 'plist': plist, + } + + def _available_services(refresh=False): ''' This is a helper function for getting the available macOS services. @@ -324,102 +389,36 @@ def _available_services(refresh=False): file present, but normally services which have an automated startup will have a plist file, so this is a minor compromise. ''' - try: - if __context__['available_services'] and not refresh: - log.debug('Found context for available services.') - __context__['using_cached_services'] = True - return __context__['available_services'] - except KeyError: - pass + if 'available_services' in __context__ and not refresh: + log.debug('Found context for available services.') + __context__['using_cached_services'] = True + return __context__['available_services'] - launchd_paths = [ + launchd_paths = { '/Library/LaunchAgents', '/Library/LaunchDaemons', '/System/Library/LaunchAgents', '/System/Library/LaunchDaemons', - ] + } - try: - for user in os.listdir('/Users/'): - agent_path = '/Users/{}/Library/LaunchAgents'.format(user) - if os.path.isdir(agent_path): - launchd_paths.append(agent_path) - except OSError: - pass + agent_path = '/Users/{}/Library/LaunchAgents' + launchd_paths.update({agent_path.format(user) for user in os.listdir('/Users/') if os.path.isdir(agent_path.format(user))}) - _available_services = dict() + result = {} for launch_dir in launchd_paths: for root, dirs, files in salt.utils.path.os_walk(launch_dir): for file_name in files: - - # Must be a plist file - if not file_name.endswith('.plist'): - continue - - # Follow symbolic links of files in _launchd_paths - file_path = os.path.join(root, file_name) - true_path = os.path.realpath(file_path) - log.trace('Gathering service info for %s', true_path) - # ignore broken symlinks - if not os.path.exists(true_path): - continue - - try: - if six.PY2: - # py2 plistlib can't read binary plists, and - # uses a different API than py3. - plist = plistlib.readPlist(true_path) - else: - with salt.utils.files.fopen(true_path, 'rb') as handle: - plist = plistlib.load(handle) - - except plistlib.InvalidFileException: - # Raised in python3 if the file is not XML. - # There's nothing we can do; move on to the next one. - msg = 'Unable to parse "%s" as it is invalid XML: InvalidFileException.' - logging.warning(msg, true_path) - continue - - except xml.parsers.expat.ExpatError: - # Raised by py2 for all errors. - # Raised by py3 if the file is XML, but with errors. - if six.PY3: - # There's an error in the XML, so move on. - msg = 'Unable to parse "%s" as it is invalid XML: xml.parsers.expat.ExpatError.' - logging.warning(msg, true_path) - continue - - # Use the system provided plutil program to attempt - # conversion from binary. - cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format( - true_path) - try: - plist_xml = __salt__['cmd.run'](cmd) - plist = plistlib.readPlistFromString(plist_xml) - except xml.parsers.expat.ExpatError: - # There's still an error in the XML, so move on. - msg = 'Unable to parse "%s" as it is invalid XML: xml.parsers.expat.ExpatError.' - logging.warning(msg, true_path) - continue - - try: - # not all launchd plists contain a Label key - _available_services[plist['Label'].lower()] = { - 'file_name': file_name, - 'file_path': true_path, - 'plist': plist} - except KeyError: - log.debug('Service %s does not contain a' - ' Label key. Skipping.', true_path) - continue + data = _read_plist_file(root, file_name) + if data: + result[data['plist']['Label'].lower()] = data # put this in __context__ as this is a time consuming function. # a fix for this issue. https://github.com/saltstack/salt/issues/48414 - __context__['available_services'] = _available_services + __context__['available_services'] = result # this is a fresh gathering of services, set cached to false __context__['using_cached_services'] = False - return __context__['available_services'] + return result def available_services(refresh=False): diff --git a/salt/utils/master.py b/salt/utils/master.py index f5a2803a34a4..e30b1b591e9e 100644 --- a/salt/utils/master.py +++ b/salt/utils/master.py @@ -29,7 +29,7 @@ from salt.exceptions import SaltException import salt.config from salt.utils.cache import CacheCli as cache_cli -from salt.utils.process import MultiprocessingProcess +from salt.utils.process import Process # Import third party libs from salt.ext import six @@ -510,7 +510,7 @@ def run(self): count = 0 -class CacheWorker(MultiprocessingProcess): +class CacheWorker(Process): ''' Worker for ConnectedCache which runs in its own process to prevent blocking of ConnectedCache @@ -528,7 +528,6 @@ def __init__(self, opts, **kwargs): # We do this so that __init__ will be invoked on Windows in the child # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): - self._is_child = True self.__init__( state['opts'], log_queue=state['log_queue'], @@ -553,7 +552,7 @@ def run(self): log.debug('ConCache CacheWorker update finished') -class ConnectedCache(MultiprocessingProcess): +class ConnectedCache(Process): ''' Provides access to all minions ids that the master has successfully authenticated. The cache is cleaned up regularly by @@ -591,7 +590,6 @@ def __init__(self, opts, **kwargs): # We do this so that __init__ will be invoked on Windows in the child # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): - self._is_child = True self.__init__( state['opts'], log_queue=state['log_queue'], diff --git a/salt/utils/minions.py b/salt/utils/minions.py index a24f29370141..84ff996c6119 100644 --- a/salt/utils/minions.py +++ b/salt/utils/minions.py @@ -777,30 +777,6 @@ def match_check(self, regex, fun): log.error('Invalid regular expression: %s', regex) return vals and all(vals) - def any_auth(self, form, auth_list, fun, arg, tgt=None, tgt_type='glob'): - ''' - Read in the form and determine which auth check routine to execute - ''' - # This function is only called from salt.auth.Authorize(), which is also - # deprecated and will be removed in Neon. - salt.utils.versions.warn_until( - 'Neon', - 'The \'any_auth\' function has been deprecated. Support for this ' - 'function will be removed in Salt {version}.' - ) - if form == 'publish': - return self.auth_check( - auth_list, - fun, - arg, - tgt, - tgt_type) - return self.spec_check( - auth_list, - fun, - arg, - form) - def auth_check_expanded(self, auth_list, funs, diff --git a/salt/utils/msgpack.py b/salt/utils/msgpack.py new file mode 100644 index 000000000000..1d02aa96ba8b --- /dev/null +++ b/salt/utils/msgpack.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +''' +Functions to work with MessagePack +''' + +# Import Python libs +from __future__ import absolute_import +import logging + +log = logging.getLogger(__name__) + +# Import 3rd party libs +HAS_MSGPACK = False +try: + import msgpack + + # There is a serialization issue on ARM and potentially other platforms for some msgpack bindings, check for it + if msgpack.version >= (0, 4, 0) and msgpack.loads(msgpack.dumps([1, 2, 3], use_bin_type=False), + use_list=True) is None: + raise ImportError + elif msgpack.loads(msgpack.dumps([1, 2, 3]), use_list=True) is None: + raise ImportError + HAS_MSGPACK = True +except ImportError: + try: + import msgpack_pure as msgpack # pylint: disable=import-error + + HAS_MSGPACK = True + except ImportError: + pass + # Don't exit if msgpack is not available, this is to make local mode work without msgpack + # sys.exit(salt.defaults.exitcodes.EX_GENERIC) + +if HAS_MSGPACK and hasattr(msgpack, 'exceptions'): + exceptions = msgpack.exceptions +else: + class PackValueError(Exception): + ''' + older versions of msgpack do not have PackValueError + ''' + + class _exceptions(object): + ''' + older versions of msgpack do not have an exceptions module + ''' + PackValueError = PackValueError() + + exceptions = _exceptions() + +# One-to-one mappings +Packer = msgpack.Packer +ExtType = msgpack.ExtType +version = (0, 0, 0) if not HAS_MSGPACK else msgpack.version + + +def _sanitize_msgpack_kwargs(kwargs): + ''' + Clean up msgpack keyword arguments based on the version + https://github.com/msgpack/msgpack-python/blob/master/ChangeLog.rst + ''' + assert isinstance(kwargs, dict) + if version < (0, 6, 0) and kwargs.pop('strict_map_key', None) is not None: + log.info('removing unsupported `strict_map_key` argument from msgpack call') + if version < (0, 5, 5) and kwargs.pop('raw', None) is not None: + log.info('removing unsupported `raw` argument from msgpack call') + if version < (0, 4, 0) and kwargs.pop('use_bin_type', None) is not None: + log.info('removing unsupported `use_bin_type` argument from msgpack call') + + return kwargs + + +class Unpacker(msgpack.Unpacker): + ''' + Wraps the msgpack.Unpacker and removes non-relevant arguments + ''' + def __init__(self, *args, **kwargs): + msgpack.Unpacker.__init__(self, *args, **_sanitize_msgpack_kwargs(kwargs)) + + +def pack(o, stream, **kwargs): + ''' + .. versionadded:: 2018.3.4 + + Wraps msgpack.pack and ensures that the passed object is unwrapped if it is + a proxy. + + By default, this function uses the msgpack module and falls back to + msgpack_pure, if the msgpack is not available. + ''' + # Writes to a stream, there is no return + msgpack.pack(o, stream, **_sanitize_msgpack_kwargs(kwargs)) + + +def packb(o, **kwargs): + ''' + .. versionadded:: 2018.3.4 + + Wraps msgpack.packb and ensures that the passed object is unwrapped if it + is a proxy. + + By default, this function uses the msgpack module and falls back to + msgpack_pure, if the msgpack is not available. + ''' + return msgpack.packb(o, **_sanitize_msgpack_kwargs(kwargs)) + + +def unpack(stream, **kwargs): + ''' + .. versionadded:: 2018.3.4 + + Wraps msgpack.unpack. + + By default, this function uses the msgpack module and falls back to + msgpack_pure, if the msgpack is not available. + ''' + return msgpack.unpack(stream, **_sanitize_msgpack_kwargs(kwargs)) + + +def unpackb(packed, **kwargs): + ''' + .. versionadded:: 2018.3.4 + + Wraps msgpack.unpack. + + By default, this function uses the msgpack module and falls back to + msgpack_pure. + ''' + return msgpack.unpackb(packed, **_sanitize_msgpack_kwargs(kwargs)) + + +# alias for compatibility to simplejson/marshal/pickle. +load = unpack +loads = unpackb + +dump = pack +dumps = packb diff --git a/salt/utils/network.py b/salt/utils/network.py index d6fc6a98c6ee..df299773e8ff 100644 --- a/salt/utils/network.py +++ b/salt/utils/network.py @@ -47,8 +47,8 @@ try: import ctypes import ctypes.util - libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("c")) - res_init = libc.__res_init + LIBC = ctypes.cdll.LoadLibrary(ctypes.util.find_library("c")) + RES_INIT = LIBC.__res_init except (ImportError, OSError, AttributeError, TypeError): pass @@ -249,29 +249,29 @@ def is_reachable_host(entity_name): return ret -def is_ip(ip): +def is_ip(ip_addr): ''' Returns a bool telling if the passed IP is a valid IPv4 or IPv6 address. ''' - return is_ipv4(ip) or is_ipv6(ip) + return is_ipv4(ip_addr) or is_ipv6(ip_addr) -def is_ipv4(ip): +def is_ipv4(ip_addr): ''' Returns a bool telling if the value passed to it was a valid IPv4 address ''' try: - return ipaddress.ip_address(ip).version == 4 + return ipaddress.ip_address(ip_addr).version == 4 except ValueError: return False -def is_ipv6(ip): +def is_ipv6(ip_addr): ''' Returns a bool telling if the value passed to it was a valid IPv6 address ''' try: - return ipaddress.ip_address(ip).version == 6 + return ipaddress.ip_address(ip_addr).version == 6 except ValueError: return False @@ -304,11 +304,11 @@ def is_ipv6_subnet(cidr): @jinja_filter('is_ip') -def is_ip_filter(ip, options=None): +def is_ip_filter(ip_addr, options=None): ''' Returns a bool telling if the passed IP is a valid IPv4 or IPv6 address. ''' - return is_ipv4_filter(ip, options=options) or is_ipv6_filter(ip, options=options) + return is_ipv4_filter(ip_addr, options=options) or is_ipv6_filter(ip_addr, options=options) def _ip_options_global(ip_obj, version): @@ -381,7 +381,7 @@ def _ip_options(ip_obj, version, options=None): return six.text_type(ip_obj) -def _is_ipv(ip, version, options=None): +def _is_ipv(ip_addr, version, options=None): if not version: version = 4 @@ -390,11 +390,11 @@ def _is_ipv(ip, version, options=None): return None try: - ip_obj = ipaddress.ip_address(ip) + ip_obj = ipaddress.ip_address(ip_addr) except ValueError: # maybe it is an IP network try: - ip_obj = ipaddress.ip_interface(ip) + ip_obj = ipaddress.ip_interface(ip_addr) except ValueError: # nope, still not :( return None @@ -407,7 +407,7 @@ def _is_ipv(ip, version, options=None): @jinja_filter('is_ipv4') -def is_ipv4_filter(ip, options=None): +def is_ipv4_filter(ip_addr, options=None): ''' Returns a bool telling if the value passed to it was a valid IPv4 address. @@ -420,12 +420,12 @@ def is_ipv4_filter(ip, options=None): options CSV of options regarding the nature of the IP address. E.g.: loopback, multicast, private etc. ''' - _is_ipv4 = _is_ipv(ip, 4, options=options) + _is_ipv4 = _is_ipv(ip_addr, 4, options=options) return isinstance(_is_ipv4, six.string_types) @jinja_filter('is_ipv6') -def is_ipv6_filter(ip, options=None): +def is_ipv6_filter(ip_addr, options=None): ''' Returns a bool telling if the value passed to it was a valid IPv6 address. @@ -438,7 +438,7 @@ def is_ipv6_filter(ip, options=None): options CSV of options regarding the nature of the IP address. E.g.: loopback, multicast, private etc. ''' - _is_ipv6 = _is_ipv(ip, 6, options=options) + _is_ipv6 = _is_ipv(ip_addr, 6, options=options) return isinstance(_is_ipv6, six.string_types) @@ -569,11 +569,11 @@ def network_size(value, options=None, version=None): ] -def natural_ipv4_netmask(ip, fmt='prefixlen'): +def natural_ipv4_netmask(ip_addr, fmt='prefixlen'): ''' Returns the "natural" mask of an IPv4 address ''' - bits = _ipv4_to_bits(ip) + bits = _ipv4_to_bits(ip_addr) if bits.startswith('11'): mask = '24' @@ -588,14 +588,14 @@ def natural_ipv4_netmask(ip, fmt='prefixlen'): return '/' + mask -def rpad_ipv4_network(ip): +def rpad_ipv4_network(ip_addr): ''' Returns an IP network address padded with zeros. Ex: '192.168.3' -> '192.168.3.0' '10.209' -> '10.209.0.0' ''' - return '.'.join(itertools.islice(itertools.chain(ip.split('.'), '0000'), 0, + return '.'.join(itertools.islice(itertools.chain(ip_addr.split('.'), '0000'), 0, 4)) @@ -959,6 +959,7 @@ def _interfaces_ipconfig(out): ''' ifaces = dict() iface = None + addr = None adapter_iface_regex = re.compile(r'adapter (\S.+):$') for line in out.splitlines(): @@ -1303,16 +1304,16 @@ def hex2ip(hex_ip, invert=False): returned. If 'invert=True' assume that ip from /proc/net/ ''' if len(hex_ip) == 32: # ipv6 - ip = [] + ip_addr = [] for i in range(0, 32, 8): ip_part = hex_ip[i:i + 8] ip_part = [ip_part[x:x + 2] for x in range(0, 8, 2)] if invert: - ip.append("{0[3]}{0[2]}:{0[1]}{0[0]}".format(ip_part)) + ip_addr.append("{0[3]}{0[2]}:{0[1]}{0[0]}".format(ip_part)) else: - ip.append("{0[0]}{0[1]}:{0[2]}{0[3]}".format(ip_part)) + ip_addr.append("{0[0]}{0[1]}:{0[2]}{0[3]}".format(ip_part)) try: - address = ipaddress.IPv6Address(":".join(ip)) + address = ipaddress.IPv6Address(":".join(ip_addr)) if address.ipv4_mapped: return str(address.ipv4_mapped) else: @@ -1363,17 +1364,18 @@ def active_tcp(): ''' ret = {} for statf in ['/proc/net/tcp', '/proc/net/tcp6']: - if os.path.isfile(statf): - with salt.utils.files.fopen(statf, 'rb') as fp_: - for line in fp_: - line = salt.utils.stringutils.to_unicode(line) - if line.strip().startswith('sl'): - continue - iret = _parse_tcp_line(line) - sl = next(iter(iret)) - if iret[sl]['state'] == 1: # 1 is ESTABLISHED - del iret[sl]['state'] - ret[len(ret)] = iret[sl] + if not os.path.isfile(statf): + continue + with salt.utils.files.fopen(statf, 'rb') as fp_: + for line in fp_: + line = salt.utils.stringutils.to_unicode(line) + if line.strip().startswith('sl'): + continue + iret = _parse_tcp_line(line) + slot = next(iter(iret)) + if iret[slot]['state'] == 1: # 1 is ESTABLISHED + del iret[slot]['state'] + ret[len(ret)] = iret[slot] return ret @@ -1406,17 +1408,18 @@ def _remotes_on(port, which_end): ret = set() proc_available = False for statf in ['/proc/net/tcp', '/proc/net/tcp6']: - if os.path.isfile(statf): - proc_available = True - with salt.utils.files.fopen(statf, 'r') as fp_: - for line in fp_: - line = salt.utils.stringutils.to_unicode(line) - if line.strip().startswith('sl'): - continue - iret = _parse_tcp_line(line) - sl = next(iter(iret)) - if iret[sl][which_end] == port and iret[sl]['state'] == 1: # 1 is ESTABLISHED - ret.add(iret[sl]['remote_addr']) + if not os.path.isfile(statf): + continue + proc_available = True + with salt.utils.files.fopen(statf, 'r') as fp_: + for line in fp_: + line = salt.utils.stringutils.to_unicode(line) + if line.strip().startswith('sl'): + continue + iret = _parse_tcp_line(line) + slot = next(iter(iret)) + if iret[slot][which_end] == port and iret[slot]['state'] == 1: # 1 is ESTABLISHED + ret.add(iret[slot]['remote_addr']) if not proc_available: # Fallback to use OS specific tools if salt.utils.platform.is_sunos(): @@ -1443,15 +1446,15 @@ def _parse_tcp_line(line): ''' ret = {} comps = line.strip().split() - sl = comps[0].rstrip(':') - ret[sl] = {} + slot = comps[0].rstrip(':') + ret[slot] = {} l_addr, l_port = comps[1].split(':') r_addr, r_port = comps[2].split(':') - ret[sl]['local_addr'] = hex2ip(l_addr, True) - ret[sl]['local_port'] = int(l_port, 16) - ret[sl]['remote_addr'] = hex2ip(r_addr, True) - ret[sl]['remote_port'] = int(r_port, 16) - ret[sl]['state'] = int(comps[3], 16) + ret[slot]['local_addr'] = hex2ip(l_addr, True) + ret[slot]['local_port'] = int(l_port, 16) + ret[slot]['remote_addr'] = hex2ip(r_addr, True) + ret[slot]['remote_port'] = int(r_port, 16) + ret[slot]['state'] = int(comps[3], 16) return ret @@ -1470,8 +1473,9 @@ def _netlink_tool_remote_on(port, which_end): ''' remotes = set() valid = False + tcp_end = 'dst' if which_end == 'remote_port' else 'src' try: - data = subprocess.check_output(['ss', '-ant']) # pylint: disable=minimum-python-version + data = subprocess.check_output(['ss', '-ant', tcp_end, ':{0}'.format(port)]) # pylint: disable=minimum-python-version except subprocess.CalledProcessError: log.error('Failed ss') raise @@ -1486,13 +1490,8 @@ def _netlink_tool_remote_on(port, which_end): elif 'ESTAB' not in line: continue chunks = line.split() - local_host, local_port = chunks[3].rsplit(':', 1) remote_host, remote_port = chunks[4].rsplit(':', 1) - if which_end == 'remote_port' and int(remote_port) != port: - continue - if which_end == 'local_port' and int(local_port) != port: - continue remotes.add(remote_host) if valid is False: @@ -1890,9 +1889,9 @@ def refresh_dns(): issue #21397: force glibc to re-read resolv.conf ''' try: - res_init() + RES_INIT() except NameError: - # Exception raised loading the library, thus res_init is not defined + # Exception raised loading the library, thus RES_INIT is not defined pass diff --git a/salt/utils/parsers.py b/salt/utils/parsers.py index 4df2c3f63688..e5dc2bd28369 100644 --- a/salt/utils/parsers.py +++ b/salt/utils/parsers.py @@ -15,6 +15,7 @@ from __future__ import absolute_import, print_function, unicode_literals import os import sys +import types import signal import getpass import logging @@ -56,6 +57,17 @@ def _sorted(mixins_or_funcs): ) +class MixinFuncsContainer(list): + + def append(self, func): + if isinstance(func, types.MethodType): + # We only care about unbound methods + func = func.__func__ + if func not in self: + # And no duplicates please + list.append(self, func) + + class MixInMeta(type): # This attribute here won't actually do anything. But, if you need to # specify an order or a dependency within the mix-ins, please define the @@ -79,13 +91,13 @@ def __new__(mcs, name, bases, attrs): bases, attrs) if not hasattr(instance, '_mixin_setup_funcs'): - instance._mixin_setup_funcs = [] + instance._mixin_setup_funcs = MixinFuncsContainer() if not hasattr(instance, '_mixin_process_funcs'): - instance._mixin_process_funcs = [] + instance._mixin_process_funcs = MixinFuncsContainer() if not hasattr(instance, '_mixin_after_parsed_funcs'): - instance._mixin_after_parsed_funcs = [] + instance._mixin_after_parsed_funcs = MixinFuncsContainer() if not hasattr(instance, '_mixin_before_exit_funcs'): - instance._mixin_before_exit_funcs = [] + instance._mixin_before_exit_funcs = MixinFuncsContainer() for base in _sorted(bases + (instance,)): func = getattr(base, '_mixin_setup', None) @@ -304,7 +316,7 @@ def _mixin_setup(self): # the config options and if needed override them self._mixin_after_parsed_funcs.append(self.__merge_config_with_cli) - def __merge_config_with_cli(self, *args): # pylint: disable=unused-argument + def __merge_config_with_cli(self): # Merge parser options for option in self.option_list: if option.dest is None: @@ -687,7 +699,7 @@ def process_log_level_logfile(self): # Remove it from config so it inherits from log_level_logfile self.config.pop(self._logfile_loglevel_config_setting_name_) - def __setup_logfile_logger_config(self, *args): # pylint: disable=unused-argument + def __setup_logfile_logger_config(self): if self._logfile_loglevel_config_setting_name_ in self.config and not \ self.config.get(self._logfile_loglevel_config_setting_name_): # Remove it from config so it inherits from log_level @@ -852,7 +864,7 @@ def setup_logfile_logger(self): for name, level in six.iteritems(self.config.get('log_granular_levels', {})): log.set_logger_level(name, level) - def __setup_extended_logging(self, *args): # pylint: disable=unused-argument + def __setup_extended_logging(self): if salt.utils.platform.is_windows() and self._setup_mp_logging_listener_: # On Windows when using a logging listener, all extended logging # will go through the logging listener. @@ -862,14 +874,14 @@ def __setup_extended_logging(self, *args): # pylint: disable=unused-argument def _get_mp_logging_listener_queue(self): return log.get_multiprocessing_logging_queue() - def _setup_mp_logging_listener(self, *args): # pylint: disable=unused-argument + def _setup_mp_logging_listener(self): if self._setup_mp_logging_listener_: log.setup_multiprocessing_logging_listener( self.config, self._get_mp_logging_listener_queue() ) - def _setup_mp_logging_client(self, *args): # pylint: disable=unused-argument + def _setup_mp_logging_client(self): if self._setup_mp_logging_listener_: # Set multiprocessing logging level even in non-Windows # environments. In non-Windows environments, this setting will @@ -895,7 +907,7 @@ def _setup_mp_logging_client(self, *args): # pylint: disable=unused-argument log.shutdown_console_logging() log.shutdown_logfile_logging() - def __setup_console_logger_config(self, *args): # pylint: disable=unused-argument + def __setup_console_logger_config(self): # Since we're not going to be a daemon, setup the console logger logfmt = self.config.get( 'log_fmt_console', @@ -921,7 +933,7 @@ def __setup_console_logger_config(self, *args): # pylint: disable=unused-argume self.config['log_fmt_console'] = logfmt self.config['log_datefmt_console'] = datefmt - def __setup_console_logger(self, *args): # pylint: disable=unused-argument + def __setup_console_logger(self): # If daemon is set force console logger to quiet if getattr(self.options, 'daemon', False) is True: return @@ -2580,7 +2592,7 @@ def _mixin_after_parsed(self): # info or error. self.config['loglevel'] = 'info' - def __create_keys_dir(self, *args): # pylint: disable=unused-argument + def __create_keys_dir(self): if not os.path.isdir(self.config['gen_keys_dir']): os.makedirs(self.config['gen_keys_dir']) diff --git a/salt/utils/path.py b/salt/utils/path.py index b1d601e464f6..6cbbf3a08eee 100644 --- a/salt/utils/path.py +++ b/salt/utils/path.py @@ -6,7 +6,10 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import collections +try: + from collections.abc import Iterable +except ImportError: + from collections import Iterable import errno import logging import os @@ -20,7 +23,6 @@ import salt.utils.platform import salt.utils.stringutils from salt.exceptions import CommandNotFoundError -from salt.utils.decorators import memoize as real_memoize from salt.utils.decorators.jinja import jinja_filter # Import 3rd-party libs @@ -191,70 +193,110 @@ def which(exe=None): ''' Python clone of /usr/bin/which ''' - def _is_executable_file_or_link(exe): - # check for os.X_OK doesn't suffice because directory may executable - return (os.access(exe, os.X_OK) and - (os.path.isfile(exe) or os.path.islink(exe))) - - if exe: - if _is_executable_file_or_link(exe): - # executable in cwd or fullpath - return exe - - ext_list = salt.utils.stringutils.to_str( - os.environ.get('PATHEXT', str('.EXE')) - ).split(str(';')) - - @real_memoize - def _exe_has_ext(): - ''' - Do a case insensitive test if exe has a file extension match in - PATHEXT - ''' - for ext in ext_list: - try: - pattern = r'.*\.{0}$'.format( - salt.utils.stringutils.to_unicode(ext).lstrip('.') - ) - re.match( - pattern, - salt.utils.stringutils.to_unicode(exe), - re.I).groups() - return True - except AttributeError: - continue - return False - - # Enhance POSIX path for the reliability at some environments, when $PATH is changing - # This also keeps order, where 'first came, first win' for cases to find optional alternatives - system_path = salt.utils.stringutils.to_unicode(os.environ.get('PATH', '')) - search_path = system_path.split(os.pathsep) - if not salt.utils.platform.is_windows(): - search_path.extend([ - x for x in ('/bin', '/sbin', '/usr/bin', - '/usr/sbin', '/usr/local/bin') - if x not in search_path - ]) - - for path in search_path: - full_path = join(path, exe) - if _is_executable_file_or_link(full_path): - return full_path - elif salt.utils.platform.is_windows() and not _exe_has_ext(): - # On Windows, check for any extensions in PATHEXT. - # Allows both 'cmd' and 'cmd.exe' to be matched. - for ext in ext_list: - # Windows filesystem is case insensitive so we - # safely rely on that behavior - if _is_executable_file_or_link(full_path + ext): - return full_path + ext - log.trace( - '\'%s\' could not be found in the following search path: \'%s\'', - exe, search_path - ) - else: + + if not exe: log.error('No executable was passed to be searched by salt.utils.path.which()') + return None + + ## define some utilities (we use closures here because our predecessor used them) + def is_executable_common(path): + ''' + This returns truth if posixy semantics (which python simulates on + windows) states that this is executable. + ''' + return os.path.isfile(path) and os.access(path, os.X_OK) + + def resolve(path): + ''' + This will take a path and recursively follow the link until we get to a + real file. + ''' + while os.path.islink(path): + res = os.readlink(path) + + # if the link points to a relative target, then convert it to an + # absolute path relative to the original path + if not os.path.isabs(res): + directory, _ = os.path.split(path) + res = join(directory, res) + path = res + return path + + # windows-only + def has_executable_ext(path, ext_membership): + ''' + Extract the extension from the specified path, lowercase it so we + can be insensitive, and then check it against the available exts. + ''' + p, ext = os.path.splitext(path) + return ext.lower() in ext_membership + + ## prepare related variables from the environment + res = salt.utils.stringutils.to_unicode(os.environ.get('PATH', '')) + system_path = res.split(os.pathsep) + + # add some reasonable defaults in case someone's PATH is busted + if not salt.utils.platform.is_windows(): + res = set(system_path) + extended_path = ['/sbin', '/bin', '/usr/sbin', '/usr/bin', '/usr/local/sbin', '/usr/local/bin'] + system_path.extend([p for p in extended_path if p not in res]) + + ## now to define the semantics of what's considered executable on a given platform + if salt.utils.platform.is_windows(): + # executable semantics on windows requires us to search PATHEXT + res = salt.utils.stringutils.to_str(os.environ.get('PATHEXT', str('.EXE'))) + + # generate two variables, one of them for O(n) searches (but ordered) + # and another for O(1) searches. the previous guy was trying to use + # memoization with a function that has no arguments, this provides + # the exact same benefit + pathext = res.split(os.pathsep) + res = {ext.lower() for ext in pathext} + + # check if our caller already specified a valid extension as then we don't need to match it + _, ext = os.path.splitext(exe) + if ext.lower() in res: + pathext = [''] + + is_executable = is_executable_common + + # The specified extension isn't valid, so we just assume it's part of the + # filename and proceed to walk the pathext list + else: + is_executable = lambda path, membership=res: is_executable_common(path) and has_executable_ext(path, membership) + else: + # in posix, there's no such thing as file extensions..only zuul + pathext = [''] + + # executable semantics are pretty simple on reasonable platforms... + is_executable = is_executable_common + + ## search for the executable + + # check to see if the full path was specified as then we don't need + # to actually walk the system_path for any reason + if is_executable(exe): + return exe + + # now to search through our system_path + for path in system_path: + p = join(path, exe) + + # iterate through all extensions to see which one is executable + for ext in pathext: + pext = p + ext + rp = resolve(pext) + if is_executable(rp): + return p + ext + continue + continue + + ## if something was executable, we should've found it already... + log.trace( + '\'%s\' could not be found in the following search path: \'%s\'', + exe, system_path + ) return None @@ -262,7 +304,7 @@ def which_bin(exes): ''' Scan over some possible executables and return the first one that is found ''' - if not isinstance(exes, collections.Iterable): + if not isinstance(exes, Iterable): return None for exe in exes: path = which(exe) diff --git a/salt/utils/pkg/rpm.py b/salt/utils/pkg/rpm.py index 94e231da4bc4..bc5eb30eda0d 100644 --- a/salt/utils/pkg/rpm.py +++ b/salt/utils/pkg/rpm.py @@ -9,7 +9,9 @@ import datetime import logging import subprocess +import platform import salt.utils.stringutils +import salt.utils.path # Import 3rd-party libs from salt.ext import six @@ -19,7 +21,7 @@ # These arches compiled from the rpmUtils.arch python module source ARCHES_64 = ('x86_64', 'athlon', 'amd64', 'ia32e', 'ia64', 'geode') ARCHES_32 = ('i386', 'i486', 'i586', 'i686') -ARCHES_PPC = ('ppc', 'ppc64', 'ppc64iseries', 'ppc64pseries') +ARCHES_PPC = ('ppc', 'ppc64', 'ppc64le', 'ppc64iseries', 'ppc64pseries') ARCHES_S390 = ('s390', 's390x') ARCHES_SPARC = ( 'sparc', 'sparcv8', 'sparcv9', 'sparcv9v', 'sparc64', 'sparc64v' @@ -42,12 +44,16 @@ def get_osarch(): ''' Get the os architecture using rpm --eval ''' - ret = subprocess.Popen( - 'rpm --eval "%{_host_cpu}"', - shell=True, - close_fds=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE).communicate()[0] + if salt.utils.path.which('rpm'): + ret = subprocess.Popen( + 'rpm --eval "%{_host_cpu}"', + shell=True, + close_fds=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate()[0] + else: + ret = ''.join([x for x in platform.uname()[-2:] if x][-1:]) + return salt.utils.stringutils.to_str(ret).strip() or 'unknown' diff --git a/salt/utils/process.py b/salt/utils/process.py index 4fec4ed698cd..bc4e8c510be6 100644 --- a/salt/utils/process.py +++ b/salt/utils/process.py @@ -14,6 +14,7 @@ import types import signal import logging +import functools import threading import contextlib import subprocess @@ -21,7 +22,6 @@ import multiprocessing.util import socket - # Import salt libs import salt.defaults.exitcodes import salt.utils.files @@ -29,6 +29,7 @@ import salt.utils.platform import salt.log.setup import salt.defaults.exitcodes +import salt.utils.versions from salt.log.mixins import NewStyleClassMixIn # Import 3rd-party libs @@ -404,9 +405,9 @@ def add_process(self, tgt, args=None, kwargs=None, name=None): if salt.utils.platform.is_windows(): # Need to ensure that 'log_queue' and 'log_queue_level' is # correctly transferred to processes that inherit from - # 'MultiprocessingProcess'. - if type(MultiprocessingProcess) is type(tgt) and ( - issubclass(tgt, MultiprocessingProcess)): + # 'Process'. + if type(Process) is type(tgt) and ( + issubclass(tgt, Process)): need_log_queue = True else: need_log_queue = False @@ -446,7 +447,7 @@ def add_process(self, tgt, args=None, kwargs=None, name=None): else: process = multiprocessing.Process(target=tgt, args=args, kwargs=kwargs, name=name) - if isinstance(process, SignalHandlingMultiprocessingProcess): + if isinstance(process, SignalHandlingProcess): with default_signals(signal.SIGINT, signal.SIGTERM): process.start() else: @@ -683,12 +684,12 @@ def kill_children(self, *args, **kwargs): ) -class MultiprocessingProcess(multiprocessing.Process, NewStyleClassMixIn): +class Process(multiprocessing.Process, NewStyleClassMixIn): def __init__(self, *args, **kwargs): log_queue = kwargs.pop('log_queue', None) log_queue_level = kwargs.pop('log_queue_level', None) - super(MultiprocessingProcess, self).__init__(*args, **kwargs) + super(Process, self).__init__(*args, **kwargs) if salt.utils.platform.is_windows(): # On Windows, subclasses should call super if they define # __setstate__ and/or __getstate__ @@ -709,12 +710,21 @@ def __init__(self, *args, **kwargs): salt.log.setup.set_multiprocessing_logging_level(self.log_queue_level) self._after_fork_methods = [ - (MultiprocessingProcess._setup_process_logging, [self], {}), + (Process._setup_process_logging, [self], {}), ] self._finalize_methods = [ (salt.log.setup.shutdown_multiprocessing_logging, [], {}) ] + # Because we need to enforce our after fork and finalize routines, + # we must wrap this class run method to allow for these extra steps + # to be executed pre and post calling the actual run method, + # having subclasses call super would just not work. + # + # We use setattr here to fool pylint not to complain that we're + # overriding run from the subclass here + setattr(self, 'run', self.__decorate_run(self.run)) + # __setstate__ and __getstate__ are only used on Windows. def __setstate__(self, state): args = state['args'] @@ -740,33 +750,56 @@ def __getstate__(self): def _setup_process_logging(self): salt.log.setup.setup_multiprocessing_logging(self.log_queue) - def run(self): - for method, args, kwargs in self._after_fork_methods: - method(*args, **kwargs) - try: - return super(MultiprocessingProcess, self).run() - except SystemExit: - # These are handled by multiprocessing.Process._bootstrap() - raise - except Exception as exc: - log.error( - 'An un-handled exception from the multiprocessing process ' - '\'%s\' was caught:\n', self.name, exc_info=True) - # Re-raise the exception. multiprocessing.Process will write it to - # sys.stderr and set the proper exitcode and we have already logged - # it above. - raise - finally: - for method, args, kwargs in self._finalize_methods: + def __decorate_run(self, run_func): + + @functools.wraps(run_func) + def wrapped_run_func(): + for method, args, kwargs in self._after_fork_methods: method(*args, **kwargs) + try: + return run_func() + except SystemExit: + # These are handled by multiprocessing.Process._bootstrap() + six.reraise(*sys.exc_info()) + except Exception as exc: # pylint: disable=broad-except + log.error( + 'An un-handled exception from the multiprocessing process ' + '\'%s\' was caught:\n', self.name, exc_info=True) + # Re-raise the exception. multiprocessing.Process will write it to + # sys.stderr and set the proper exitcode and we have already logged + # it above. + six.reraise(*sys.exc_info()) + finally: + for method, args, kwargs in self._finalize_methods: + method(*args, **kwargs) + + return wrapped_run_func + + +class MultiprocessingProcess(Process): + ''' + This class exists for backwards compatibility and to properly deprecate it. + ''' + def __init__(self, *args, **kwargs): + salt.utils.versions.warn_until_date( + '20220101', + 'Please stop using \'{name}.MultiprocessingProcess\' and instead use ' + '\'{name}.Process\'. \'{name}.MultiprocessingProcess\' will go away ' + 'after {{date}}.'.format( + name=__name__ + ), + stacklevel=3 + ) + super(MultiprocessingProcess, self).__init__(*args, **kwargs) -class SignalHandlingMultiprocessingProcess(MultiprocessingProcess): + +class SignalHandlingProcess(Process): def __init__(self, *args, **kwargs): - super(SignalHandlingMultiprocessingProcess, self).__init__(*args, **kwargs) + super(SignalHandlingProcess, self).__init__(*args, **kwargs) self._signal_handled = multiprocessing.Event() self._after_fork_methods.append( - (SignalHandlingMultiprocessingProcess._setup_signals, [self], {}) + (SignalHandlingProcess._setup_signals, [self], {}) ) def signal_handled(self): @@ -811,7 +844,25 @@ def _handle_signals(self, signum, sigframe): def start(self): with default_signals(signal.SIGINT, signal.SIGTERM): - super(SignalHandlingMultiprocessingProcess, self).start() + super(SignalHandlingProcess, self).start() + + +class SignalHandlingMultiprocessingProcess(SignalHandlingProcess): + ''' + This class exists for backwards compatibility and to properly deprecate it. + ''' + + def __init__(self, *args, **kwargs): + salt.utils.versions.warn_until_date( + '20220101', + 'Please stop using \'{name}.SignalHandlingMultiprocessingProcess\' and instead use ' + '\'{name}.SignalHandlingProcess\'. \'{name}.SignalHandlingMultiprocessingProcess\' ' + 'will go away after {{date}}.'.format( + name=__name__ + ), + stacklevel=3 + ) + super(SignalHandlingMultiprocessingProcess, self).__init__(*args, **kwargs) @contextlib.contextmanager diff --git a/salt/utils/reactor.py b/salt/utils/reactor.py index 6b6e95cbfb38..c58f41681eee 100644 --- a/salt/utils/reactor.py +++ b/salt/utils/reactor.py @@ -39,7 +39,7 @@ ]) -class Reactor(salt.utils.process.SignalHandlingMultiprocessingProcess, salt.state.Compiler): +class Reactor(salt.utils.process.SignalHandlingProcess, salt.state.Compiler): ''' Read in the reactor configuration variable and compare it to events processed on the master. @@ -63,7 +63,6 @@ def __init__(self, opts, **kwargs): # These methods are only used when pickling so will not be used on # non-Windows platforms. def __setstate__(self, state): - self._is_child = True Reactor.__init__( self, state['opts'], log_queue=state['log_queue'], @@ -234,43 +233,43 @@ def run(self): salt.utils.process.appendproctitle(self.__class__.__name__) # instantiate some classes inside our new process - self.event = salt.utils.event.get_event( + with salt.utils.event.get_event( self.opts['__role'], self.opts['sock_dir'], self.opts['transport'], opts=self.opts, - listen=True) - self.wrap = ReactWrap(self.opts) + listen=True) as event: + self.wrap = ReactWrap(self.opts) - for data in self.event.iter_events(full=True): - # skip all events fired by ourselves - if data['data'].get('user') == self.wrap.event_user: - continue - if data['tag'].endswith('salt/reactors/manage/add'): - _data = data['data'] - res = self.add_reactor(_data['event'], _data['reactors']) - self.event.fire_event({'reactors': self.list_all(), - 'result': res}, - 'salt/reactors/manage/add-complete') - elif data['tag'].endswith('salt/reactors/manage/delete'): - _data = data['data'] - res = self.delete_reactor(_data['event']) - self.event.fire_event({'reactors': self.list_all(), - 'result': res}, - 'salt/reactors/manage/delete-complete') - elif data['tag'].endswith('salt/reactors/manage/list'): - self.event.fire_event({'reactors': self.list_all()}, - 'salt/reactors/manage/list-results') - else: - reactors = self.list_reactors(data['tag']) - if not reactors: + for data in event.iter_events(full=True): + # skip all events fired by ourselves + if data['data'].get('user') == self.wrap.event_user: continue - chunks = self.reactions(data['tag'], data['data'], reactors) - if chunks: - try: - self.call_reactions(chunks) - except SystemExit: - log.warning('Exit ignored by reactor') + if data['tag'].endswith('salt/reactors/manage/add'): + _data = data['data'] + res = self.add_reactor(_data['event'], _data['reactors']) + event.fire_event({'reactors': self.list_all(), + 'result': res}, + 'salt/reactors/manage/add-complete') + elif data['tag'].endswith('salt/reactors/manage/delete'): + _data = data['data'] + res = self.delete_reactor(_data['event']) + event.fire_event({'reactors': self.list_all(), + 'result': res}, + 'salt/reactors/manage/delete-complete') + elif data['tag'].endswith('salt/reactors/manage/list'): + event.fire_event({'reactors': self.list_all()}, + 'salt/reactors/manage/list-results') + else: + reactors = self.list_reactors(data['tag']) + if not reactors: + continue + chunks = self.reactions(data['tag'], data['data'], reactors) + if chunks: + try: + self.call_reactions(chunks) + except SystemExit: + log.warning('Exit ignored by reactor') class ReactWrap(object): diff --git a/salt/utils/rsax931.py b/salt/utils/rsax931.py index aa1d33024ee0..1ee72f49552b 100644 --- a/salt/utils/rsax931.py +++ b/salt/utils/rsax931.py @@ -115,9 +115,11 @@ def __init__(self, keydata): if not libcrypto.PEM_read_bio_RSAPrivateKey(self._bio, pointer(self._rsa), None, None): raise ValueError('invalid RSA private key') + # pylint: disable=W1701 def __del__(self): libcrypto.BIO_free(self._bio) libcrypto.RSA_free(self._rsa) + # pylint: enable=W1701 def sign(self, msg): ''' @@ -153,9 +155,11 @@ def __init__(self, pubdata): if not libcrypto.PEM_read_bio_RSA_PUBKEY(self._bio, pointer(self._rsa), None, None): raise ValueError('invalid RSA public key') + # pylint: disable=W1701 def __del__(self): libcrypto.BIO_free(self._bio) libcrypto.RSA_free(self._rsa) + # pylint: enable=W1701 def verify(self, signed): ''' diff --git a/salt/utils/schedule.py b/salt/utils/schedule.py index 3ec12f576d16..cbfed8c7a1cf 100644 --- a/salt/utils/schedule.py +++ b/salt/utils/schedule.py @@ -295,10 +295,10 @@ def delete_job(self, name, persist=True): log.warning("Cannot delete job %s, it's in the pillar!", name) # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_delete_complete') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + evt.fire_event({'complete': True, + 'schedule': self._get_schedule()}, + tag='/salt/minion/minion_schedule_delete_complete') # remove from self.intervals if name in self.intervals: @@ -330,10 +330,10 @@ def delete_job_prefix(self, name, persist=True): log.warning("Cannot delete job %s, it's in the pillar!", job) # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_delete_complete') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + evt.fire_event({'complete': True, + 'schedule': self._get_schedule()}, + tag='/salt/minion/minion_schedule_delete_complete') # remove from self.intervals for job in list(self.intervals.keys()): @@ -377,10 +377,10 @@ def add_job(self, data, persist=True): self.opts['schedule'].update(data) # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_add_complete') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + evt.fire_event({'complete': True, + 'schedule': self._get_schedule()}, + tag='/salt/minion/minion_schedule_add_complete') if persist: self.persist() @@ -397,10 +397,10 @@ def enable_job(self, name, persist=True): log.warning("Cannot modify job %s, it's in the pillar!", name) # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_enabled_job_complete') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + evt.fire_event({'complete': True, + 'schedule': self._get_schedule()}, + tag='/salt/minion/minion_schedule_enabled_job_complete') if persist: self.persist() @@ -416,11 +416,11 @@ def disable_job(self, name, persist=True): elif name in self._get_schedule(include_opts=False): log.warning("Cannot modify job %s, it's in the pillar!", name) - # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_disabled_job_complete') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + # Fire the complete event back along with updated list of schedule + evt.fire_event({'complete': True, + 'schedule': self._get_schedule()}, + tag='/salt/minion/minion_schedule_disabled_job_complete') if persist: self.persist() @@ -486,9 +486,10 @@ def enable_schedule(self): self.opts['schedule']['enabled'] = True # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True, 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_enabled_complete') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + evt.fire_event({'complete': True, + 'schedule': self._get_schedule()}, + tag='/salt/minion/minion_schedule_enabled_complete') def disable_schedule(self): ''' @@ -497,9 +498,10 @@ def disable_schedule(self): self.opts['schedule']['enabled'] = False # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True, 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_disabled_complete') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + evt.fire_event({'complete': True, + 'schedule': self._get_schedule()}, + tag='/salt/minion/minion_schedule_disabled_complete') def reload(self, schedule): ''' @@ -524,9 +526,9 @@ def list(self, where): schedule = self._get_schedule() # Fire the complete event back along with the list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True, 'schedule': schedule}, - tag='/salt/minion/minion_schedule_list_complete') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + evt.fire_event({'complete': True, 'schedule': schedule}, + tag='/salt/minion/minion_schedule_list_complete') def save_schedule(self): ''' @@ -535,9 +537,9 @@ def save_schedule(self): self.persist() # Fire the complete event back along with the list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True}, - tag='/salt/minion/minion_schedule_saved') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + evt.fire_event({'complete': True}, + tag='/salt/minion/minion_schedule_saved') def postpone_job(self, name, data): ''' @@ -564,10 +566,10 @@ def postpone_job(self, name, data): log.warning("Cannot modify job %s, it's in the pillar!", name) # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_postpone_job_complete') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + evt.fire_event({'complete': True, + 'schedule': self._get_schedule()}, + tag='/salt/minion/minion_schedule_postpone_job_complete') def skip_job(self, name, data): ''' @@ -588,10 +590,10 @@ def skip_job(self, name, data): log.warning("Cannot modify job %s, it's in the pillar!", name) # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_skip_job_complete') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + evt.fire_event({'complete': True, + 'schedule': self._get_schedule()}, + tag='/salt/minion/minion_schedule_skip_job_complete') def get_next_fire_time(self, name, fmt='%Y-%m-%dT%H:%M:%S'): ''' @@ -606,9 +608,9 @@ def get_next_fire_time(self, name, fmt='%Y-%m-%dT%H:%M:%S'): _next_fire_time = _next_fire_time.strftime(fmt) # Fire the complete event back along with updated list of schedule - evt = salt.utils.event.get_event('minion', opts=self.opts, listen=False) - evt.fire_event({'complete': True, 'next_fire_time': _next_fire_time}, - tag='/salt/minion/minion_schedule_next_fire_time_complete') + with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + evt.fire_event({'complete': True, 'next_fire_time': _next_fire_time}, + tag='/salt/minion/minion_schedule_next_fire_time_complete') def job_status(self, name): ''' @@ -721,15 +723,14 @@ def handle_func(self, multiprocessing_enabled, func, data): jid = salt.utils.jid.gen_jid(self.opts) tag = salt.utils.event.tagify(jid, prefix='salt/scheduler/') - event = salt.utils.event.get_event( + namespaced_event = salt.utils.event.NamespacedEvent( + salt.utils.event.get_event( self.opts['__role'], self.opts['sock_dir'], self.opts['transport'], opts=self.opts, - listen=False) - - namespaced_event = salt.utils.event.NamespacedEvent( - event, + listen=False, + ), tag, print_func=None ) @@ -831,6 +832,11 @@ def handle_func(self, multiprocessing_enabled, func, data): event.fire_event(load, '__schedule_return') except Exception as exc: log.exception('Unhandled exception firing __schedule_return event') + finally: + event.destroy() + + if self.opts['__role'] == 'master': + namespaced_event.destroy() if not self.standalone: log.debug('schedule.handle_func: Removing %s', proc_fn) @@ -1662,7 +1668,7 @@ def _run_job(self, func, data): run_schedule_jobs_in_background = self.opts.get('run_schedule_jobs_in_background', True) if run_schedule_jobs_in_background is False: - # Explicitly pass False for multiprocessing_enabled + # Explicitly pass False for multiprocessing_enabled self.handle_func(False, func, data) return @@ -1679,7 +1685,7 @@ def _run_job(self, func, data): try: if multiprocessing_enabled: - thread_cls = salt.utils.process.SignalHandlingMultiprocessingProcess + thread_cls = salt.utils.process.SignalHandlingProcess else: thread_cls = threading.Thread diff --git a/salt/utils/stringutils.py b/salt/utils/stringutils.py index 50f6bd4f3229..e1eb6a06bf60 100644 --- a/salt/utils/stringutils.py +++ b/salt/utils/stringutils.py @@ -58,7 +58,7 @@ def to_bytes(s, encoding=None, errors='strict'): # raised, otherwise we would have already returned (or raised some # other exception). raise exc # pylint: disable=raising-bad-type - raise TypeError('expected bytes, bytearray, or str') + raise TypeError('expected str, bytes, or bytearray not {}'.format(type(s))) else: return to_str(s, encoding, errors) @@ -115,7 +115,7 @@ def _normalize(s): # raised, otherwise we would have already returned (or raised some # other exception). raise exc # pylint: disable=raising-bad-type - raise TypeError('expected str, bytearray, or unicode') + raise TypeError('expected str, bytes, or bytearray not {}'.format(type(s))) def to_unicode(s, encoding=None, errors='strict', normalize=False): @@ -140,7 +140,7 @@ def _normalize(s): return _normalize(s) elif isinstance(s, (bytes, bytearray)): return _normalize(to_str(s, encoding, errors)) - raise TypeError('expected str, bytes, or bytearray') + raise TypeError('expected str, bytes, or bytearray not {}'.format(type(s))) else: # This needs to be str and not six.string_types, since if the string is # already a unicode type, it does not need to be decoded (and doing so @@ -158,7 +158,7 @@ def _normalize(s): # raised, otherwise we would have already returned (or raised some # other exception). raise exc # pylint: disable=raising-bad-type - raise TypeError('expected str or bytearray') + raise TypeError('expected str, bytes, or bytearray not {}'.format(type(s))) @jinja_filter('str_to_num') # Remove this for Neon diff --git a/salt/utils/templates.py b/salt/utils/templates.py index be33b96e0c78..c4b925f4c3d4 100644 --- a/salt/utils/templates.py +++ b/salt/utils/templates.py @@ -161,7 +161,7 @@ def render_tmpl(tmplsrc, tmplsrc, exc, exc_info_on_loglevel=logging.DEBUG ) - raise exc + six.reraise(*sys.exc_info()) else: # assume tmplsrc is file-like. tmplstr = tmplsrc.read() tmplsrc.close() @@ -520,7 +520,26 @@ def render_cheetah_tmpl(tmplstr, context, tmplpath=None): Render a Cheetah template. ''' from Cheetah.Template import Template - return salt.utils.data.decode(Template(tmplstr, searchList=[context])) + + # Compile the template and render it into the class + tclass = Template.compile(tmplstr) + data = tclass(namespaces=[context]) + + # Figure out which method to call based on the type of tmplstr + if six.PY3 and isinstance(tmplstr, six.string_types): + # This should call .__unicode__() + res = str(data) + elif six.PY2 and isinstance(tmplstr, six.text_type): + # Expicitly call .__unicode__() + res = data.__unicode__() + elif isinstance(tmplstr, six.binary_type): + # This should call .__str() + res = str(data) + else: + raise SaltRenderError('Unknown type {!s} for Cheetah template while trying to render.'.format(type(tmplstr))) + + # Now we can decode it to the correct encoding + return salt.utils.data.decode(res) # pylint: enable=3rd-party-module-not-gated diff --git a/salt/textformat.py b/salt/utils/textformat.py similarity index 100% rename from salt/textformat.py rename to salt/utils/textformat.py diff --git a/salt/utils/thin.py b/salt/utils/thin.py index 3c8ffec8af3a..1b2f148f296c 100644 --- a/salt/utils/thin.py +++ b/salt/utils/thin.py @@ -789,7 +789,7 @@ def gen_min(cachedir, extra_mods='', overwrite=False, so_mods='', 'salt/minion.py', 'salt/pillar', 'salt/pillar/__init__.py', - 'salt/textformat.py', + 'salt/utils/textformat.py', 'salt/log', 'salt/log/__init__.py', 'salt/log/handlers', diff --git a/salt/utils/versions.py b/salt/utils/versions.py index aa95b8afb71e..763f1bcbce35 100644 --- a/salt/utils/versions.py +++ b/salt/utils/versions.py @@ -17,6 +17,9 @@ import numbers import sys import warnings +import datetime +import inspect +import contextlib # pylint: disable=blacklisted-module,no-name-in-module from distutils.version import StrictVersion as _StrictVersion from distutils.version import LooseVersion as _LooseVersion @@ -75,6 +78,28 @@ def _cmp(self, other): return 1 +def _format_warning(message, category, filename, lineno, line=None): + ''' + Replacement for warnings.formatwarning that disables the echoing of + the 'line' parameter. + ''' + return '{}:{}: {}: {}\n'.format( + filename, lineno, category.__name__, message + ) + + +@contextlib.contextmanager +def _patched_format_warning(): + if six.PY2: + saved = warnings.formatwarning + warnings.formatwarning = _format_warning + yield + warnings.formatwarning = saved + else: + # Under Py3 we no longer have to patch warnings.formatwarning + yield + + def warn_until(version, message, category=DeprecationWarning, @@ -125,7 +150,6 @@ def warn_until(version, _version_ = salt.version.SaltStackVersion(*_version_info_) if _version_ >= version: - import inspect caller = inspect.getframeinfo(sys._getframe(stacklevel - 1)) raise RuntimeError( 'The warning triggered on filename \'{filename}\', line number ' @@ -140,26 +164,86 @@ def warn_until(version, ) if _dont_call_warnings is False: - def _formatwarning(message, - category, - filename, - lineno, - line=None): # pylint: disable=W0613 - ''' - Replacement for warnings.formatwarning that disables the echoing of - the 'line' parameter. - ''' - return '{0}:{1}: {2}: {3}\n'.format( - filename, lineno, category.__name__, message + with _patched_format_warning(): + warnings.warn( + message.format(version=version.formatted_version), + category, + stacklevel=stacklevel + ) + + +def warn_until_date(date, + message, + category=DeprecationWarning, + stacklevel=None, + _current_date=None, + _dont_call_warnings=False): + ''' + Helper function to raise a warning, by default, a ``DeprecationWarning``, + until the provided ``date``, after which, a ``RuntimeError`` will + be raised to remind the developers to remove the warning because the + target date has been reached. + + :param date: A ``datetime.date`` or ``datetime.datetime`` instance. + :param message: The warning message to be displayed. + :param category: The warning class to be thrown, by default + ``DeprecationWarning`` + :param stacklevel: There should be no need to set the value of + ``stacklevel``. Salt should be able to do the right thing. + :param _dont_call_warnings: This parameter is used just to get the + functionality until the actual error is to be + issued. When we're only after the date + checks to raise a ``RuntimeError``. + ''' + _strptime_fmt = '%Y%m%d' + if not isinstance(date, (six.string_types, datetime.date, datetime.datetime)): + raise RuntimeError( + 'The \'date\' argument should be passed as a \'datetime.date()\' or ' + '\'datetime.datetime()\' objects or as string parserable by ' + '\'datetime.datetime.strptime()\' with the following format \'{}\'.'.format( + _strptime_fmt ) - saved = warnings.formatwarning - warnings.formatwarning = _formatwarning - warnings.warn( - message.format(version=version.formatted_version), - category, - stacklevel=stacklevel ) - warnings.formatwarning = saved + elif isinstance(date, six.text_type): + date = datetime.datetime.strptime(date, _strptime_fmt) + + # We're really not interested in the time + if isinstance(date, datetime.datetime): + date = date.date() + + if stacklevel is None: + # Attribute the warning to the calling function, not to warn_until_date() + stacklevel = 2 + + today = _current_date or datetime.datetime.utcnow().date() + if today >= date: + caller = inspect.getframeinfo(sys._getframe(stacklevel - 1)) + raise RuntimeError( + '{message} This warning(now exception) triggered on ' + 'filename \'{filename}\', line number {lineno}, is ' + 'supposed to be shown until {date}. Today is {today}. ' + 'Please remove the warning.'.format( + message=message.format( + date=date.isoformat(), + today=today.isoformat() + ), + filename=caller.filename, + lineno=caller.lineno, + date=date.isoformat(), + today=today.isoformat(), + ), + ) + + if _dont_call_warnings is False: + with _patched_format_warning(): + warnings.warn( + message.format( + date=date.isoformat(), + today=today.isoformat() + ), + category, + stacklevel=stacklevel + ) def kwargs_warn_until(kwargs, diff --git a/salt/utils/vmware.py b/salt/utils/vmware.py index 721f496bc36b..027699d78714 100644 --- a/salt/utils/vmware.py +++ b/salt/utils/vmware.py @@ -79,6 +79,7 @@ import errno import logging import time +import requests import sys import ssl @@ -100,6 +101,14 @@ except ImportError: HAS_PYVMOMI = False +try: + from com.vmware.vapi.std.errors_client import Unauthenticated + from vmware.vapi.vsphere.client import create_vsphere_client + HAS_VSPHERE_SDK = True + +except ImportError: + HAS_VSPHERE_SDK = False + try: import gssapi import base64 @@ -179,6 +188,42 @@ def esxcli(host, user, pwd, cmd, protocol=None, port=None, esxi_host=None, creds return ret +def get_vsphere_client(server, username, password, session=None): + ''' + Internal helper method to create an instance of the vSphere API client. + Please provide username and password to authenticate. + + :param basestring server: + vCenter host name or IP address + :param basestring username: + Name of the user + :param basestring password: + Password of the user + :param Session session: + Request HTTP session instance. If not specified, one + is automatically created and used + + :returns: + Vsphere Client instance + :rtype: + :class:`vmware.vapi.vmc.client.VsphereClient` + ''' + if not session: + # Create an https session to be used for a vSphere client + session = requests.session() + # If client uses own SSL cert, session should not verify + session.verify = False + client = None + try: + client = create_vsphere_client(server=server, + username=username, + password=password, + session=session) + except Unauthenticated as err: + log.trace(err) + return client + + def _get_service_instance(host, username, password, protocol, port, mechanism, principal, domain): ''' @@ -946,7 +991,7 @@ def get_mors_with_properties(service_instance, object_type, property_list=None, content = get_content(*content_args, **content_kwargs) except IOError as exc: if exc.errno != errno.EPIPE: - raise exc + six.reraise(*sys.exc_info()) content = get_content(*content_args, **content_kwargs) object_list = [] diff --git a/salt/utils/vt.py b/salt/utils/vt.py index 0212751edbc9..c771204062e8 100644 --- a/salt/utils/vt.py +++ b/salt/utils/vt.py @@ -805,7 +805,7 @@ def isalive(self, 'else call waitpid() on our process?' ) else: - raise err + six.reraise(*sys.exc_info()) # I have to do this twice for Solaris. # I can't even believe that I figured this out... @@ -824,7 +824,7 @@ def isalive(self, 'someone else call waitpid() on our process?' ) else: - raise + six.reraise(*sys.exc_info()) # If pid is still 0 after two calls to waitpid() then the # process really is alive. This seems to work on all platforms, @@ -931,6 +931,7 @@ def kill(self): # <---- Linux Methods ---------------------------------------------------- # ----- Cleanup!!! ------------------------------------------------------> + # pylint: disable=W1701 def __del__(self, _maxsize=sys.maxsize, _active=_ACTIVE): # pylint: disable=W0102 # I've disabled W0102 above which is regarding a dangerous default # value of [] for _ACTIVE, though, this is how Python itself handles @@ -945,5 +946,6 @@ def __del__(self, _maxsize=sys.maxsize, _active=_ACTIVE): # pylint: disable=W01 if self.isalive() and _ACTIVE is not None: # Child is still running, keep us alive until we can wait on it. _ACTIVE.append(self) + # pylint: enable=W1701 # <---- Cleanup!!! ------------------------------------------------------- # <---- Platform Specific Methods -------------------------------------------- diff --git a/salt/utils/win_functions.py b/salt/utils/win_functions.py index 059b3c1a668a..37044cd354e6 100644 --- a/salt/utils/win_functions.py +++ b/salt/utils/win_functions.py @@ -80,18 +80,27 @@ def get_user_groups(name, sid=False): Returns: list: A list of group names or sids ''' - if name == 'SYSTEM': + groups = [] + if name.upper() == 'SYSTEM': # 'win32net.NetUserGetLocalGroups' will fail if you pass in 'SYSTEM'. - groups = [name] + groups = ['SYSTEM'] else: - groups = win32net.NetUserGetLocalGroups(None, name) + try: + groups = win32net.NetUserGetLocalGroups(None, name) + except win32net.error as exc: + if exc.winerror == 5: + # Try without LG_INCLUDE_INDIRECT flag, because the user might + # not have permissions for it + groups = win32net.NetUserGetLocalGroups(None, name, 0) + else: + raise if not sid: return groups - ret_groups = set() + ret_groups = [] for group in groups: - ret_groups.add(get_sid_from_name(group)) + ret_groups.append(get_sid_from_name(group)) return ret_groups diff --git a/salt/utils/yamldumper.py b/salt/utils/yamldumper.py index c4597f5bb4ad..4980ce3b18ae 100644 --- a/salt/utils/yamldumper.py +++ b/salt/utils/yamldumper.py @@ -59,8 +59,13 @@ def represent_ordereddict(dumper, data): return dumper.represent_dict(list(data.items())) +def represent_undefined(dumper, data): + return dumper.represent_scalar(u'tag:yaml.org,2002:null', u'NULL') + + OrderedDumper.add_representer(OrderedDict, represent_ordereddict) SafeOrderedDumper.add_representer(OrderedDict, represent_ordereddict) +SafeOrderedDumper.add_representer(None, represent_undefined) OrderedDumper.add_representer( collections.defaultdict, diff --git a/salt/utils/yamlloader.py b/salt/utils/yamlloader.py index 3d2e3bea948c..5c2185675ed4 100644 --- a/salt/utils/yamlloader.py +++ b/salt/utils/yamlloader.py @@ -20,6 +20,7 @@ import salt.utils.stringutils + __all__ = ['SaltYamlSafeLoader', 'load', 'safe_load'] diff --git a/salt/utils/zfs.py b/salt/utils/zfs.py index b0d1af15f3ec..628d2448fd53 100644 --- a/salt/utils/zfs.py +++ b/salt/utils/zfs.py @@ -123,7 +123,7 @@ def _property_parse_cmd(cmd, alias=None): # NOTE: parse output prop_hdr = [] for prop_data in _exec(cmd=cmd)['stderr'].split('\n'): - # NOTE: make the line data more managable + # NOTE: make the line data more manageable prop_data = prop_data.lower().split() # NOTE: skip empty lines @@ -217,7 +217,7 @@ def _command(source, command, flags=None, opts=None, cmd.append(flag) # NOTE: append options - # we pass through 'sorted' to garentee the same order + # we pass through 'sorted' to guarantee the same order if opts is None: opts = {} for opt in sorted(opts): @@ -228,7 +228,7 @@ def _command(source, command, flags=None, opts=None, cmd.append(to_str(val)) # NOTE: append filesystem properties (really just options with a key/value) - # we pass through 'sorted' to garentee the same order + # we pass through 'sorted' to guarantee the same order if filesystem_properties is None: filesystem_properties = {} for fsopt in sorted(filesystem_properties): @@ -239,7 +239,7 @@ def _command(source, command, flags=None, opts=None, )) # NOTE: append pool properties (really just options with a key/value) - # we pass through 'sorted' to garentee the same order + # we pass through 'sorted' to guarantee the same order if pool_properties is None: pool_properties = {} for fsopt in sorted(pool_properties): @@ -330,8 +330,8 @@ def property_data_zpool(): .. warning:: - This data is probed from the output of 'zpool get' with some suplimental - data that is hardcoded. There is no better way to get this informatio aside + This data is probed from the output of 'zpool get' with some supplemental + data that is hardcoded. There is no better way to get this information aside from reading the code. ''' @@ -387,8 +387,8 @@ def property_data_zfs(): .. warning:: - This data is probed from the output of 'zfs get' with some suplimental - data that is hardcoded. There is no better way to get this informatio aside + This data is probed from the output of 'zfs get' with some supplemental + data that is hardcoded. There is no better way to get this information aside from reading the code. ''' @@ -476,7 +476,7 @@ def to_bool_alt(value): def from_size(value): ''' - Convert zfs size (human readble) to python int (bytes) + Convert zfs size (human readable) to python int (bytes) ''' match_size = re_zfs_size.match(str(value)) if match_size: @@ -676,7 +676,7 @@ def parse_command_result(res, label=None): .. note:: - Output on failure is rather predicatable. + Output on failure is rather predictable. - retcode > 0 - each 'error' is a line on stderr - optional 'Usage:' block under those with hits diff --git a/salt/version.py b/salt/version.py index 506a2b89bc2e..826f1379a7d0 100644 --- a/salt/version.py +++ b/salt/version.py @@ -108,8 +108,8 @@ class SaltStackVersion(object): 'Neon' : (MAX_SIZE - 99, 0), 'Sodium' : (MAX_SIZE - 98, 0), 'Magnesium' : (MAX_SIZE - 97, 0), + 'Aluminium' : (MAX_SIZE - 96, 0), # pylint: disable=E8265 - #'Aluminium' : (MAX_SIZE - 96, 0), #'Silicon' : (MAX_SIZE - 95, 0), #'Phosphorus' : (MAX_SIZE - 94, 0), #'Sulfur' : (MAX_SIZE - 93, 0), @@ -722,10 +722,11 @@ def versions_report(include_salt_cloud=False): Yield each version properly formatted for console output. ''' ver_info = versions_information(include_salt_cloud) - + not_installed = 'Not Installed' + ns_pad = len(not_installed) lib_pad = max(len(name) for name in ver_info['Dependency Versions']) sys_pad = max(len(name) for name in ver_info['System Versions']) - padding = max(lib_pad, sys_pad) + 1 + padding = max(lib_pad, sys_pad, ns_pad) + 1 fmt = '{0:>{pad}}: {1}' info = [] @@ -734,7 +735,7 @@ def versions_report(include_salt_cloud=False): # List dependencies in alphabetical, case insensitive order for name in sorted(ver_info[ver_type], key=lambda x: x.lower()): ver = fmt.format(name, - ver_info[ver_type][name] or 'Not Installed', + ver_info[ver_type][name] or not_installed, pad=padding) info.append(ver) info.append(' ') diff --git a/salt/wheel/__init__.py b/salt/wheel/__init__.py index 60368db377e8..93609c8f8fcb 100644 --- a/salt/wheel/__init__.py +++ b/salt/wheel/__init__.py @@ -71,17 +71,16 @@ def master_call(self, **kwargs): salt.utils.zeromq.ip_bracket(interface), six.text_type(self.opts['ret_port']) ) - channel = salt.transport.client.ReqChannel.factory(self.opts, - crypt='clear', - master_uri=master_uri, - usage='master_call') - try: + with salt.transport.client.ReqChannel.factory(self.opts, + crypt='clear', + master_uri=master_uri, + usage='master_call') as channel: ret = channel.send(load) - finally: - channel.close() + if isinstance(ret, collections.Mapping): if 'error' in ret: salt.utils.error.raise_error(**ret['error']) + return ret def cmd_sync(self, low, timeout=None, full_return=False): diff --git a/templates/test_module/tests/unit/modules/test_{{module_name}}.py b/templates/test_module/tests/unit/modules/test_{{module_name}}.py index db532d600c31..8fd4178f0da3 100644 --- a/templates/test_module/tests/unit/modules/test_{{module_name}}.py +++ b/templates/test_module/tests/unit/modules/test_{{module_name}}.py @@ -8,16 +8,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - NO_MOCK, - NO_MOCK_REASON -) +from tests.support.unit import TestCase +from tests.support.mock import patch import salt.modules.{{module_name}} as {{module_name}} -@skipIf(NO_MOCK, NO_MOCK_REASON) class {{module_name|capitalize}}TestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/templates/test_state/tests/unit/states/test_{{module_name}}.py b/templates/test_state/tests/unit/states/test_{{module_name}}.py index f3e5ac87e22b..c4481ae386ee 100644 --- a/templates/test_state/tests/unit/states/test_{{module_name}}.py +++ b/templates/test_state/tests/unit/states/test_{{module_name}}.py @@ -8,17 +8,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - NO_MOCK, - NO_MOCK_REASON -) +from tests.support.unit import TestCase +from tests.support.mock import patch import salt.states.{{module_name}} as {{module_name}} - -@skipIf(NO_MOCK, NO_MOCK_REASON) class {{module_name|capitalize}}TestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/conftest.py b/tests/conftest.py index e84436edaae7..94ff9254e397 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,71 +7,133 @@ Prepare py.test for our test suite ''' +# pylint: disable=wrong-import-order,wrong-import-position,3rd-party-local-module-not-gated +# pylint: disable=redefined-outer-name,invalid-name # Import python libs -from __future__ import absolute_import +from __future__ import absolute_import, print_function, unicode_literals import os import sys import stat +import pprint +import shutil import socket +import fnmatch import logging -from collections import namedtuple +import tempfile +import textwrap +from contextlib import contextmanager -TESTS_DIR = os.path.dirname( - os.path.normpath(os.path.abspath(__file__)) -) +TESTS_DIR = os.path.dirname(os.path.normpath(os.path.abspath(__file__))) CODE_DIR = os.path.dirname(TESTS_DIR) + +# Change to code checkout directory os.chdir(CODE_DIR) -try: - # If we have a system-wide salt module imported, unload it - import salt - for module in list(sys.modules): - if module.startswith(('salt',)): - try: - if not sys.modules[module].__file__.startswith(CODE_DIR): - sys.modules.pop(module) - except AttributeError: - continue - sys.path.insert(0, CODE_DIR) -except ImportError: - sys.path.insert(0, CODE_DIR) + +# Make sure the current directory is the first item in sys.path +if CODE_DIR in sys.path: + sys.path.remove(CODE_DIR) +sys.path.insert(0, CODE_DIR) # Import test libs -import tests.support.paths # pylint: disable=unused-import -from tests.integration import TestDaemon +from tests.support.runtests import RUNTIME_VARS +from tests.support.sminion import create_sminion # Import pytest libs import pytest -from _pytest.terminal import TerminalReporter +import _pytest.logging +import _pytest.skipping +from _pytest.mark.evaluate import MarkEvaluator # Import 3rd-party libs import psutil from salt.ext import six # Import salt libs +import salt.loader +import salt.config import salt.utils.files import salt.utils.path import salt.log.setup -from salt.utils.odict import OrderedDict +import salt.log.mixins +import salt.utils.platform +import salt.utils.win_functions +from salt.serializers import yaml +from salt.utils.immutabletypes import freeze + +# Import Pytest Salt libs +from pytestsalt.utils import cli_scripts + +# Coverage +if 'COVERAGE_PROCESS_START' in os.environ: + MAYBE_RUN_COVERAGE = True + COVERAGERC_FILE = os.environ['COVERAGE_PROCESS_START'] +else: + COVERAGERC_FILE = os.path.join(CODE_DIR, '.coveragerc') + MAYBE_RUN_COVERAGE = sys.argv[0].endswith('pytest.py') or '_COVERAGE_RCFILE' in os.environ + if MAYBE_RUN_COVERAGE: + # Flag coverage to track suprocesses by pointing it to the right .coveragerc file + os.environ[str('COVERAGE_PROCESS_START')] = str(COVERAGERC_FILE) # Define the pytest plugins we rely on -pytest_plugins = ['tempdir', 'helpers_namespace', 'salt-from-filenames'] # pylint: disable=invalid-name +pytest_plugins = ['tempdir', 'helpers_namespace', 'salt-runtests-bridge'] # Define where not to collect tests from collect_ignore = ['setup.py'] -log = logging.getLogger('salt.testsuite') + +# Patch PyTest logging handlers +class LogCaptureHandler(salt.log.mixins.ExcInfoOnLogLevelFormatMixIn, + _pytest.logging.LogCaptureHandler): + ''' + Subclassing PyTest's LogCaptureHandler in order to add the + exc_info_on_loglevel functionality. + ''' + + +_pytest.logging.LogCaptureHandler = LogCaptureHandler + + +class LiveLoggingStreamHandler(salt.log.mixins.ExcInfoOnLogLevelFormatMixIn, + _pytest.logging._LiveLoggingStreamHandler): + ''' + Subclassing PyTest's LiveLoggingStreamHandler in order to add the + exc_info_on_loglevel functionality. + ''' + + +_pytest.logging._LiveLoggingStreamHandler = LiveLoggingStreamHandler # Reset logging root handlers -for handler in logging.root.handlers: +for handler in logging.root.handlers[:]: logging.root.removeHandler(handler) +# Reset the root logger to it's default level(because salt changed it) +logging.root.setLevel(logging.WARNING) + +log = logging.getLogger('salt.testsuite') + + +# ----- PyTest Tempdir Plugin Hooks ---------------------------------------------------------------------------------> +def pytest_tempdir_temproot(): + # Taken from https://github.com/saltstack/salt/blob/v2019.2.0/tests/support/paths.py + # Avoid ${TMPDIR} and gettempdir() on MacOS as they yield a base path too long + # for unix sockets: ``error: AF_UNIX path too long`` + # Gentoo Portage prefers ebuild tests are rooted in ${TMPDIR} + if not sys.platform.startswith('darwin'): + tempdir = os.environ.get('TMPDIR') or tempfile.gettempdir() + else: + tempdir = '/tmp' + return os.path.abspath(os.path.realpath(tempdir)) + + def pytest_tempdir_basename(): ''' Return the temporary directory basename for the salt test suite. ''' - return 'salt-tests-tmp' + return 'salt-tests-tmpdir' +# <---- PyTest Tempdir Plugin Hooks ---------------------------------------------------------------------------------- # ----- CLI Options Setup -------------------------------------------------------------------------------------------> @@ -141,59 +203,19 @@ def pytest_addoption(parser): action='store_true', help='Disable colour printing.' ) -# <---- CLI Options Setup -------------------------------------------------------------------------------------------- - -# ----- CLI Terminal Reporter ---------------------------------------------------------------------------------------> -class SaltTerminalReporter(TerminalReporter): - def __init__(self, config): - TerminalReporter.__init__(self, config) - - @pytest.hookimpl(trylast=True) - def pytest_sessionstart(self, session): - TerminalReporter.pytest_sessionstart(self, session) - self._session = session - - def pytest_runtest_logreport(self, report): - TerminalReporter.pytest_runtest_logreport(self, report) - if self.verbosity <= 0: - return - if report.when != 'call': - return - if self.config.getoption('--sys-stats') is False: - return - - test_daemon = getattr(self._session, 'test_daemon', None) - if self.verbosity == 1: - line = ' [CPU:{0}%|MEM:{1}%]'.format(psutil.cpu_percent(), - psutil.virtual_memory().percent) - self._tw.write(line) - return - else: - self.ensure_newline() - template = ' {} - CPU: {:6.2f} % MEM: {:6.2f} % SWAP: {:6.2f} %\n' - self._tw.write( - template.format( - ' System', - psutil.cpu_percent(), - psutil.virtual_memory().percent, - psutil.swap_memory().percent - ) - ) - for name, psproc in self._session.stats_processes.items(): - with psproc.oneshot(): - cpu = psproc.cpu_percent() - mem = psproc.memory_percent('vms') - swap = psproc.memory_percent('swap') - self._tw.write(template.format(name, cpu, mem, swap)) - - -def pytest_sessionstart(session): - session.stats_processes = OrderedDict(( - #('Log Server', test_daemon.log_server), - (' Test Suite Run', psutil.Process(os.getpid())), - )) -# <---- CLI Terminal Reporter ---------------------------------------------------------------------------------------- + # ----- Test Groups ---------------------------------------------------------------------------------------------> + # This will allow running the tests in chunks + test_selection_group.addoption( + '--test-group-count', dest='test-group-count', type=int, + help='The number of groups to split the tests into' + ) + test_selection_group.addoption( + '--test-group', dest='test-group', type=int, + help='The group of tests that should be executed' + ) + # <---- Test Groups ---------------------------------------------------------------------------------------------- +# <---- CLI Options Setup -------------------------------------------------------------------------------------------- # ----- Register Markers --------------------------------------------------------------------------------------------> @@ -203,7 +225,17 @@ def pytest_configure(config): called after command line options have been parsed and all plugins and initial conftest files been loaded. ''' + for dirname in os.listdir(CODE_DIR): + if not os.path.isdir(dirname): + continue + if dirname != 'tests': + config.addinivalue_line('norecursedirs', os.path.join(CODE_DIR, dirname)) + config.addinivalue_line('norecursedirs', os.path.join(CODE_DIR, 'templates')) + config.addinivalue_line('norecursedirs', os.path.join(CODE_DIR, 'tests/kitchen')) + config.addinivalue_line('norecursedirs', os.path.join(CODE_DIR, 'tests/support')) + + # Expose the markers we use to pytest CLI config.addinivalue_line( 'markers', 'destructive_test: Run destructive tests. These tests can include adding ' @@ -224,44 +256,135 @@ def pytest_configure(config): 'requires_network(only_local_network=False): Skip if no networking is set up. ' 'If \'only_local_network\' is \'True\', only the local network is checked.' ) + config.addinivalue_line( + 'markers', + 'requires_salt_modules(*required_module_names): Skip if at least one module is not available. ' + ) + # Make sure the test suite "knows" this is a pytest test run + RUNTIME_VARS.PYTEST_SESSION = True +# <---- Register Markers --------------------------------------------------------------------------------------------- - # Register our terminal reporter - if not getattr(config, 'slaveinput', None): - standard_reporter = config.pluginmanager.getplugin('terminalreporter') - salt_reporter = SaltTerminalReporter(standard_reporter.config) - config.pluginmanager.unregister(standard_reporter) - config.pluginmanager.register(salt_reporter, 'terminalreporter') +# ----- PyTest Tweaks -----------------------------------------------------------------------------------------------> +def set_max_open_files_limits(min_soft=3072, min_hard=4096): + + # Get current limits + if salt.utils.platform.is_windows(): + import win32file + prev_hard = win32file._getmaxstdio() + prev_soft = 512 + else: + import resource + prev_soft, prev_hard = resource.getrlimit(resource.RLIMIT_NOFILE) + + # Check minimum required limits + set_limits = False + if prev_soft < min_soft: + soft = min_soft + set_limits = True + else: + soft = prev_soft + + if prev_hard < min_hard: + hard = min_hard + set_limits = True + else: + hard = prev_hard + + # Increase limits + if set_limits: + log.debug( + ' * Max open files settings is too low (soft: %s, hard: %s) for running the tests. ' + 'Trying to raise the limits to soft: %s, hard: %s', + prev_soft, + prev_hard, + soft, + hard + ) + try: + if salt.utils.platform.is_windows(): + hard = 2048 if hard > 2048 else hard + win32file._setmaxstdio(hard) + else: + resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard)) + except Exception as err: + log.error( + 'Failed to raise the max open files settings -> %s. Please issue the following command ' + 'on your console: \'ulimit -u %s\'', + err, + soft, + ) + exit(1) + return soft, hard - # Transplant configuration - TestDaemon.transplant_configs(transport=config.getoption('--transport')) -# <---- Register Markers --------------------------------------------------------------------------------------------- + +def pytest_report_header(): + soft, hard = set_max_open_files_limits() + return 'max open files; soft: {}; hard: {}'.format(soft, hard) + + +def pytest_runtest_logstart(nodeid): + ''' + implements the runtest_setup/call/teardown protocol for + the given test item, including capturing exceptions and calling + reporting hooks. + ''' + log.debug('>>>>> START >>>>> %s', nodeid) + + +def pytest_runtest_logfinish(nodeid): + ''' + called after ``pytest_runtest_call`` + ''' + log.debug('<<<<< END <<<<<<< %s', nodeid) +# <---- PyTest Tweaks ------------------------------------------------------------------------------------------------ # ----- Test Setup --------------------------------------------------------------------------------------------------> +def _has_unittest_attr(item, attr): + # XXX: This is a hack while we support both runtests.py and PyTest + if hasattr(item.obj, attr): + return True + if item.cls and hasattr(item.cls, attr): + return True + if item.parent and hasattr(item.parent.obj, attr): + return True + return False + + @pytest.hookimpl(tryfirst=True) def pytest_runtest_setup(item): ''' Fixtures injection based on markers or test skips based on CLI arguments ''' - destructive_tests_marker = item.get_marker('destructive_test') - if destructive_tests_marker is not None: + destructive_tests_marker = item.get_closest_marker('destructive_test') + if destructive_tests_marker is not None or _has_unittest_attr(item, '__destructive_test__'): if item.config.getoption('--run-destructive') is False: + item._skipped_by_mark = True pytest.skip('Destructive tests are disabled') - os.environ['DESTRUCTIVE_TESTS'] = six.text_type(item.config.getoption('--run-destructive')) + os.environ[str('DESTRUCTIVE_TESTS')] = str(item.config.getoption('--run-destructive')) - expensive_tests_marker = item.get_marker('expensive_test') - if expensive_tests_marker is not None: + expensive_tests_marker = item.get_closest_marker('expensive_test') + if expensive_tests_marker is not None or _has_unittest_attr(item, '__expensive_test__'): if item.config.getoption('--run-expensive') is False: + item._skipped_by_mark = True pytest.skip('Expensive tests are disabled') - os.environ['EXPENSIVE_TESTS'] = six.text_type(item.config.getoption('--run-expensive')) - - skip_if_not_root_marker = item.get_marker('skip_if_not_root') - if skip_if_not_root_marker is not None: - if os.getuid() != 0: - pytest.skip('You must be logged in as root to run this test') + os.environ[str('EXPENSIVE_TESTS')] = str(item.config.getoption('--run-expensive')) + + skip_if_not_root_marker = item.get_closest_marker('skip_if_not_root') + if skip_if_not_root_marker is not None or _has_unittest_attr(item, '__skip_if_not_root__'): + if not sys.platform.startswith('win'): + if os.getuid() != 0: + item._skipped_by_mark = True + pytest.skip('You must be logged in as root to run this test') + else: + current_user = salt.utils.win_functions.get_current_user() + if current_user != 'SYSTEM': + if not salt.utils.win_functions.is_admin(current_user): + item._skipped_by_mark = True + pytest.skip('You must be logged in as an Administrator to run this test') - skip_if_binaries_missing_marker = item.get_marker('skip_if_binaries_missing') + skip_if_binaries_missing_marker = item.get_closest_marker('skip_if_binaries_missing') if skip_if_binaries_missing_marker is not None: binaries = skip_if_binaries_missing_marker.args if len(binaries) == 1: @@ -272,6 +395,7 @@ def pytest_runtest_setup(item): if check_all: for binary in binaries: if salt.utils.path.which(binary) is None: + item._skipped_by_mark = True pytest.skip( '{0}The "{1}" binary was not found'.format( message and '{0}. '.format(message) or '', @@ -279,6 +403,7 @@ def pytest_runtest_setup(item): ) ) elif salt.utils.path.which_bin(binaries) is None: + item._skipped_by_mark = True pytest.skip( '{0}None of the following binaries was found: {1}'.format( message and '{0}. '.format(message) or '', @@ -286,7 +411,7 @@ def pytest_runtest_setup(item): ) ) - requires_network_marker = item.get_marker('requires_network') + requires_network_marker = item.get_closest_marker('requires_network') if requires_network_marker is not None: only_local_network = requires_network_marker.kwargs.get('only_local_network', False) has_local_network = False @@ -323,6 +448,7 @@ def pytest_runtest_setup(item): if has_local_network is False: # Since we're only supposed to check local network, and no # local network was detected, skip the test + item._skipped_by_mark = True pytest.skip('No local network was detected') # We are using the google.com DNS records as numerical IPs to avoid @@ -342,36 +468,103 @@ def pytest_runtest_setup(item): # Let's check the next IP continue else: + item._skipped_by_mark = True pytest.skip('No internet network connection was detected') + + requires_salt_modules_marker = item.get_closest_marker('requires_salt_modules') + if requires_salt_modules_marker is not None: + required_salt_modules = requires_salt_modules_marker.args + if len(required_salt_modules) == 1 and isinstance(required_salt_modules[0], (list, tuple, set)): + required_salt_modules = required_salt_modules[0] + required_salt_modules = set(required_salt_modules) + sminion = create_sminion() + available_modules = list(sminion.functions) + not_available_modules = set() + try: + cached_not_available_modules = sminion.__not_availiable_modules__ + except AttributeError: + cached_not_available_modules = sminion.__not_availiable_modules__ = set() + + if cached_not_available_modules: + for not_available_module in cached_not_available_modules: + if not_available_module in required_salt_modules: + not_available_modules.add(not_available_module) + required_salt_modules.remove(not_available_module) + + for required_module_name in required_salt_modules: + search_name = required_module_name + if '.' not in search_name: + search_name += '.*' + if not fnmatch.filter(available_modules, search_name): + not_available_modules.add(required_module_name) + cached_not_available_modules.add(required_module_name) + + if not_available_modules: + item._skipped_by_mark = True + if len(not_available_modules) == 1: + pytest.skip('Salt module \'{}\' is not available'.format(*not_available_modules)) + pytest.skip('Salt modules not available: {}'.format(', '.join(not_available_modules))) # <---- Test Setup --------------------------------------------------------------------------------------------------- -# ----- Automatic Markers Setup -------------------------------------------------------------------------------------> -def pytest_collection_modifyitems(items): - ''' - Automatically add markers to tests based on directory layout - ''' - for item in items: - fspath = str(item.fspath) - if '/integration/' in fspath: - if 'test_daemon' not in item.fixturenames: - item.fixturenames.append('test_daemon') - item.add_marker(pytest.mark.integration) - for kind in ('cli', 'client', 'cloud', 'fileserver', 'loader', 'minion', 'modules', - 'netapi', 'output', 'reactor', 'renderers', 'runners', 'sdb', 'shell', - 'ssh', 'states', 'utils', 'wheel'): - if '/{0}/'.format(kind) in fspath: - item.add_marker(getattr(pytest.mark, kind)) - break - if '/unit/' in fspath: - item.add_marker(pytest.mark.unit) - for kind in ('acl', 'beacons', 'cli', 'cloud', 'config', 'grains', 'modules', 'netapi', - 'output', 'pillar', 'renderers', 'runners', 'serializers', 'states', - 'templates', 'transport', 'utils'): - if '/{0}/'.format(kind) in fspath: - item.add_marker(getattr(pytest.mark, kind)) - break -# <---- Automatic Markers Setup -------------------------------------------------------------------------------------- +# ----- Test Groups Selection ---------------------------------------------------------------------------------------> +def get_group_size(total_items, total_groups): + ''' + Return the group size. + ''' + return int(total_items / total_groups) + + +def get_group(items, group_count, group_size, group_id): + ''' + Get the items from the passed in group based on group size. + ''' + start = group_size * (group_id - 1) + end = start + group_size + total_items = len(items) + + if start >= total_items: + pytest.fail("Invalid test-group argument. start({})>=total_items({})".format(start, total_items)) + elif start < 0: + pytest.fail("Invalid test-group argument. Start({})<0".format(start)) + + if group_count == group_id and end < total_items: + # If this is the last group and there are still items to test + # which don't fit in this group based on the group items count + # add them anyway + end = total_items + + return items[start:end] + + +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_collection_modifyitems(config, items): + # Let PyTest or other plugins handle the initial collection + yield + + group_count = config.getoption('test-group-count') + group_id = config.getoption('test-group') + + if not group_count or not group_id: + # We're not selection tests using groups, don't do any filtering + return + + total_items = len(items) + + group_size = get_group_size(total_items, group_count) + tests_in_group = get_group(items, group_count, group_size, group_id) + # Replace all items in the list + items[:] = tests_in_group + + terminal_reporter = config.pluginmanager.get_plugin('terminalreporter') + terminal_reporter.write( + 'Running test group #{0} ({1} tests)\n'.format( + group_id, + len(items) + ), + yellow=True + ) +# <---- Test Groups Selection ---------------------------------------------------------------------------------------- # ----- Pytest Helpers ----------------------------------------------------------------------------------------------> @@ -460,6 +653,64 @@ def _readline_side_effect(): def mock_open(mock=None, read_data=''): _mock = pytest.importorskip('mock', minversion='2.0.0') return _mock.mock_open(mock=mock, read_data=read_data) + + +@pytest.helpers.register +@contextmanager +def temp_directory(name=None): + if name is not None: + directory_path = os.path.join(RUNTIME_VARS.TMP, name) + else: + directory_path = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + + yield directory_path + + shutil.rmtree(directory_path, ignore_errors=True) + + +@pytest.helpers.register +@contextmanager +def temp_file(name, contents=None, directory=None, strip_first_newline=True): + if directory is None: + directory = RUNTIME_VARS.TMP + + file_path = os.path.join(directory, name) + file_directory = os.path.dirname(file_path) + if contents is not None: + if contents: + if contents.startswith('\n') and strip_first_newline: + contents = contents[1:] + file_contents = textwrap.dedent(contents) + else: + file_contents = contents + + try: + if not os.path.isdir(file_directory): + os.makedirs(file_directory) + if contents is not None: + with salt.utils.files.fopen(file_path, 'w') as wfh: + wfh.write(file_contents) + + yield file_path + + finally: + try: + os.unlink(file_path) + except OSError: + # Already deleted + pass + + +@pytest.helpers.register +def temp_state_file(name, contents, saltenv='base', strip_first_newline=True): + + if saltenv == 'base': + directory = RUNTIME_VARS.TMP_STATE_TREE + elif saltenv == 'prod': + directory = RUNTIME_VARS.TMP_PRODENV_STATE_TREE + else: + raise RuntimeError('"saltenv" can only be "base" or "prod", not "{}"'.format(saltenv)) + return temp_file(name, contents, directory=directory, strip_first_newline=strip_first_newline) # <---- Pytest Helpers ----------------------------------------------------------------------------------------------- @@ -470,7 +721,7 @@ def cli_master_script_name(): ''' Return the CLI script basename ''' - return 'cli_salt_master' + return 'cli_salt_master.py' @pytest.fixture(scope='session') @@ -478,7 +729,7 @@ def cli_minion_script_name(): ''' Return the CLI script basename ''' - return 'cli_salt_minion' + return 'cli_salt_minion.py' @pytest.fixture(scope='session') @@ -486,7 +737,7 @@ def cli_salt_script_name(): ''' Return the CLI script basename ''' - return 'cli_salt' + return 'cli_salt.py' @pytest.fixture(scope='session') @@ -494,7 +745,7 @@ def cli_run_script_name(): ''' Return the CLI script basename ''' - return 'cli_salt_run' + return 'cli_salt_run.py' @pytest.fixture(scope='session') @@ -502,7 +753,7 @@ def cli_key_script_name(): ''' Return the CLI script basename ''' - return 'cli_salt_key' + return 'cli_salt_key.py' @pytest.fixture(scope='session') @@ -510,7 +761,7 @@ def cli_call_script_name(): ''' Return the CLI script basename ''' - return 'cli_salt_call' + return 'cli_salt_call.py' @pytest.fixture(scope='session') @@ -518,7 +769,7 @@ def cli_syndic_script_name(): ''' Return the CLI script basename ''' - return 'cli_salt_syndic' + return 'cli_salt_syndic.py' @pytest.fixture(scope='session') @@ -526,7 +777,15 @@ def cli_ssh_script_name(): ''' Return the CLI script basename ''' - return 'cli_salt_ssh' + return 'cli_salt_ssh.py' + + +@pytest.fixture(scope='session') +def cli_proxy_script_name(): + ''' + Return the CLI script basename + ''' + return 'cli_salt_proxy.py' @pytest.fixture(scope='session') @@ -539,69 +798,36 @@ def cli_bin_dir(tempdir, cli_call_script_name, cli_key_script_name, cli_run_script_name, - cli_ssh_script_name): + cli_ssh_script_name, + cli_syndic_script_name, + cli_proxy_script_name): ''' Return the path to the CLI script directory to use ''' tmp_cli_scripts_dir = tempdir.join('cli-scrips-bin') + # Make sure we re-write the scripts every time we start the tests + shutil.rmtree(tmp_cli_scripts_dir.strpath, ignore_errors=True) tmp_cli_scripts_dir.ensure(dir=True) cli_bin_dir_path = tmp_cli_scripts_dir.strpath # Now that we have the CLI directory created, lets generate the required CLI scripts to run salt's test suite - script_templates = { - 'salt': [ - 'from salt.scripts import salt_main\n', - 'if __name__ == \'__main__\':\n' - ' salt_main()' - ], - 'salt-api': [ - 'import salt.cli\n', - 'def main():\n', - ' sapi = salt.cli.SaltAPI()', - ' sapi.run()\n', - 'if __name__ == \'__main__\':', - ' main()' - ], - 'common': [ - 'from salt.scripts import salt_{0}\n', - 'if __name__ == \'__main__\':\n', - ' salt_{0}()' - ] - } - for script_name in (cli_master_script_name, cli_minion_script_name, cli_call_script_name, cli_key_script_name, cli_run_script_name, cli_salt_script_name, - cli_ssh_script_name): - original_script_name = script_name.split('cli_')[-1].replace('_', '-') - script_path = os.path.join(cli_bin_dir_path, script_name) - - if not os.path.isfile(script_path): - log.info('Generating {0}'.format(script_path)) - - with salt.utils.files.fopen(script_path, 'w') as sfh: - script_template = script_templates.get(original_script_name, None) - if script_template is None: - script_template = script_templates.get('common', None) - if script_template is None: - raise RuntimeError( - 'Salt\'s test suite does not know how to handle the "{0}" script'.format( - original_script_name - ) - ) - sfh.write( - '#!{0}\n\n'.format(python_executable_path) + - 'import sys\n' + - 'CODE_DIR="{0}"\n'.format(request.config.startdir.realpath().strpath) + - 'if CODE_DIR not in sys.path:\n' + - ' sys.path.insert(0, CODE_DIR)\n\n' + - '\n'.join(script_template).format(original_script_name.replace('salt-', '')) - ) - fst = os.stat(script_path) - os.chmod(script_path, fst.st_mode | stat.S_IEXEC) + cli_ssh_script_name, + cli_syndic_script_name, + cli_proxy_script_name): + original_script_name = os.path.splitext(script_name)[0].split('cli_')[-1].replace('_', '-') + cli_scripts.generate_script( + bin_dir=cli_bin_dir_path, + script_name=original_script_name, + executable=sys.executable, + code_dir=CODE_DIR, + inject_sitecustomize=MAYBE_RUN_COVERAGE + ) # Return the CLI bin dir value return cli_bin_dir_path @@ -610,56 +836,469 @@ def cli_bin_dir(tempdir, # ----- Salt Configuration ------------------------------------------------------------------------------------------> @pytest.fixture(scope='session') -def session_integration_files_dir(request): +def session_master_of_masters_id(): ''' - Fixture which returns the salt integration files directory path. - Creates the directory if it does not yet exist. + Returns the master of masters id ''' - return request.config.startdir.join('tests').join('integration').join('files') + return 'syndic_master' @pytest.fixture(scope='session') -def session_state_tree_root_dir(session_integration_files_dir): +def session_master_id(): ''' - Fixture which returns the salt state tree root directory path. - Creates the directory if it does not yet exist. + Returns the session scoped master id ''' - return session_integration_files_dir.join('file') + return 'master' @pytest.fixture(scope='session') -def session_pillar_tree_root_dir(session_integration_files_dir): +def session_minion_id(): ''' - Fixture which returns the salt pillar tree root directory path. - Creates the directory if it does not yet exist. + Returns the session scoped minion id ''' - return session_integration_files_dir.join('pillar') -# <---- Salt Configuration ------------------------------------------------------------------------------------------- + return 'minion' +@pytest.fixture(scope='session') +def session_secondary_minion_id(): + ''' + Returns the session scoped secondary minion id + ''' + return 'sub_minion' + + +@pytest.fixture(scope='session') +def session_syndic_id(): + ''' + Returns the session scoped syndic id + ''' + return 'syndic' + + +@pytest.fixture(scope='session') +def session_proxy_id(): + ''' + Returns the session scoped proxy id + ''' + return 'proxytest' + + +@pytest.fixture(scope='session') +def salt_fail_hard(): + ''' + Return the salt fail hard value + ''' + return True + + +@pytest.fixture(scope='session') +def session_master_default_options(request, session_root_dir): + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'master')) as rfh: + opts = yaml.deserialize(rfh.read()) + + tests_known_hosts_file = session_root_dir.join('salt_ssh_known_hosts').strpath + with salt.utils.files.fopen(tests_known_hosts_file, 'w') as known_hosts: + known_hosts.write('') + + opts['known_hosts_file'] = tests_known_hosts_file + opts['syndic_master'] = 'localhost' + opts['transport'] = request.config.getoption('--transport') + + # Config settings to test `event_return` + if 'returner_dirs' not in opts: + opts['returner_dirs'] = [] + opts['returner_dirs'].append(os.path.join(RUNTIME_VARS.FILES, 'returners')) + opts['event_return'] = 'runtests_noop' + + return opts + + +@pytest.fixture(scope='session') +def session_master_config_overrides(session_root_dir): + ext_pillar = [] + if salt.utils.platform.is_windows(): + ext_pillar.append( + {'cmd_yaml': 'type {0}'.format(os.path.join(RUNTIME_VARS.FILES, 'ext.yaml'))} + ) + else: + ext_pillar.append( + {'cmd_yaml': 'cat {0}'.format(os.path.join(RUNTIME_VARS.FILES, 'ext.yaml'))} + ) + ext_pillar.append( + { + 'file_tree': { + 'root_dir': os.path.join(RUNTIME_VARS.PILLAR_DIR, 'base', 'file_tree'), + 'follow_dir_links': False, + 'keep_newline': True + } + } + ) + + # We need to copy the extension modules into the new master root_dir or + # it will be prefixed by it + extension_modules_path = session_root_dir.join('extension_modules').strpath + if not os.path.exists(extension_modules_path): + shutil.copytree( + os.path.join( + RUNTIME_VARS.FILES, 'extension_modules' + ), + extension_modules_path + ) + + # Copy the autosign_file to the new master root_dir + autosign_file_path = session_root_dir.join('autosign_file').strpath + shutil.copyfile( + os.path.join(RUNTIME_VARS.FILES, 'autosign_file'), + autosign_file_path + ) + # all read, only owner write + autosign_file_permissions = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + os.chmod(autosign_file_path, autosign_file_permissions) + + pytest_stop_sending_events_file = session_root_dir.join('pytest_stop_sending_events_file').strpath + with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: + wfh.write('') + + return { + 'pillar_opts': True, + 'ext_pillar': ext_pillar, + 'extension_modules': extension_modules_path, + 'file_roots': { + 'base': [ + os.path.join(RUNTIME_VARS.FILES, 'file', 'base'), + ], + # Alternate root to test __env__ choices + 'prod': [ + os.path.join(RUNTIME_VARS.FILES, 'file', 'prod'), + ] + }, + 'pillar_roots': { + 'base': [ + os.path.join(RUNTIME_VARS.FILES, 'pillar', 'base'), + ] + }, + 'reactor': [ + { + 'salt/minion/*/start': [ + os.path.join(RUNTIME_VARS.FILES, 'reactor-sync-minion.sls') + ], + }, + { + 'salt/test/reactor': [ + os.path.join(RUNTIME_VARS.FILES, 'reactor-test.sls') + ], + } + ], + 'pytest_stop_sending_events_file': pytest_stop_sending_events_file + } + + +@pytest.fixture(scope='session') +def session_minion_default_options(request, tempdir): + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'minion')) as rfh: + opts = yaml.deserialize(rfh.read()) + + opts['hosts.file'] = tempdir.join('hosts').strpath + opts['aliases.file'] = tempdir.join('aliases').strpath + opts['transport'] = request.config.getoption('--transport') + + return opts + + +def _get_virtualenv_binary_path(): + try: + return _get_virtualenv_binary_path.__virtualenv_binary__ + except AttributeError: + # Under windows we can't seem to properly create a virtualenv off of another + # virtualenv, we can on linux but we will still point to the virtualenv binary + # outside the virtualenv running the test suite, if that's the case. + try: + real_prefix = sys.real_prefix + # The above attribute exists, this is a virtualenv + if salt.utils.platform.is_windows(): + virtualenv_binary = os.path.join(real_prefix, 'Scripts', 'virtualenv.exe') + else: + # We need to remove the virtualenv from PATH or we'll get the virtualenv binary + # from within the virtualenv, we don't want that + path = os.environ.get('PATH') + if path is not None: + path_items = path.split(os.pathsep) + for item in path_items[:]: + if item.startswith(sys.base_prefix): + path_items.remove(item) + os.environ['PATH'] = os.pathsep.join(path_items) + virtualenv_binary = salt.utils.path.which('virtualenv') + if path is not None: + # Restore previous environ PATH + os.environ['PATH'] = path + if not virtualenv_binary.startswith(real_prefix): + virtualenv_binary = None + if virtualenv_binary and not os.path.exists(virtualenv_binary): + # It doesn't exist?! + virtualenv_binary = None + except AttributeError: + # We're not running inside a virtualenv + virtualenv_binary = None + _get_virtualenv_binary_path.__virtualenv_binary__ = virtualenv_binary + return virtualenv_binary + + +@pytest.fixture(scope='session') +def session_minion_config_overrides(): + opts = { + 'file_roots': { + 'base': [ + os.path.join(RUNTIME_VARS.FILES, 'file', 'base'), + ], + # Alternate root to test __env__ choices + 'prod': [ + os.path.join(RUNTIME_VARS.FILES, 'file', 'prod'), + ] + }, + 'pillar_roots': { + 'base': [ + os.path.join(RUNTIME_VARS.FILES, 'pillar', 'base'), + ] + }, + } + virtualenv_binary = _get_virtualenv_binary_path() + if virtualenv_binary: + opts['venv_bin'] = virtualenv_binary + return opts + + +@pytest.fixture(scope='session') +def session_secondary_minion_default_options(request, tempdir): + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'sub_minion')) as rfh: + opts = yaml.deserialize(rfh.read()) + + opts['hosts.file'] = tempdir.join('hosts').strpath + opts['aliases.file'] = tempdir.join('aliases').strpath + opts['transport'] = request.config.getoption('--transport') + + return opts + + +@pytest.fixture(scope='session') +def session_seconary_minion_config_overrides(): + opts = {} + virtualenv_binary = _get_virtualenv_binary_path() + if virtualenv_binary: + opts['venv_bin'] = virtualenv_binary + return opts + + +@pytest.fixture(scope='session') +def session_master_of_masters_default_options(request, tempdir): + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic_master')) as rfh: + opts = yaml.deserialize(rfh.read()) + + opts['hosts.file'] = tempdir.join('hosts').strpath + opts['aliases.file'] = tempdir.join('aliases').strpath + opts['transport'] = request.config.getoption('--transport') + + return opts + + +@pytest.fixture(scope='session') +def session_master_of_masters_config_overrides(session_master_of_masters_root_dir): + if salt.utils.platform.is_windows(): + ext_pillar = {'cmd_yaml': 'type {0}'.format(os.path.join(RUNTIME_VARS.FILES, 'ext.yaml'))} + else: + ext_pillar = {'cmd_yaml': 'cat {0}'.format(os.path.join(RUNTIME_VARS.FILES, 'ext.yaml'))} + + # We need to copy the extension modules into the new master root_dir or + # it will be prefixed by it + extension_modules_path = session_master_of_masters_root_dir.join('extension_modules').strpath + if not os.path.exists(extension_modules_path): + shutil.copytree( + os.path.join( + RUNTIME_VARS.FILES, 'extension_modules' + ), + extension_modules_path + ) + + # Copy the autosign_file to the new master root_dir + autosign_file_path = session_master_of_masters_root_dir.join('autosign_file').strpath + shutil.copyfile( + os.path.join(RUNTIME_VARS.FILES, 'autosign_file'), + autosign_file_path + ) + # all read, only owner write + autosign_file_permissions = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + os.chmod(autosign_file_path, autosign_file_permissions) + + pytest_stop_sending_events_file = session_master_of_masters_root_dir.join('pytest_stop_sending_events_file').strpath + with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: + wfh.write('') + + return { + 'ext_pillar': [ext_pillar], + 'extension_modules': extension_modules_path, + 'file_roots': { + 'base': [ + os.path.join(RUNTIME_VARS.FILES, 'file', 'base'), + ], + # Alternate root to test __env__ choices + 'prod': [ + os.path.join(RUNTIME_VARS.FILES, 'file', 'prod'), + ] + }, + 'pillar_roots': { + 'base': [ + os.path.join(RUNTIME_VARS.FILES, 'pillar', 'base'), + ] + }, + 'pytest_stop_sending_events_file': pytest_stop_sending_events_file + } + + +@pytest.fixture(scope='session') +def session_syndic_master_default_options(request, tempdir): + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic_master')) as rfh: + opts = yaml.deserialize(rfh.read()) + + opts['hosts.file'] = tempdir.join('hosts').strpath + opts['aliases.file'] = tempdir.join('aliases').strpath + opts['transport'] = request.config.getoption('--transport') + + return opts + + +@pytest.fixture(scope='session') +def session_syndic_default_options(request, tempdir): + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic')) as rfh: + opts = yaml.deserialize(rfh.read()) + + opts['hosts.file'] = tempdir.join('hosts').strpath + opts['aliases.file'] = tempdir.join('aliases').strpath + opts['transport'] = request.config.getoption('--transport') + + return opts + + +@pytest.fixture(scope='session') +def session_proxy_default_options(request, tempdir): + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'proxy')) as rfh: + opts = yaml.deserialize(rfh.read()) + + opts['hosts.file'] = tempdir.join('hosts').strpath + opts['aliases.file'] = tempdir.join('aliases').strpath + opts['transport'] = request.config.getoption('--transport') + + return opts + + +@pytest.fixture(scope='session', autouse=True) +def bridge_pytest_and_runtests(reap_stray_processes, + session_root_dir, + session_conf_dir, + session_secondary_conf_dir, + session_syndic_conf_dir, + session_master_of_masters_conf_dir, + session_base_env_pillar_tree_root_dir, + session_base_env_state_tree_root_dir, + session_prod_env_state_tree_root_dir, + session_master_config, + session_minion_config, + session_secondary_minion_config, + session_master_of_masters_config, + session_syndic_config): + + # Make sure unittest2 classes know their paths + RUNTIME_VARS.TMP_ROOT_DIR = session_root_dir.realpath().strpath + RUNTIME_VARS.TMP_CONF_DIR = session_conf_dir.realpath().strpath + RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR = session_secondary_conf_dir.realpath().strpath + RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR = session_master_of_masters_conf_dir.realpath().strpath + RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR = session_syndic_conf_dir.realpath().strpath + RUNTIME_VARS.TMP_PILLAR_TREE = session_base_env_pillar_tree_root_dir.realpath().strpath + RUNTIME_VARS.TMP_STATE_TREE = session_base_env_state_tree_root_dir.realpath().strpath + RUNTIME_VARS.TMP_PRODENV_STATE_TREE = session_prod_env_state_tree_root_dir.realpath().strpath + + # Make sure unittest2 uses the pytest generated configuration + RUNTIME_VARS.RUNTIME_CONFIGS['master'] = freeze(session_master_config) + RUNTIME_VARS.RUNTIME_CONFIGS['minion'] = freeze(session_minion_config) + RUNTIME_VARS.RUNTIME_CONFIGS['sub_minion'] = freeze(session_secondary_minion_config) + RUNTIME_VARS.RUNTIME_CONFIGS['syndic_master'] = freeze(session_master_of_masters_config) + RUNTIME_VARS.RUNTIME_CONFIGS['syndic'] = freeze(session_syndic_config) + RUNTIME_VARS.RUNTIME_CONFIGS['client_config'] = freeze( + salt.config.client_config(session_conf_dir.join('master').strpath) + ) + + # Copy configuration files and directories which are not automatically generated + for entry in os.listdir(RUNTIME_VARS.CONF_DIR): + if entry in ('master', 'minion', 'sub_minion', 'syndic', 'syndic_master', 'proxy'): + # These have runtime computed values and are handled by pytest-salt fixtures + continue + entry_path = os.path.join(RUNTIME_VARS.CONF_DIR, entry) + if os.path.isfile(entry_path): + shutil.copy( + entry_path, + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry) + ) + elif os.path.isdir(entry_path): + shutil.copytree( + entry_path, + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry) + ) +# <---- Salt Configuration ------------------------------------------------------------------------------------------- # <---- Fixtures Overrides ------------------------------------------------------------------------------------------- -# ----- Custom Fixtures Definitions ---------------------------------------------------------------------------------> + + +# ----- Custom Grains Mark Evaluator --------------------------------------------------------------------------------> +class GrainsMarkEvaluator(MarkEvaluator): + _cached_grains = None + + def _getglobals(self): + item_globals = super(GrainsMarkEvaluator, self)._getglobals() + if GrainsMarkEvaluator._cached_grains is None: + sminion = create_sminion() + GrainsMarkEvaluator._cached_grains = sminion.opts['grains'].copy() + item_globals['grains'] = GrainsMarkEvaluator._cached_grains.copy() + return item_globals + + +# Patch PyTest's skipping MarkEvaluator to use our GrainsMarkEvaluator +_pytest.skipping.MarkEvaluator = GrainsMarkEvaluator +# <---- Custom Grains Mark Evaluator --------------------------------------------------------------------------------- + + +# ----- Custom Fixtures ---------------------------------------------------------------------------------------------> +@pytest.fixture(scope='session') +def reap_stray_processes(): + # Run tests + yield + + children = psutil.Process(os.getpid()).children(recursive=True) + if not children: + log.info('No astray processes found') + return + + def on_terminate(proc): + log.debug('Process %s terminated with exit code %s', proc, proc.returncode) + + if children: + # Reverse the order, sublings first, parents after + children.reverse() + log.warning( + 'Test suite left %d astray processes running. Killing those processes:\n%s', + len(children), + pprint.pformat(children) + ) + + _, alive = psutil.wait_procs(children, timeout=3, callback=on_terminate) + for child in alive: + child.kill() + + _, alive = psutil.wait_procs(alive, timeout=3, callback=on_terminate) + if alive: + # Give up + for child in alive: + log.warning('Process %s survived SIGKILL, giving up:\n%s', child, pprint.pformat(child.as_dict())) + + @pytest.fixture(scope='session') -def test_daemon(request): - values = (('transport', request.config.getoption('--transport')), - ('sysinfo', request.config.getoption('--sysinfo')), - ('no_colors', request.config.getoption('--no-colors')), - ('output_columns', request.config.getoption('--output-columns')), - ('ssh', request.config.getoption('--ssh')), - ('proxy', request.config.getoption('--proxy'))) - options = namedtuple('options', [n for n, v in values])(*[v for n, v in values]) - fake_parser = namedtuple('parser', 'options')(options) - - test_daemon = TestDaemon(fake_parser) - with test_daemon as test_daemon_running: - request.session.test_daemon = test_daemon_running - request.session.stats_processes.update(OrderedDict(( - (' Salt Master', psutil.Process(test_daemon.master_process.pid)), - (' Salt Minion', psutil.Process(test_daemon.minion_process.pid)), - (' Salt Sub Minion', psutil.Process(test_daemon.sub_minion_process.pid)), - ('Salt Syndic Master', psutil.Process(test_daemon.smaster_process.pid)), - (' Salt Syndic', psutil.Process(test_daemon.syndic_process.pid)), - )).items()) - yield - TestDaemon.clean() -# <---- Custom Fixtures Definitions ---------------------------------------------------------------------------------- +def grains(request): + sminion = create_sminion() + return sminion.opts['grains'].copy() +# <---- Custom Fixtures ---------------------------------------------------------------------------------------------- diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index 2bfd91760dbb..b9114972e775 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -36,7 +36,7 @@ from tests.support.case import ShellTestCase from tests.support.parser import PNUM, print_header, SaltTestcaseParser from tests.support.helpers import requires_sshd_server, RedirectStdStreams -from tests.support.paths import ScriptPathMixin +from tests.support.cli_scripts import ScriptPathMixin from tests.support.mixins import CheckShellBinaryNameAndVersionMixin, ShellCaseCommonTestsMixin from tests.support.mixins import AdaptedConfigurationTestCaseMixin, SaltClientTestCaseMixin from tests.support.mixins import SaltMinionEventAssertsMixin, SaltReturnAssertsMixin @@ -52,6 +52,7 @@ import salt.version import salt.utils.color import salt.utils.files +import salt.utils.msgpack import salt.utils.path import salt.utils.platform import salt.utils.process @@ -63,7 +64,6 @@ from salt.exceptions import SaltClientError # Import 3rd-party libs -import msgpack from salt.ext import six try: @@ -95,15 +95,16 @@ def get_unused_localhost_port(): DARWIN = True if sys.platform.startswith('darwin') else False BSD = True if 'bsd' in sys.platform else False + AIX = True if sys.platform.startswith('aix') else False - if DARWIN and port in _RUNTESTS_PORTS: + if (AIX or DARWIN) and port in _RUNTESTS_PORTS: port = get_unused_localhost_port() usock.close() return port _RUNTESTS_PORTS[port] = usock - if DARWIN or BSD: + if DARWIN or BSD or AIX: usock.close() return port @@ -141,7 +142,7 @@ def server_close(self): class SocketServerRequestHandler(socketserver.StreamRequestHandler): def handle(self): - unpacker = msgpack.Unpacker(encoding='utf-8') + unpacker = salt.utils.msgpack.Unpacker(encoding='utf-8') while not self.server.shutting_down.is_set(): try: wire_bytes = self.request.recv(1024) @@ -253,16 +254,6 @@ def __enter__(self): finally: self.post_setup_minions() - def start_daemon(self, cls, opts, start_fun): - def start(cls, opts, start_fun): - salt.utils.process.appendproctitle('{0}-{1}'.format(self.__class__.__name__, cls.__name__)) - daemon = cls(opts) - getattr(daemon, start_fun)() - process = multiprocessing.Process(target=start, - args=(cls, opts, start_fun)) - process.start() - return process - def start_zeromq_daemons(self): ''' Fire up the daemons used for zeromq tests @@ -285,6 +276,7 @@ def start_zeromq_daemons(self): daemon_class=SaltMaster, bin_dir_path=SCRIPT_DIR, fail_hard=True, + event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR, start_timeout=120) sys.stdout.write( '\r{0}\r'.format( @@ -322,6 +314,7 @@ def start_zeromq_daemons(self): daemon_class=SaltMinion, bin_dir_path=SCRIPT_DIR, fail_hard=True, + event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR, start_timeout=120) sys.stdout.write( '\r{0}\r'.format( @@ -359,6 +352,7 @@ def start_zeromq_daemons(self): daemon_class=SaltMinion, bin_dir_path=SCRIPT_DIR, fail_hard=True, + event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR, start_timeout=120) sys.stdout.write( '\r{0}\r'.format( @@ -397,6 +391,7 @@ def start_zeromq_daemons(self): daemon_class=SaltMaster, bin_dir_path=SCRIPT_DIR, fail_hard=True, + event_listener_config_dir=RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, start_timeout=120) sys.stdout.write( '\r{0}\r'.format( @@ -434,6 +429,7 @@ def start_zeromq_daemons(self): daemon_class=SaltSyndic, bin_dir_path=SCRIPT_DIR, fail_hard=True, + event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR, start_timeout=120) sys.stdout.write( '\r{0}\r'.format( @@ -657,7 +653,7 @@ def prep_ssh(self): else: os.environ['SSH_DAEMON_RUNNING'] = 'True' self.prep_syndic() - with salt.utils.fopen(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'roster'), 'a') as roster: + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'roster'), 'a') as roster: roster.write(' user: {0}\n'.format(RUNTIME_VARS.RUNNING_TESTS_USER)) roster.write(' priv: {0}/{1}'.format(RUNTIME_VARS.TMP_CONF_DIR, 'key_test')) sys.stdout.write( @@ -703,14 +699,15 @@ def client(self): @classmethod def transplant_configs(cls, transport='zeromq'): - if os.path.isdir(RUNTIME_VARS.TMP_CONF_DIR): - shutil.rmtree(RUNTIME_VARS.TMP_CONF_DIR) + if os.path.isdir(RUNTIME_VARS.TMP): + shutil.rmtree(RUNTIME_VARS.TMP) + os.makedirs(RUNTIME_VARS.TMP) + os.makedirs(RUNTIME_VARS.TMP_ROOT_DIR) os.makedirs(RUNTIME_VARS.TMP_CONF_DIR) os.makedirs(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR) os.makedirs(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR) os.makedirs(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR) - if not os.path.exists(RUNTIME_VARS.TMP): - os.makedirs(RUNTIME_VARS.TMP) + print(' * Transplanting configuration files to \'{0}\''.format(RUNTIME_VARS.TMP_CONF_DIR)) tests_known_hosts_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'salt_ssh_known_hosts') with salt.utils.files.fopen(tests_known_hosts_file, 'w') as known_hosts: @@ -719,12 +716,15 @@ def transplant_configs(cls, transport='zeromq'): # This master connects to syndic_master via a syndic master_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'master')) master_opts['known_hosts_file'] = tests_known_hosts_file - master_opts['cachedir'] = os.path.join(TMP, 'rootdir', 'cache') + master_opts['cachedir'] = 'cache' master_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - master_opts['config_dir'] = RUNTIME_VARS.TMP_CONF_DIR - master_opts['root_dir'] = os.path.join(TMP, 'rootdir') - master_opts['pki_dir'] = os.path.join(TMP, 'rootdir', 'pki', 'master') + master_opts['root_dir'] = os.path.join(TMP_ROOT_DIR) + master_opts['pki_dir'] = 'pki' master_opts['syndic_master'] = 'localhost' + pytest_stop_sending_events_file = os.path.join(TMP_ROOT_DIR, 'pytest_stop_sending_events_file_master') + with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: + wfh.write('') + master_opts['pytest_stop_sending_events_file'] = pytest_stop_sending_events_file file_tree = { 'root_dir': os.path.join(FILES, 'pillar', 'base', 'file_tree'), 'follow_dir_links': False, @@ -771,47 +771,51 @@ def transplant_configs(cls, transport='zeromq'): # This minion connects to master minion_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'minion')) - minion_opts['cachedir'] = os.path.join(TMP, 'rootdir', 'cache') + minion_opts['cachedir'] = 'cache' minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - minion_opts['config_dir'] = RUNTIME_VARS.TMP_CONF_DIR - minion_opts['root_dir'] = os.path.join(TMP, 'rootdir') - minion_opts['pki_dir'] = os.path.join(TMP, 'rootdir', 'pki') - minion_opts['hosts.file'] = os.path.join(TMP, 'rootdir', 'hosts') - minion_opts['aliases.file'] = os.path.join(TMP, 'rootdir', 'aliases') + minion_opts['root_dir'] = os.path.join(TMP_ROOT_DIR) + minion_opts['pki_dir'] = 'pki' + minion_opts['hosts.file'] = os.path.join(TMP_ROOT_DIR, 'hosts') + minion_opts['aliases.file'] = os.path.join(TMP_ROOT_DIR, 'aliases') if virtualenv_binary: minion_opts['venv_bin'] = virtualenv_binary # This sub_minion also connects to master sub_minion_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'sub_minion')) - sub_minion_opts['cachedir'] = os.path.join(TMP, 'rootdir-sub-minion', 'cache') + sub_minion_opts['cachedir'] = 'cache' sub_minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - sub_minion_opts['config_dir'] = RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR sub_minion_opts['root_dir'] = os.path.join(TMP, 'rootdir-sub-minion') - sub_minion_opts['pki_dir'] = os.path.join(TMP, 'rootdir-sub-minion', 'pki', 'minion') - sub_minion_opts['hosts.file'] = os.path.join(TMP, 'rootdir', 'hosts') - sub_minion_opts['aliases.file'] = os.path.join(TMP, 'rootdir', 'aliases') + sub_minion_opts['pki_dir'] = 'pki' + sub_minion_opts['hosts.file'] = os.path.join(TMP_ROOT_DIR, 'hosts') + sub_minion_opts['aliases.file'] = os.path.join(TMP_ROOT_DIR, 'aliases') if virtualenv_binary: sub_minion_opts['venv_bin'] = virtualenv_binary # This is the master of masters syndic_master_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic_master')) - syndic_master_opts['cachedir'] = os.path.join(TMP, 'rootdir-syndic-master', 'cache') + syndic_master_opts['cachedir'] = 'cache' syndic_master_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - syndic_master_opts['config_dir'] = RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR syndic_master_opts['root_dir'] = os.path.join(TMP, 'rootdir-syndic-master') - syndic_master_opts['pki_dir'] = os.path.join(TMP, 'rootdir-syndic-master', 'pki', 'master') + syndic_master_opts['pki_dir'] = 'pki' + pytest_stop_sending_events_file = os.path.join(TMP_ROOT_DIR, 'pytest_stop_sending_events_file_syndic_master') + with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: + wfh.write('') + syndic_master_opts['pytest_stop_sending_events_file'] = pytest_stop_sending_events_file + + # This is the syndic for master + # Let's start with a copy of the syndic master configuration + syndic_opts = copy.deepcopy(syndic_master_opts) + # Let's update with the syndic configuration + syndic_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic'))) + syndic_opts['cachedir'] = 'cache' + syndic_opts['root_dir'] = os.path.join(TMP_ROOT_DIR) # This proxy connects to master proxy_opts = salt.config._read_conf_file(os.path.join(CONF_DIR, 'proxy')) - proxy_opts['cachedir'] = os.path.join(TMP, 'rootdir-proxy', 'cache') - if not os.path.exists(proxy_opts['cachedir']): - os.makedirs(proxy_opts['cachedir']) + proxy_opts['cachedir'] = 'cache' # proxy_opts['user'] = running_tests_user - proxy_opts['config_dir'] = RUNTIME_VARS.TMP_CONF_DIR proxy_opts['root_dir'] = os.path.join(TMP, 'rootdir-proxy') - proxy_opts['pki_dir'] = os.path.join(TMP, 'rootdir-proxy', 'pki') - if not os.path.exists(proxy_opts['pki_dir']): - os.makedirs(proxy_opts['pki_dir']) + proxy_opts['pki_dir'] = 'pki' proxy_opts['hosts.file'] = os.path.join(TMP, 'rootdir-proxy', 'hosts') proxy_opts['aliases.file'] = os.path.join(TMP, 'rootdir-proxy', 'aliases') diff --git a/tests/integration/cloud/clouds/test_digitalocean.py b/tests/integration/cloud/clouds/test_digitalocean.py index 688596f2b7f8..f2a243cef7ca 100644 --- a/tests/integration/cloud/clouds/test_digitalocean.py +++ b/tests/integration/cloud/clouds/test_digitalocean.py @@ -7,13 +7,14 @@ from __future__ import absolute_import, print_function, unicode_literals import base64 import hashlib -from Crypto.PublicKey import RSA # Import Salt Testing Libs from tests.integration.cloud.helpers.cloud_test_base import CloudTest, TIMEOUT # Import Salt Libs +import salt.ext.six as six from salt.ext.six.moves import range +import salt.crypt import salt.utils.stringutils @@ -61,8 +62,17 @@ def test_key_management(self): do_key_name = self.instance_name + '-key' # generate key and fingerprint - ssh_key = RSA.generate(4096) - pub = salt.utils.stringutils.to_str(ssh_key.publickey().exportKey("OpenSSH")) + if salt.crypt.HAS_M2: + rsa_key = salt.crypt.RSA.gen_key(4096, 65537, lambda: None) + pub = six.b('ssh-rsa {}'.format( + base64.b64encode( + six.b('\x00\x00\x00\x07ssh-rsa{}{}'.format(*rsa_key.pub())) + ) + )) + else: + ssh_key = salt.crypt.RSA.generate(4096) + pub = ssh_key.publickey().exportKey("OpenSSH") + pub = salt.utils.stringutils.to_str(pub) key_hex = hashlib.md5(base64.b64decode(pub.strip().split()[1].encode())).hexdigest() finger_print = ':'.join([key_hex[x:x+2] for x in range(0, len(key_hex), 2)]) diff --git a/tests/integration/cloud/clouds/test_ec2.py b/tests/integration/cloud/clouds/test_ec2.py index 35196d1f7d53..c7b81f31b40e 100644 --- a/tests/integration/cloud/clouds/test_ec2.py +++ b/tests/integration/cloud/clouds/test_ec2.py @@ -14,8 +14,7 @@ import salt.utils.yaml # Import Salt Testing Libs -from tests.support.paths import FILES -from tests.support.helpers import expensiveTest +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import skipIf from tests.support import win_installer @@ -38,14 +37,14 @@ class EC2Test(CloudTest): def __fetch_installer(): # Determine the downloaded installer name by searching the files # directory for the first file that looks like an installer. - for path, dirs, files in os.walk(FILES): + for path, dirs, files in os.walk(RUNTIME_VARS.FILES): for file in files: if file.startswith(win_installer.PREFIX): return file # If the installer wasn't found in the previous steps, download the latest Windows installer executable name = win_installer.latest_installer_name() - path = os.path.join(FILES, name) + path = os.path.join(RUNTIME_VARS.FILES, name) with salt.utils.files.fopen(path, 'wb') as fp: win_installer.download_and_verify(fp, name) return name @@ -59,7 +58,6 @@ def installer(self): self._installer = self.__fetch_installer() return self._installer - @expensiveTest def setUp(self): ''' Sets up the test requirements @@ -87,7 +85,7 @@ def copy_file(self, name): configuration directory. The path to the file which is created will be returned. ''' - src = os.path.join(FILES, name) + src = os.path.join(RUNTIME_VARS.FILES, name) dst = os.path.join(self.config_dir, name) with salt.utils.files.fopen(src, 'rb') as sfp: with salt.utils.files.fopen(dst, 'wb') as dfp: diff --git a/tests/integration/cloud/clouds/test_linode.py b/tests/integration/cloud/clouds/test_linode.py index 4912c029e8ed..f1483aa43fd1 100644 --- a/tests/integration/cloud/clouds/test_linode.py +++ b/tests/integration/cloud/clouds/test_linode.py @@ -6,6 +6,7 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + # Create the cloud instance name to be used throughout the tests from tests.integration.cloud.helpers.cloud_test_base import CloudTest, TIMEOUT diff --git a/tests/integration/cloud/clouds/test_virtualbox.py b/tests/integration/cloud/clouds/test_virtualbox.py index c0f12269fbc7..1cd7c5966fdc 100644 --- a/tests/integration/cloud/clouds/test_virtualbox.py +++ b/tests/integration/cloud/clouds/test_virtualbox.py @@ -9,8 +9,8 @@ import socket # Import Salt Testing Libs -import tests.integration as integration from tests.support.unit import TestCase, skipIf +from tests.support.runtests import RUNTIME_VARS from tests.integration.cloud.helpers.virtualbox import (VirtualboxTestCase, VirtualboxCloudTestCase, CONFIG_NAME, @@ -87,7 +87,7 @@ def setUp(self): # check if personal access token, ssh_key_file, and ssh_key_names are present config_path = os.path.join( - integration.FILES, + RUNTIME_VARS.FILES, 'conf', 'cloud.providers.d', PROVIDER_NAME + '.conf' @@ -96,7 +96,7 @@ def setUp(self): providers = cloud_providers_config(config_path) log.debug("config: %s", providers) config_path = os.path.join( - integration.FILES, + RUNTIME_VARS.FILES, 'conf', 'cloud.profiles.d', PROVIDER_NAME + '.conf' @@ -251,7 +251,7 @@ def setUp(self): # check if personal access token, ssh_key_file, and ssh_key_names are present config_path = os.path.join( - integration.FILES, + RUNTIME_VARS.FILES, 'conf', 'cloud.providers.d', PROVIDER_NAME + '.conf' @@ -260,7 +260,7 @@ def setUp(self): providers = cloud_providers_config(config_path) log.debug("config: %s", providers) config_path = os.path.join( - integration.FILES, + RUNTIME_VARS.FILES, 'conf', 'cloud.profiles.d', PROVIDER_NAME + '.conf' diff --git a/tests/integration/cloud/clouds/test_vultrpy.py b/tests/integration/cloud/clouds/test_vultrpy.py index f0dc58e309e7..c91281440388 100644 --- a/tests/integration/cloud/clouds/test_vultrpy.py +++ b/tests/integration/cloud/clouds/test_vultrpy.py @@ -45,10 +45,7 @@ def test_list_sizes(self): Tests the return of running the --list-sizes command for Vultr ''' size_list = self.run_cloud('--list-sizes {0}'.format(self.PROVIDER)) - self.assertIn( - '32768 MB RAM,4x110 GB SSD,40.00 TB BW', - [i.strip() for i in size_list] - ) + self.assertIn('2048 MB RAM,64 GB SSD,2.00 TB BW', [i.strip() for i in size_list]) # Commented for now, Vultr driver does not yet support key management # def test_key_management(self): diff --git a/tests/integration/cloud/helpers/cloud_test_base.py b/tests/integration/cloud/helpers/cloud_test_base.py index 0958fff78b69..9fd3b18da12a 100644 --- a/tests/integration/cloud/helpers/cloud_test_base.py +++ b/tests/integration/cloud/helpers/cloud_test_base.py @@ -26,6 +26,7 @@ log = logging.getLogger(__name__) +@expensiveTest class CloudTest(ShellCase): PROVIDER = '' REQUIRED_PROVIDER_CONFIG_ITEMS = tuple() @@ -178,7 +179,6 @@ def config(self): def profile_str(self): return self.PROVIDER + '-config' - @expensiveTest def setUp(self): ''' Sets up the test requirements. In child classes, define PROVIDER and REQUIRED_CONFIG_ITEMS or this will fail diff --git a/tests/integration/cloud/helpers/virtualbox.py b/tests/integration/cloud/helpers/virtualbox.py index a4a6694408ea..e7900e0a9509 100644 --- a/tests/integration/cloud/helpers/virtualbox.py +++ b/tests/integration/cloud/helpers/virtualbox.py @@ -9,7 +9,7 @@ import tests.integration.cloud.helpers from tests.support.case import ShellCase from tests.support.unit import TestCase, skipIf -from tests.support.paths import FILES +from tests.support.runtests import RUNTIME_VARS # Import Salt libs from salt.ext import six @@ -57,7 +57,7 @@ def run_cloud(self, arg_str, catch_stderr=False, timeout=None): @return: @rtype: dict """ - config_path = os.path.join(FILES, 'conf') + config_path = os.path.join(RUNTIME_VARS.FILES, 'conf') arg_str = '--out=json -c {0} {1}'.format(config_path, arg_str) # arg_str = "{0} --log-level=error".format(arg_str) log.debug("running salt-cloud with %s", arg_str) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py new file mode 100644 index 000000000000..ff53d8d7601f --- /dev/null +++ b/tests/integration/conftest.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +''' + tests.integration.conftest + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Integration tests PyTest configuration/fixtures +''' +# pylint: disable=unused-argument,redefined-outer-name + +# Import Python libs +from __future__ import absolute_import, unicode_literals +import logging +from collections import OrderedDict + +# Import 3rd-party libs +import psutil +import pytest + +log = logging.getLogger(__name__) + + +@pytest.fixture(scope='package', autouse=True) +def default_session_daemons(request, + log_server, + session_salt_master, + session_salt_minion, + session_secondary_salt_minion, + ): + + request.session.stats_processes.update(OrderedDict(( + ('Salt Master', psutil.Process(session_salt_master.pid)), + ('Salt Minion', psutil.Process(session_salt_minion.pid)), + ('Salt Sub Minion', psutil.Process(session_secondary_salt_minion.pid)), + )).items()) + + # Run tests + yield + + # Stop daemons now(they would be stopped at the end of the test run session + for daemon in (session_secondary_salt_minion, session_salt_minion, session_salt_master): + try: + daemon.terminate() + except Exception as exc: # pylint: disable=broad-except + log.warning('Failed to terminate daemon: %s', daemon.__class__.__name__) diff --git a/tests/integration/daemons/test_masterapi.py b/tests/integration/daemons/test_masterapi.py index a2a0ff84a69a..ca1638ab80fc 100644 --- a/tests/integration/daemons/test_masterapi.py +++ b/tests/integration/daemons/test_masterapi.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -tests/integration/daemons/test_masterapi.py:71*- coding: utf-8 -*- # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -7,8 +7,8 @@ import stat # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ShellCase -from tests.support.paths import TMP, INTEGRATION_TEST_DIR # Import 3rd-party libs @@ -16,10 +16,6 @@ import salt.utils.files import salt.utils.stringutils -# all read, only owner write -autosign_file_permissions = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR -autosign_file_path = os.path.join(TMP, 'rootdir', 'autosign_file') - class AutosignGrainsTest(ShellCase): ''' @@ -27,11 +23,17 @@ class AutosignGrainsTest(ShellCase): ''' def setUp(self): + # all read, only owner write + self.autosign_file_permissions = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + if RUNTIME_VARS.PYTEST_SESSION: + self.autosign_file_path = os.path.join(RUNTIME_VARS.TMP, 'autosign_file') + else: + self.autosign_file_path = os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'autosign_file') shutil.copyfile( - os.path.join(INTEGRATION_TEST_DIR, 'files', 'autosign_grains', 'autosign_file'), - autosign_file_path + os.path.join(RUNTIME_VARS.FILES, 'autosign_grains', 'autosign_file'), + self.autosign_file_path ) - os.chmod(autosign_file_path, autosign_file_permissions) + os.chmod(self.autosign_file_path, self.autosign_file_permissions) self.run_key('-d minion -y') self.run_call('test.ping -l quiet') # get minon to try to authenticate itself again @@ -49,21 +51,24 @@ def setUp(self): def tearDown(self): shutil.copyfile( - os.path.join(INTEGRATION_TEST_DIR, 'files', 'autosign_file'), - autosign_file_path + os.path.join(RUNTIME_VARS.FILES, 'autosign_file'), + self.autosign_file_path ) - os.chmod(autosign_file_path, autosign_file_permissions) + os.chmod(self.autosign_file_path, self.autosign_file_permissions) self.run_call('test.ping -l quiet') # get minon to authenticate itself again - if os.path.isdir(self.autosign_grains_dir): - shutil.rmtree(self.autosign_grains_dir) + try: + if os.path.isdir(self.autosign_grains_dir): + shutil.rmtree(self.autosign_grains_dir) + except AttributeError: + pass def test_autosign_grains_accept(self): grain_file_path = os.path.join(self.autosign_grains_dir, 'test_grain') with salt.utils.files.fopen(grain_file_path, 'w') as f: f.write(salt.utils.stringutils.to_str('#invalid_value\ncheese')) - os.chmod(grain_file_path, autosign_file_permissions) + os.chmod(grain_file_path, self.autosign_file_permissions) self.run_call('test.ping -l quiet') # get minon to try to authenticate itself again self.assertIn('minion', self.run_key('-l acc')) @@ -72,7 +77,7 @@ def test_autosign_grains_fail(self): grain_file_path = os.path.join(self.autosign_grains_dir, 'test_grain') with salt.utils.files.fopen(grain_file_path, 'w') as f: f.write(salt.utils.stringutils.to_str('#cheese\ninvalid_value')) - os.chmod(grain_file_path, autosign_file_permissions) + os.chmod(grain_file_path, self.autosign_file_permissions) self.run_call('test.ping -l quiet') # get minon to try to authenticate itself again self.assertNotIn('minion', self.run_key('-l acc')) diff --git a/tests/integration/doc/test_man.py b/tests/integration/doc/test_man.py index 15ee43124d9a..1803bb15121f 100644 --- a/tests/integration/doc/test_man.py +++ b/tests/integration/doc/test_man.py @@ -12,77 +12,85 @@ import salt.utils.platform # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import TMP from tests.support.unit import skipIf @skipIf(salt.utils.platform.is_windows(), 'minion is windows') +@skipIf(salt.utils.platform.is_aix(), 'minion is AIX') class ManTest(ModuleCase): - # Map filenames to search strings which should be in the manpage - manpages = { - 'salt-cp.1': [ - 'salt-cp Documentation', - 'copies files from the master', - ], - 'salt-cloud.1': [ - 'Salt Cloud Command', - 'Provision virtual machines in the cloud', - ], - 'salt-call.1': [ - 'salt-call Documentation', - 'run module functions locally', - ], - 'salt-api.1': [ - 'salt-api Command', - 'Start interfaces used to remotely connect', - ], - 'salt-unity.1': [ - 'salt-unity Command', - 'unified invocation wrapper', - ], - 'salt-syndic.1': [ - 'salt-syndic Documentation', - 'Salt syndic daemon', - ], - 'salt-ssh.1': [ - 'salt-ssh Documentation', - 'executed using only SSH', - ], - 'salt-run.1': [ - 'salt-run Documentation', - 'frontend command for executing', - ], - 'salt-proxy.1': [ - 'salt-proxy Documentation', - 'proxies these commands', - ], - 'salt-minion.1': [ - 'salt-minion Documentation', - 'Salt minion daemon', - ], - 'salt-master.1': [ - 'salt-master Documentation', - 'Salt master daemon', - ], - 'salt-key.1': [ - 'salt-key Documentation', - 'management of Salt server public keys', - ], - 'salt.1': [ - 'allows for commands to be executed', - ], - 'salt.7': [ - 'Salt Documentation', - ], - 'spm.1': [ - 'Salt Package Manager Command', - 'command for managing Salt packages', - ], - } + + @classmethod + def setUpClass(cls): + cls.rootdir = os.path.join(RUNTIME_VARS.TMP, 'mantest') + # Map filenames to search strings which should be in the manpage + cls.manpages = { + 'salt-cp.1': [ + 'salt-cp Documentation', + 'copies files from the master', + ], + 'salt-cloud.1': [ + 'Salt Cloud Command', + 'Provision virtual machines in the cloud', + ], + 'salt-call.1': [ + 'salt-call Documentation', + 'run module functions locally', + ], + 'salt-api.1': [ + 'salt-api Command', + 'Start interfaces used to remotely connect', + ], + 'salt-unity.1': [ + 'salt-unity Command', + 'unified invocation wrapper', + ], + 'salt-syndic.1': [ + 'salt-syndic Documentation', + 'Salt syndic daemon', + ], + 'salt-ssh.1': [ + 'salt-ssh Documentation', + 'executed using only SSH', + ], + 'salt-run.1': [ + 'salt-run Documentation', + 'frontend command for executing', + ], + 'salt-proxy.1': [ + 'salt-proxy Documentation', + 'proxies these commands', + ], + 'salt-minion.1': [ + 'salt-minion Documentation', + 'Salt minion daemon', + ], + 'salt-master.1': [ + 'salt-master Documentation', + 'Salt master daemon', + ], + 'salt-key.1': [ + 'salt-key Documentation', + 'management of Salt server public keys', + ], + 'salt.1': [ + 'allows for commands to be executed', + ], + 'salt.7': [ + 'Salt Documentation', + ], + 'spm.1': [ + 'Salt Package Manager Command', + 'command for managing Salt packages', + ], + } + + @classmethod + def tearDownClass(cls): + cls.manpages = None def setUp(self): - self.rootdir = os.path.join(TMP, 'mantest') self.addCleanup(shutil.rmtree, self.rootdir, ignore_errors=True) if not os.path.exists(self.rootdir): ret = self.run_function('mantest.install', [self.rootdir]) diff --git a/tests/integration/executors/__init__.py b/tests/integration/executors/__init__.py new file mode 100644 index 000000000000..40a96afc6ff0 --- /dev/null +++ b/tests/integration/executors/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/tests/integration/files/conf/mm_master b/tests/integration/files/conf/mm_master index e69de29bb2d1..57bf9dad7249 100644 --- a/tests/integration/files/conf/mm_master +++ b/tests/integration/files/conf/mm_master @@ -0,0 +1 @@ +id: mm-master diff --git a/tests/integration/files/conf/mm_minion b/tests/integration/files/conf/mm_minion index 058b407dd1ef..76e0cb944292 100644 --- a/tests/integration/files/conf/mm_minion +++ b/tests/integration/files/conf/mm_minion @@ -1,3 +1,4 @@ +id: mm-minion master: - localhost:64506 - localhost:64508 diff --git a/tests/integration/files/conf/mm_sub_master b/tests/integration/files/conf/mm_sub_master index 7f8e0d60cfdb..fc4212edaa0e 100644 --- a/tests/integration/files/conf/mm_sub_master +++ b/tests/integration/files/conf/mm_sub_master @@ -1,4 +1,4 @@ -id: sub_master +id: mm-sub-master publish_port: 64507 ret_port: 64508 diff --git a/tests/integration/files/conf/mm_sub_minion b/tests/integration/files/conf/mm_sub_minion index 058b407dd1ef..baf38fc51825 100644 --- a/tests/integration/files/conf/mm_sub_minion +++ b/tests/integration/files/conf/mm_sub_minion @@ -1,3 +1,4 @@ +id: mm-sub-minion master: - localhost:64506 - localhost:64508 diff --git a/tests/integration/files/conf/syndic b/tests/integration/files/conf/syndic index 5746d493c9aa..3d465162e4c8 100644 --- a/tests/integration/files/conf/syndic +++ b/tests/integration/files/conf/syndic @@ -3,7 +3,8 @@ id: syndic interface: 127.0.0.1 syndic_master: localhost syndic_master_port: 54506 -syndic_log_file: syndic.log -syndic_pidfile: syndic.pid +syndic_log_file: logs/syndic.log +syndic_pidfile: run/syndic.pid tcp_pub_port: 64510 tcp_pull_port: 64511 +sock_dir: syndic_sock diff --git a/tests/integration/files/engines/runtests_engine.py b/tests/integration/files/engines/runtests_engine.py index 68f22ba561e8..55b80f7f7dd0 100644 --- a/tests/integration/files/engines/runtests_engine.py +++ b/tests/integration/files/engines/runtests_engine.py @@ -14,6 +14,7 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals +import os import sys import errno import socket @@ -49,6 +50,7 @@ class PyTestEngine(object): def __init__(self, opts): self.opts = opts self.sock = None + self.stop_sending_events_file = opts.get('pytest_stop_sending_events_file') def start(self): self.io_loop = ioloop.IOLoop() @@ -58,9 +60,9 @@ def start(self): @gen.coroutine def _start(self): - self.io_loop.spawn_callback(self.fire_master_started_event) port = int(self.opts['runtests_conn_check_port']) - log.info('Starting Pytest Engine(role=%s) on port %s', self.opts['__role'], port) + log.info('Starting Pytest Engine(role=%s, id=%s) on port %s', self.opts['__role'], self.opts['id'], port) + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.setblocking(0) @@ -74,6 +76,9 @@ def _start(self): self.handle_connection, ) + if self.opts['__role'] == 'master': + yield self.fire_master_started_event() + def handle_connection(self, connection, address): log.warning('Accepted connection from %s. Role: %s', address, self.opts['__role']) # We just need to know that the daemon running the engine is alive... @@ -92,19 +97,23 @@ def handle_connection(self, connection, address): @gen.coroutine def fire_master_started_event(self): - log.info('Firing salt-master started event...') - event_bus = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) - master_start_event_tag = 'salt/master/{0}/start'.format(self.opts['id']) - load = {'id': self.opts['id'], 'tag': master_start_event_tag, 'data': {}} + log.info('Firing salt-%s started event...', self.opts['__role']) + start_event_tag = 'salt/{}/{}/start'.format(self.opts['__role'], self.opts['id']) + log.info('Firing salt-%s started event. Tag: %s', self.opts['__role'], start_event_tag) + load = {'id': self.opts['id'], 'tag': start_event_tag, 'data': {}} # One minute should be more than enough to fire these events every second in order # for pytest-salt to pickup that the master is running - timeout = 60 - while True: - timeout -= 1 - try: - event_bus.fire_event(load, master_start_event_tag, timeout=500) - if timeout <= 0: + with salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) as event_bus: + timeout = 30 + while True: + if self.stop_sending_events_file and not os.path.exists(self.stop_sending_events_file): + log.info('The stop sending events file "marker" is done. Stop sending events...') + break + timeout -= 1 + try: + event_bus.fire_event(load, start_event_tag, timeout=500) + if timeout <= 0: + break + yield gen.sleep(1) + except iostream.StreamClosedError: break - yield gen.sleep(1) - except iostream.StreamClosedError: - break diff --git a/tests/integration/files/file/base/_executors/arg.py b/tests/integration/files/file/base/_executors/arg.py new file mode 100644 index 000000000000..dfe09cb7b834 --- /dev/null +++ b/tests/integration/files/file/base/_executors/arg.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- + + +def __virtual__(): + return True + + +def execute(*args, **kwargs): + # we use the dunder to assert the loader is provided minionmods + return __salt__['test.arg']('test.arg fired') diff --git a/tests/integration/files/file/base/_modules/mantest.py b/tests/integration/files/file/base/_modules/mantest.py index ee0acb9fcc3b..16a9d5e5d2ab 100644 --- a/tests/integration/files/file/base/_modules/mantest.py +++ b/tests/integration/files/file/base/_modules/mantest.py @@ -15,7 +15,7 @@ from salt.exceptions import CommandExecutionError # Import Salt Tesing libs -from tests.support.paths import CODE_DIR +from tests.support.runtests import RUNTIME_VARS log = logging.getLogger(__name__) @@ -26,7 +26,7 @@ def install(rootdir): return __salt__['cmd.run_all']( [ sys.executable, - os.path.join(CODE_DIR, 'setup.py'), + os.path.join(RUNTIME_VARS.CODE_DIR, 'setup.py'), 'install', '--root={0}'.format(rootdir) ], redirect_stderr=True diff --git a/tests/integration/files/file/base/_modules/runtests_decorators.py b/tests/integration/files/file/base/_modules/runtests_decorators.py index ff316bf8a195..c0cd4106c629 100644 --- a/tests/integration/files/file/base/_modules/runtests_decorators.py +++ b/tests/integration/files/file/base/_modules/runtests_decorators.py @@ -7,10 +7,10 @@ # Import Salt libs import salt.utils.decorators -from tests.support.paths import BASE_FILES +from tests.support.runtests import RUNTIME_VARS -EXIT_CODE_SH = os.path.join(BASE_FILES, 'exit_code.sh') -EXIT_CODE_CMD = os.path.join(BASE_FILES, 'exit_code.cmd') +EXIT_CODE_SH = os.path.join(RUNTIME_VARS.BASE_FILES, 'exit_code.sh') +EXIT_CODE_CMD = os.path.join(RUNTIME_VARS.BASE_FILES, 'exit_code.cmd') def _exit_code(code): diff --git a/tests/integration/files/file/base/_modules/runtests_helpers.py b/tests/integration/files/file/base/_modules/runtests_helpers.py index de252a68e438..22dc6e2e9ff3 100644 --- a/tests/integration/files/file/base/_modules/runtests_helpers.py +++ b/tests/integration/files/file/base/_modules/runtests_helpers.py @@ -9,7 +9,6 @@ # Import python libs from __future__ import absolute_import -import fnmatch import os import re import sys @@ -22,26 +21,35 @@ # Import 3rd-party libs from salt.ext import six -SYS_TMP_DIR = os.path.realpath( - # Avoid ${TMPDIR} and gettempdir() on MacOS as they yield a base path too long - # for unix sockets: ``error: AF_UNIX path too long`` - # Gentoo Portage prefers ebuild tests are rooted in ${TMPDIR} - os.environ.get('TMPDIR', tempfile.gettempdir()) if not salt.utils.platform.is_darwin() else '/tmp' -) -# This tempdir path is defined on tests.integration.__init__ -TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir') +# Import tests libs +try: + from tests.support.runtests import RUNTIME_VARS +except ImportError: + # Salt SSH Tests + SYS_TMP_DIR = os.path.realpath( + # Avoid ${TMPDIR} and gettempdir() on MacOS as they yield a base path too long + # for unix sockets: ``error: AF_UNIX path too long`` + # Gentoo Portage prefers ebuild tests are rooted in ${TMPDIR} + os.environ.get('TMPDIR', tempfile.gettempdir()) if not salt.utils.platform.is_darwin() else '/tmp' + ) + # This tempdir path is defined on tests.integration.__init__ + TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir') + + class RUNTIME_VARS(object): + TMP = TMP + SYS_TMP_DIR = SYS_TMP_DIR def get_salt_temp_dir(): - return TMP + return RUNTIME_VARS.TMP def get_salt_temp_dir_for_path(*path): - return os.path.join(TMP, *path) + return os.path.join(RUNTIME_VARS.TMP, *path) def get_sys_temp_dir_for_path(*path): - return os.path.join(SYS_TMP_DIR, *path) + return os.path.join(RUNTIME_VARS.SYS_TMP_DIR, *path) def get_invalid_docs(): diff --git a/tests/integration/files/file/base/file-pillardefaultget.sls b/tests/integration/files/file/base/file-pillardefaultget.sls index b2d73ac92955..b0f80236daf6 100644 --- a/tests/integration/files/file/base/file-pillardefaultget.sls +++ b/tests/integration/files/file/base/file-pillardefaultget.sls @@ -1,11 +1,5 @@ -{% if grains['kernel'] == 'Windows' %} - {% set TMP = "C:\\Windows\\Temp\\" %} -{% else %} - {% set TMP = "/tmp/" %} -{% endif %} - {% set file = salt['pillar.get']('pillardoesnotexist', 'defaultvalue') %} create_file: file.managed: - - name: {{ TMP }}filepillar-{{ file }} + - name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('filepillar-' + file) }} diff --git a/tests/integration/files/file/base/file-pillarget.sls b/tests/integration/files/file/base/file-pillarget.sls index 44adf16c801a..a8381ada662e 100644 --- a/tests/integration/files/file/base/file-pillarget.sls +++ b/tests/integration/files/file/base/file-pillarget.sls @@ -1,11 +1,5 @@ -{% if grains['kernel'] == 'Windows' %} - {% set TMP = "C:\\Windows\\Temp\\" %} -{% else %} - {% set TMP = "/tmp/" %} -{% endif %} - {% set file = salt['pillar.get']('monty', '') %} create_file: file.managed: - - name: {{ TMP }}filepillar-{{ file }} + - name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('filepillar-' + file) }} diff --git a/tests/integration/files/file/base/file_contents_pillar.sls b/tests/integration/files/file/base/file_contents_pillar.sls index 06069d147978..2a5ae8baff4a 100644 --- a/tests/integration/files/file/base/file_contents_pillar.sls +++ b/tests/integration/files/file/base/file_contents_pillar.sls @@ -1,10 +1,4 @@ -{% if grains['kernel'] == 'Windows' %} - {% set TMP = "C:\\Windows\\Temp\\" %} -{% else %} - {% set TMP = "/tmp/" %} -{% endif %} - add_contents_pillar_sls: file.managed: - - name: {{ TMP }}test-lists-content-pillars + - name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('test-lists-content-pillars') }} - contents_pillar: companions:three diff --git a/tests/integration/files/file/base/issue-35384.sls b/tests/integration/files/file/base/issue-35384.sls index 82214b7d3e05..3c41617ca8fa 100644 --- a/tests/integration/files/file/base/issue-35384.sls +++ b/tests/integration/files/file/base/issue-35384.sls @@ -2,5 +2,5 @@ cmd_run_unless_multiple: cmd.run: - name: echo "hello" - unless: - - /bin/true - - /bin/false + - "$(which true)" + - "$(which false)" diff --git a/tests/integration/files/file/base/issue-51499.sls b/tests/integration/files/file/base/issue-51499.sls new file mode 100644 index 000000000000..0e15e7396eae --- /dev/null +++ b/tests/integration/files/file/base/issue-51499.sls @@ -0,0 +1,6 @@ +{% if 'nonexistent_module.function' in salt %} +{% do salt.log.warning("Module is available") %} +{% endif %} +always-passes: + test.succeed_without_changes: + - name: foo diff --git a/tests/integration/files/file/base/modules/jinja/defaults.yaml b/tests/integration/files/file/base/modules/jinja/defaults.yaml new file mode 100644 index 000000000000..682bbac38331 --- /dev/null +++ b/tests/integration/files/file/base/modules/jinja/defaults.yaml @@ -0,0 +1,13 @@ +--- +template: + pkg: + name: template + rootgroup: root + config: '/etc/template' + service: + name: template + subcomponent: + config: '/etc/template-subcomponent-formula.conf' + # Just here for testing + added_in_defaults: defaults_value + winner: defaults diff --git a/tests/integration/files/file/base/modules/jinja/map.jinja b/tests/integration/files/file/base/modules/jinja/map.jinja new file mode 100644 index 000000000000..c6a991433e0f --- /dev/null +++ b/tests/integration/files/file/base/modules/jinja/map.jinja @@ -0,0 +1,49 @@ +{%- set tplroot = tpldir.split('/')[0] %} + +{%- import_yaml tplroot ~ "/defaults.yaml" as default_settings %} +{%- import_json tplroot ~ "/osarchmap.json" as osarchmap %} +{%- import_yaml tplroot ~ "/osfamilymap.yaml" as osfamilymap %} +{%- import_yaml tplroot ~ "/osmap.yaml" as osmap %} +{%- import_yaml tplroot ~ "/osfingermap.yaml" as osfingermap %} + +{%- set _config = salt['config.get'](tplroot, default={}) %} + +{%- set defaults = salt['grains.filter_by']( + default_settings, + default='template', + merge=salt['grains.filter_by']( + osarchmap, + grain='osarch', + merge=salt['grains.filter_by']( + osfamilymap, + grain='os_family', + merge=salt['grains.filter_by']( + osmap, + grain='os', + merge=salt['grains.filter_by']( + osfingermap, + grain='osfinger', + merge=salt['grains.filter_by']( + _config, + default='lookup' + ) + ) + ) + ) + ) + ) +%} + +{%- set config = salt['grains.filter_by']( + {'defaults': defaults}, + default='defaults', + merge=_config + ) +%} + +{%- set template = config %} + +{%- if grains.os == 'MacOS' %} +{%- set macos_group = salt['cmd.run']("stat -f '%Sg' /dev/console") %} +{%- do template.update({'rootgroup': macos_group}) %} +{%- endif %} diff --git a/tests/integration/files/file/base/modules/jinja/osarchmap.json b/tests/integration/files/file/base/modules/jinja/osarchmap.json new file mode 100644 index 000000000000..c31720a5510e --- /dev/null +++ b/tests/integration/files/file/base/modules/jinja/osarchmap.json @@ -0,0 +1,26 @@ +{ + "386": { + "arch": 386 + }, + "amd64": { + "arch": "amd64" + }, + "x86_64": { + "arch": "amd64" + }, + "arm64": { + "arch": "arm64" + }, + "armv6l": { + "arch": "armv6l" + }, + "armv7l": { + "arch": "armv7l" + }, + "ppc64le": { + "arch": "ppc64le" + }, + "s390x": { + "arch": "s390x" + } +} diff --git a/tests/integration/files/file/base/modules/jinja/osfamilymap.yaml b/tests/integration/files/file/base/modules/jinja/osfamilymap.yaml new file mode 100644 index 000000000000..1119a7fd34cd --- /dev/null +++ b/tests/integration/files/file/base/modules/jinja/osfamilymap.yaml @@ -0,0 +1,36 @@ +--- +Debian: + pkg: + name: template-debian + config: /etc/template.d/custom.conf + +RedHat: + pkg: + name: template-redhat + config: /etc/template.conf + +Suse: + pkg: + name: template-suse + +Gentoo: {} + +Arch: + pkg: + name: template-arch + service: + name: service-arch + +Alpine: {} + +FreeBSD: + rootgroup: wheel + +OpenBSD: + rootgroup: wheel + +Solaris: {} + +Windows: {} + +MacOS: {} diff --git a/tests/integration/files/file/base/modules/jinja/osfingermap.yaml b/tests/integration/files/file/base/modules/jinja/osfingermap.yaml new file mode 100644 index 000000000000..91b5eefca5f3 --- /dev/null +++ b/tests/integration/files/file/base/modules/jinja/osfingermap.yaml @@ -0,0 +1,11 @@ +--- +# os: Ubuntu +Ubuntu-18.04: + config: /etc/template.d/custom-ubuntu-18.04.conf + +# os: CentOS +CentOS-6: + pkg: + name: template-centos-6 + config: /etc/template.d/custom-centos-6.conf +CentOS-7: {} diff --git a/tests/integration/files/file/base/modules/jinja/osmap.yaml b/tests/integration/files/file/base/modules/jinja/osmap.yaml new file mode 100644 index 000000000000..fb929547f4db --- /dev/null +++ b/tests/integration/files/file/base/modules/jinja/osmap.yaml @@ -0,0 +1,29 @@ +--- +# os_family: Debian +Ubuntu: + pkg: + name: template-ubuntu + config: /etc/template.d/custom-ubuntu.conf + +Raspbian: {} + +# os_family: RedHat +Fedora: + pkg: + name: template-fedora + service: + name: service-fedora + +CentOS: {} + +# os_family: Suse +openSUSE: {} + +# os_family: Gentoo +Funtoo: {} + +# os_family: Arch +Manjaro: {} + +# os_family: Solaris +SmartOS: {} diff --git a/tests/integration/files/file/base/nested-orch/inner.sls b/tests/integration/files/file/base/nested-orch/inner.sls index 5b2a501e3ce6..4c03f19a3ce2 100755 --- a/tests/integration/files/file/base/nested-orch/inner.sls +++ b/tests/integration/files/file/base/nested-orch/inner.sls @@ -2,5 +2,5 @@ cmd.run: salt.function: - tgt: minion - arg: - - /bin/false + - "$(which false)" - failhard: True diff --git a/tests/integration/files/file/base/pkgrepo/files/RPM-GPG-KEY-EPEL-8-salttest b/tests/integration/files/file/base/pkgrepo/files/RPM-GPG-KEY-EPEL-8-salttest new file mode 100644 index 000000000000..30b69a6295af --- /dev/null +++ b/tests/integration/files/file/base/pkgrepo/files/RPM-GPG-KEY-EPEL-8-salttest @@ -0,0 +1,28 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFz3zvsBEADJOIIWllGudxnpvJnkxQz2CtoWI7godVnoclrdl83kVjqSQp+2 +dgxuG5mUiADUfYHaRQzxKw8efuQnwxzU9kZ70ngCxtmbQWGmUmfSThiapOz00018 ++eo5MFabd2vdiGo1y+51m2sRDpN8qdCaqXko65cyMuLXrojJHIuvRA/x7iqOrRfy +a8x3OxC4PEgl5pgDnP8pVK0lLYncDEQCN76D9ubhZQWhISF/zJI+e806V71hzfyL +/Mt3mQm/li+lRKU25Usk9dWaf4NH/wZHMIPAkVJ4uD4H/uS49wqWnyiTYGT7hUbi +ecF7crhLCmlRzvJR8mkRP6/4T/F3tNDPWZeDNEDVFUkTFHNU6/h2+O398MNY/fOh +yKaNK3nnE0g6QJ1dOH31lXHARlpFOtWt3VmZU0JnWLeYdvap4Eff9qTWZJhI7Cq0 +Wm8DgLUpXgNlkmquvE7P2W5EAr2E5AqKQoDbfw/GiWdRvHWKeNGMRLnGI3QuoX3U +pAlXD7v13VdZxNydvpeypbf/AfRyrHRKhkUj3cU1pYkM3DNZE77C5JUe6/0nxbt4 +ETUZBTgLgYJGP8c7PbkVnO6I/KgL1jw+7MW6Az8Ox+RXZLyGMVmbW/TMc8haJfKL +MoUo3TVk8nPiUhoOC0/kI7j9ilFrBxBU5dUtF4ITAWc8xnG6jJs/IsvRpQARAQAB +tChGZWRvcmEgRVBFTCAoOCkgPGVwZWxAZmVkb3JhcHJvamVjdC5vcmc+iQI4BBMB +AgAiBQJc9877AhsPBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRAh6kWrL4bW +oWagD/4xnLWws34GByVDQkjprk0fX7Iyhpm/U7BsIHKspHLL+Y46vAAGY/9vMvdE +0fcr9Ek2Zp7zE1RWmSCzzzUgTG6BFoTG1H4Fho/7Z8BXK/jybowXSZfqXnTOfhSF +alwDdwlSJvfYNV9MbyvbxN8qZRU1z7PEWZrIzFDDToFRk0R71zHpnPTNIJ5/YXTw +NqU9OxII8hMQj4ufF11040AJQZ7br3rzerlyBOB+Jd1zSPVrAPpeMyJppWFHSDAI +WK6x+am13VIInXtqB/Cz4GBHLFK5d2/IYspVw47Solj8jiFEtnAq6+1Aq5WH3iB4 +bE2e6z00DSF93frwOyWN7WmPIoc2QsNRJhgfJC+isGQAwwq8xAbHEBeuyMG8GZjz +xohg0H4bOSEujVLTjH1xbAG4DnhWO/1VXLX+LXELycO8ZQTcjj/4AQKuo4wvMPrv +9A169oETG+VwQlNd74VBPGCvhnzwGXNbTK/KH1+WRH0YSb+41flB3NKhMSU6dGI0 +SGtIxDSHhVVNmx2/6XiT9U/znrZsG5Kw8nIbbFz+9MGUUWgJMsd1Zl9R8gz7V9fp +n7L7y5LhJ8HOCMsY/Z7/7HUs+t/A1MI4g7Q5g5UuSZdgi0zxukiWuCkLeAiAP4y7 +zKK4OjJ644NDcWCHa36znwVmkz3ixL8Q0auR15Oqq2BjR/fyog== +=84m8 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/integration/files/file/base/pkgrepo/managed.sls b/tests/integration/files/file/base/pkgrepo/managed.sls index 3684f16b160e..a6cf0f0b3aa7 100644 --- a/tests/integration/files/file/base/pkgrepo/managed.sls +++ b/tests/integration/files/file/base/pkgrepo/managed.sls @@ -1,7 +1,27 @@ {% if grains['os'] == 'CentOS' %} # START CentOS pkgrepo tests -{% if grains['osmajorrelease'] == 7 %} +{% if grains['osmajorrelease'] == 8 %} +epel-salttest: + pkgrepo.managed: + - humanname: Extra Packages for Enterprise Linux 8 - $basearch (salttest) + - comments: + - '#baseurl=http://download.fedoraproject.org/pub/epel/8/$basearch' + - mirrorlist: https://mirrors.fedoraproject.org/metalink?repo=epel-8&arch=$basearch + - failovermethod: priority + - enabled: 1 + - gpgcheck: 1 + - gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-8-salttest + - require: + - file: /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-8-salttest + +/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-8-salttest: + file.managed: + - source: salt://pkgrepo/files/RPM-GPG-KEY-EPEL-8-salttest + - user: root + - group: root + - mode: 644 +{% elif grains['osmajorrelease'] == 7 %} epel-salttest: pkgrepo.managed: - humanname: Extra Packages for Enterprise Linux 7 - $basearch (salttest) diff --git a/tests/integration/files/file/base/requisites/require_any.sls b/tests/integration/files/file/base/requisites/require_any.sls index 59f8ce3d13b6..449e1277a9e2 100644 --- a/tests/integration/files/file/base/requisites/require_any.sls +++ b/tests/integration/files/file/base/requisites/require_any.sls @@ -27,7 +27,7 @@ B: C: cmd.run: - - name: /bin/false + - name: "$(which false)" D: cmd.run: diff --git a/tests/integration/files/file/base/retry/retry_success.sls b/tests/integration/files/file/base/retry/retry_success.sls index 7497d14f1944..f7cddb5f0008 100644 --- a/tests/integration/files/file/base/retry/retry_success.sls +++ b/tests/integration/files/file/base/retry/retry_success.sls @@ -1,10 +1,10 @@ -{{ salt['runtests_helpers.get_salt_temp_dir_for_path']('retry_file') }}: +{{ salt['runtests_helpers.get_salt_temp_dir_for_path']('retry_file_option_success') }}: file: - touch file_test: file.exists: - - name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('retry_file') }} + - name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('retry_file_option_success') }} - retry: until: True attempts: 5 diff --git a/tests/integration/files/file/base/retry/retry_success2.sls b/tests/integration/files/file/base/retry/retry_success2.sls index daa08c5e0085..9fefb2cef461 100644 --- a/tests/integration/files/file/base/retry/retry_success2.sls +++ b/tests/integration/files/file/base/retry/retry_success2.sls @@ -1,7 +1,14 @@ -file_test: +file_test_a: + file.managed: + - name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('retry_file_eventual_success') + '_a' }} + - content: 'a' + +file_test_b: file.exists: - - name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('retry_file') }} + - name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('retry_file_eventual_success') }} - retry: until: True attempts: 20 interval: 5 + - require: + - file_test_a diff --git a/tests/integration/files/log_handlers/runtests_log_handler.py b/tests/integration/files/log_handlers/runtests_log_handler.py index 9b8e82cfb992..da1446ee5143 100644 --- a/tests/integration/files/log_handlers/runtests_log_handler.py +++ b/tests/integration/files/log_handlers/runtests_log_handler.py @@ -19,10 +19,8 @@ import threading from multiprocessing import Queue -# Import 3rd-party libs -import msgpack - # Import Salt libs +import salt.utils.msgpack from salt.ext import six from salt.utils.platform import is_darwin import salt.log.setup @@ -95,7 +93,7 @@ def process_queue(port, queue): break # Just log everything, filtering will happen on the main process # logging handlers - sock.sendall(msgpack.dumps(record.__dict__, encoding='utf-8')) + sock.sendall(salt.utils.msgpack.dumps(record.__dict__, encoding='utf-8')) except (IOError, EOFError, KeyboardInterrupt, SystemExit): try: sock.shutdown(socket.SHUT_RDWR) diff --git a/tests/integration/grains/test_core.py b/tests/integration/grains/test_core.py index 72171b8bdace..b523d58cfb5d 100644 --- a/tests/integration/grains/test_core.py +++ b/tests/integration/grains/test_core.py @@ -59,7 +59,7 @@ class TestGrainsReg(ModuleCase, LoaderModuleMockMixin): ''' def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils(opts, whitelist=['reg']) return { salt.modules.reg: { diff --git a/tests/integration/loader/test_ext_grains.py b/tests/integration/loader/test_ext_grains.py index 758b575aad3d..405013042fee 100644 --- a/tests/integration/loader/test_ext_grains.py +++ b/tests/integration/loader/test_ext_grains.py @@ -12,8 +12,8 @@ import time # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import TMP from tests.support.unit import skipIf # Import salt libs @@ -38,7 +38,7 @@ def test_grains_overwrite(self): # `test_custom_grain2.py` file is present in the _grains directory # before trying to get the grains. This test may execute before the # minion has finished syncing down the files it needs. - module = os.path.join(TMP, 'rootdir', 'cache', 'files', + module = os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'cache', 'files', 'base', '_grains', 'test_custom_grain2.py') tries = 0 while not os.path.exists(module): diff --git a/tests/integration/loader/test_ext_modules.py b/tests/integration/loader/test_ext_modules.py index bad67f8c0dd5..053666bc2e22 100644 --- a/tests/integration/loader/test_ext_modules.py +++ b/tests/integration/loader/test_ext_modules.py @@ -15,8 +15,8 @@ import time # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import TMP class LoaderOverridesTest(ModuleCase): @@ -29,7 +29,7 @@ def test_overridden_internal(self): # `override_test.py` file is present in the _modules directory before # trying to list all functions. This test may execute before the # minion has finished syncing down the files it needs. - module = os.path.join(TMP, 'rootdir', 'cache', 'files', + module = os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'cache', 'files', 'base', '_modules', 'override_test.py') tries = 0 while not os.path.exists(module): diff --git a/tests/integration/logging/test_jid_logging.py b/tests/integration/logging/test_jid_logging.py index 6bff3077c910..5fe6e84f7937 100644 --- a/tests/integration/logging/test_jid_logging.py +++ b/tests/integration/logging/test_jid_logging.py @@ -6,7 +6,7 @@ # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.unit import skipIf -from tests.support.helpers import TestsLoggingHandler, flaky +from tests.support.helpers import TstSuiteLoggingHandler, flaky import logging import salt.ext.six as six @@ -22,8 +22,8 @@ def setUp(self): Set up ''' log_format = '[%(levelname)-8s] %(jid)s %(message)s' - self.handler = TestsLoggingHandler(format=log_format, - level=logging.DEBUG) + self.handler = TstSuiteLoggingHandler(format=log_format, + level=logging.DEBUG) @flaky def test_jid_in_logs(self): diff --git a/tests/integration/master/test_event_return.py b/tests/integration/master/test_event_return.py index c80540e26540..24045ac7b8b7 100644 --- a/tests/integration/master/test_event_return.py +++ b/tests/integration/master/test_event_return.py @@ -17,12 +17,13 @@ # Import Salt Testing libs from tests.support.case import TestCase -from tests.support.paths import ScriptPathMixin +from tests.support.cli_scripts import ScriptPathMixin from tests.support.helpers import get_unused_localhost_port from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.processes import terminate_process # Import 3rd-party libs -from tests.support.processes import terminate_process +import pytest # Import Salt libs import salt.ext.six as six @@ -49,6 +50,8 @@ def setUpClass(cls): temp_config = AdaptedConfigurationTestCaseMixin.get_temp_config('master', **overrides) cls.root_dir = temp_config['root_dir'] cls.config_dir = os.path.dirname(temp_config['conf_file']) + if temp_config['transport'] == 'tcp': + pytest.skip('Test only applicable to the ZMQ transport') @classmethod def tearDownClass(cls): diff --git a/tests/integration/minion/test_blackout.py b/tests/integration/minion/test_blackout.py index 6fd135743cf4..2a8218cfac07 100644 --- a/tests/integration/minion/test_blackout.py +++ b/tests/integration/minion/test_blackout.py @@ -12,7 +12,6 @@ # Import Salt Testing libs from tests.support.case import ModuleCase -from tests.support.helpers import flaky from tests.support.runtests import RUNTIME_VARS # Import Salt libs @@ -52,10 +51,14 @@ def setUp(self): self.addCleanup(self.cleanup_blackout_pillar) def tearDown(self): - self.end_blackout(sleep=False) + self.end_blackout() # Be sure to also refresh the sub_minion pillar self.run_function('saltutil.refresh_pillar', minion_tgt='sub_minion') - time.sleep(10) # wait for minion to exit blackout mode + timeout = 120 + if not self.wait_for_blackout(end=True, tgt='sub_minion', timeout=timeout): + raise Exception( + 'Minion did not exit blackout mode after {} seconds'.format(timeout) + ) self.wait_for_all_jobs() def cleanup_blackout_pillar(self): @@ -72,11 +75,33 @@ def begin_blackout(self, blackout_data='minion_blackout: True'): self.wait_for_all_jobs() with salt.utils.files.fopen(self.blackout_pillar, 'w') as wfh: wfh.write(blackout_data) - self.run_function('saltutil.refresh_pillar') - time.sleep(10) # wait for minion to enter blackout mode + ret = self.run_function('saltutil.refresh_pillar', timeout=30) + timeout = 120 + if not self.wait_for_blackout(timeout=timeout): + raise Exception( + 'Minion did not enter blackout mode after {} seconds'.format(timeout) + ) log.info('Entered minion blackout.') - def end_blackout(self, sleep=True): + def wait_for_blackout(self, end=False, tgt='minion', timeout=120, sleep=.3): + ''' + Wait for blackout mode to start or end. + ''' + start = time.time() + while time.time() - start <= timeout: + ret = self.run_function( + 'pillar.get', minion_tgt=tgt, arg=['minion_blackout'], timeout=30, + ) + if end: + if str(ret).find('Minion in blackout mode') == -1: + return True + else: + if str(ret).find('Minion in blackout mode') != -1: + return True + time.sleep(sleep) + return False + + def end_blackout(self): ''' takedown minion blackout mode ''' @@ -84,12 +109,14 @@ def end_blackout(self, sleep=True): with salt.utils.files.fopen(self.blackout_pillar, 'w') as wfh: wfh.write('minion_blackout: False\n') self.run_function('saltutil.refresh_pillar') - if sleep: - time.sleep(10) # wait for minion to exit blackout mode + timeout = 120 + if not self.wait_for_blackout(end=True, timeout=timeout): + raise Exception( + 'Minion did not exit blackout mode after {} seconds'.format(timeout) + ) self.wait_for_all_jobs() log.info('Exited minion blackout.') - @flaky def test_blackout(self): ''' Test that basic minion blackout functionality works @@ -104,7 +131,6 @@ def test_blackout(self): ret = self.run_function('test.ping') self.assertEqual(ret, True) - @flaky def test_blackout_whitelist(self): ''' Test that minion blackout whitelist works @@ -123,7 +149,6 @@ def test_blackout_whitelist(self): self.assertTrue(isinstance(fib_ret, list)) self.assertEqual(fib_ret[0], 13) - @flaky def test_blackout_nonwhitelist(self): ''' Test that minion refuses to run non-whitelisted functions during diff --git a/tests/integration/minion/test_executor.py b/tests/integration/minion/test_executor.py new file mode 100644 index 000000000000..93e9c57ffdb3 --- /dev/null +++ b/tests/integration/minion/test_executor.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals + +import logging + +# Import Salt Testing libs +from tests.support.case import ModuleCase, ShellCase + +log = logging.getLogger(__name__) + + +class ExecutorTest(ModuleCase, ShellCase): + + def setup(self): + self.run_function('saltutil.sync_all') + + def test_executor(self): + ''' + test that dunders are set + ''' + data = self.run_call('test.arg --module-executors=arg') + self.assertIn('test.arg fired', "".join(data)) diff --git a/tests/integration/minion/test_pillar.py b/tests/integration/minion/test_pillar.py index f80df05ab1fc..f6254b91d4b1 100644 --- a/tests/integration/minion/test_pillar.py +++ b/tests/integration/minion/test_pillar.py @@ -14,10 +14,11 @@ import subprocess # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import TMP, TMP_CONF_DIR from tests.support.unit import skipIf -from tests.support.helpers import requires_system_grains +from tests.support.helpers import requires_system_grains, dedent +from tests.support.runtests import RUNTIME_VARS # Import salt libs import salt.utils.files @@ -29,33 +30,6 @@ log = logging.getLogger(__name__) -GPG_HOMEDIR = os.path.join(TMP_CONF_DIR, 'gpgkeys') -PILLAR_BASE = os.path.join(TMP, 'test-decrypt-pillar', 'pillar') -TOP_SLS = os.path.join(PILLAR_BASE, 'top.sls') -GPG_SLS = os.path.join(PILLAR_BASE, 'gpg.sls') -DEFAULT_OPTS = { - 'cachedir': os.path.join(TMP, 'rootdir', 'cache'), - 'config_dir': TMP_CONF_DIR, - 'optimization_order': [0, 1, 2], - 'extension_modules': os.path.join(TMP, - 'test-decrypt-pillar', - 'extmods'), - 'pillar_roots': {'base': [PILLAR_BASE]}, - 'ext_pillar_first': False, - 'ext_pillar': [], - 'decrypt_pillar_default': 'gpg', - 'decrypt_pillar_delimiter': ':', - 'decrypt_pillar_renderers': ['gpg'], -} -ADDITIONAL_OPTS = ( - 'conf_file', - 'file_roots', - 'state_top', - 'renderer', - 'renderer_whitelist', - 'renderer_blacklist', -) - TEST_KEY = '''\ -----BEGIN PGP PRIVATE KEY BLOCK----- @@ -222,14 +196,53 @@ } -class BasePillarTest(ModuleCase): +class _CommonBase(ModuleCase): + + @classmethod + def setUpClass(cls): + cls.pillar_base = os.path.join(RUNTIME_VARS.TMP, 'test-decrypt-pillar', 'pillar') + cls.top_sls = os.path.join(cls.pillar_base, 'top.sls') + cls.gpg_sls = os.path.join(cls.pillar_base, 'gpg.sls') + cls.default_opts = { + 'cachedir': os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'cache'), + 'optimization_order': [0, 1, 2], + 'extension_modules': os.path.join(RUNTIME_VARS.TMP, + 'test-decrypt-pillar', + 'extmods'), + 'pillar_roots': {'base': [cls.pillar_base]}, + 'ext_pillar_first': False, + 'ext_pillar': [], + 'decrypt_pillar_default': 'gpg', + 'decrypt_pillar_delimiter': ':', + 'decrypt_pillar_renderers': ['gpg'], + } + cls.additional_opts = ( + 'conf_file', + 'file_roots', + 'state_top', + 'renderer', + 'renderer_whitelist', + 'renderer_blacklist', + ) + cls.gpg_homedir = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'gpgkeys') + + def _build_opts(self, opts): + ret = copy.deepcopy(self.default_opts) + for item in self.additional_opts: + ret[item] = self.master_opts[item] + ret.update(opts) + return ret + + +class BasePillarTest(_CommonBase): ''' Tests for pillar decryption ''' @classmethod def setUpClass(cls): - os.makedirs(PILLAR_BASE) - with salt.utils.files.fopen(TOP_SLS, 'w') as fp_: + super(BasePillarTest, cls).setUpClass() + os.makedirs(cls.pillar_base) + with salt.utils.files.fopen(cls.top_sls, 'w') as fp_: fp_.write(textwrap.dedent('''\ base: 'N@mins not L@minion': @@ -238,22 +251,15 @@ def setUpClass(cls): - ng2 ''')) - with salt.utils.files.fopen(os.path.join(PILLAR_BASE, 'ng1.sls'), 'w') as fp_: + with salt.utils.files.fopen(os.path.join(cls.pillar_base, 'ng1.sls'), 'w') as fp_: fp_.write('pillar_from_nodegroup: True') - with salt.utils.files.fopen(os.path.join(PILLAR_BASE, 'ng2.sls'), 'w') as fp_: + with salt.utils.files.fopen(os.path.join(cls.pillar_base, 'ng2.sls'), 'w') as fp_: fp_.write('pillar_from_nodegroup_with_ghost: True') @classmethod def tearDownClass(cls): - shutil.rmtree(PILLAR_BASE) - - def _build_opts(self, opts): - ret = copy.deepcopy(DEFAULT_OPTS) - for item in ADDITIONAL_OPTS: - ret[item] = self.master_opts[item] - ret.update(opts) - return ret + shutil.rmtree(cls.pillar_base) def test_pillar_top_compound_match(self, grains=None): ''' @@ -284,7 +290,7 @@ def test_pillar_top_compound_match(self, grains=None): @skipIf(not salt.utils.path.which('gpg'), 'GPG is not installed') -class DecryptGPGPillarTest(ModuleCase): +class DecryptGPGPillarTest(_CommonBase): ''' Tests for pillar decryption ''' @@ -292,14 +298,15 @@ class DecryptGPGPillarTest(ModuleCase): @classmethod def setUpClass(cls): + super(DecryptGPGPillarTest, cls).setUpClass() try: - os.makedirs(GPG_HOMEDIR, mode=0o700) + os.makedirs(cls.gpg_homedir, mode=0o700) except Exception: cls.created_gpg_homedir = False raise else: cls.created_gpg_homedir = True - cmd_prefix = ['gpg', '--homedir', GPG_HOMEDIR] + cmd_prefix = ['gpg', '--homedir', cls.gpg_homedir] cmd = cmd_prefix + ['--list-keys'] log.debug('Instantiating gpg keyring using: %s', cmd) @@ -318,19 +325,19 @@ def setUpClass(cls): shell=False).communicate(input=salt.utils.stringutils.to_bytes(TEST_KEY))[0] log.debug('Result:\n%s', output) - os.makedirs(PILLAR_BASE) - with salt.utils.files.fopen(TOP_SLS, 'w') as fp_: + os.makedirs(cls.pillar_base) + with salt.utils.files.fopen(cls.top_sls, 'w') as fp_: fp_.write(textwrap.dedent('''\ base: '*': - gpg ''')) - with salt.utils.files.fopen(GPG_SLS, 'w') as fp_: + with salt.utils.files.fopen(cls.gpg_sls, 'w') as fp_: fp_.write(GPG_PILLAR_YAML) @classmethod def tearDownClass(cls): - cmd = ['gpg-connect-agent', '--homedir', GPG_HOMEDIR] + cmd = ['gpg-connect-agent', '--homedir', cls.gpg_homedir] try: log.debug('Killing gpg-agent using: %s', cmd) output = subprocess.Popen(cmd, @@ -344,19 +351,12 @@ def tearDownClass(cls): if cls.created_gpg_homedir: try: - shutil.rmtree(GPG_HOMEDIR) + shutil.rmtree(cls.gpg_homedir) except OSError as exc: # GPG socket can disappear before rmtree gets to this point if exc.errno != errno.ENOENT: raise - shutil.rmtree(PILLAR_BASE) - - def _build_opts(self, opts): - ret = copy.deepcopy(DEFAULT_OPTS) - for item in ADDITIONAL_OPTS: - ret[item] = self.master_opts[item] - ret.update(opts) - return ret + shutil.rmtree(cls.pillar_base) @requires_system_grains def test_decrypt_pillar_default_renderer(self, grains=None): @@ -490,3 +490,169 @@ def test_decrypt_pillar_invalid_renderer(self, grains=None): expected['secrets']['vault']['baz']) self.assertEqual(ret['secrets']['vault']['qux'], expected['secrets']['vault']['qux']) + + +class RefreshPillarTest(ModuleCase): + ''' + These tests validate the behavior defined in the documentation: + + https://docs.saltstack.com/en/latest/topics/pillar/#in-memory-pillar-data-vs-on-demand-pillar-data + + These tests also serve as a regression test for: + + https://github.com/saltstack/salt/issues/54941 + ''' + + def cleanup_pillars(self, top_path, pillar_path): + os.remove(top_path) + os.remove(pillar_path) + self.run_function('saltutil.refresh_pillar', arg=(True,)) + + def create_pillar(self, key): + ''' + Utility method to create a pillar for the minion and a value of true, + this method also removes and cleans up the pillar at the end of the + test. + ''' + top_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls') + pillar_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'test_pillar.sls') + with salt.utils.files.fopen(top_path, 'w') as fd: + fd.write(dedent(''' + base: + 'minion': + - test_pillar + ''')) + with salt.utils.files.fopen(pillar_path, 'w') as fd: + fd.write(dedent(''' + {}: true + '''.format(key))) + self.addCleanup(self.cleanup_pillars, top_path, pillar_path) + + def test_pillar_refresh_pillar_raw(self): + ''' + Validate the minion's pillar.raw call behavior for new pillars + ''' + key = 'issue-54941-raw' + + # We do not expect to see the pillar beacuse it does not exist yet + val = self.run_function('pillar.raw', arg=(key,)) + assert val == {} + + self.create_pillar(key) + + # The pillar exists now but raw reads it from in-memory pillars + val = self.run_function('pillar.raw', arg=(key,)) + assert val == {} + + # Calling refresh_pillar to update in-memory pillars + ret = self.run_function('saltutil.refresh_pillar', arg=(True,)) + + # The pillar can now be read from in-memory pillars + val = self.run_function('pillar.raw', arg=(key,)) + assert val is True, repr(val) + + def test_pillar_refresh_pillar_get(self): + ''' + Validate the minion's pillar.get call behavior for new pillars + ''' + key = 'issue-54941-get' + + # We do not expect to see the pillar beacuse it does not exist yet + val = self.run_function('pillar.get', arg=(key,)) + assert val == '' + top_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls') + pillar_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'test_pillar.sls') + + self.create_pillar(key) + + # The pillar exists now but get reads it from in-memory pillars, no + # refresh happens + val = self.run_function('pillar.get', arg=(key,)) + assert val == '' + + # Calling refresh_pillar to update in-memory pillars + ret = self.run_function('saltutil.refresh_pillar', arg=(True,)) + assert ret is True + + # The pillar can now be read from in-memory pillars + val = self.run_function('pillar.get', arg=(key,)) + assert val is True, repr(val) + + def test_pillar_refresh_pillar_item(self): + ''' + Validate the minion's pillar.item call behavior for new pillars + ''' + key = 'issue-54941-item' + + # We do not expect to see the pillar beacuse it does not exist yet + val = self.run_function('pillar.item', arg=(key,)) + assert key in val + assert val[key] == '' + + self.create_pillar(key) + + # The pillar exists now but get reads it from in-memory pillars, no + # refresh happens + val = self.run_function('pillar.item', arg=(key,)) + assert key in val + assert val[key] == '' + + # Calling refresh_pillar to update in-memory pillars + ret = self.run_function('saltutil.refresh_pillar', arg=(True,)) + assert ret is True + + # The pillar can now be read from in-memory pillars + val = self.run_function('pillar.item', arg=(key,)) + assert key in val + assert val[key] is True + + def test_pillar_refresh_pillar_items(self): + ''' + Validate the minion's pillar.item call behavior for new pillars + ''' + key = 'issue-54941-items' + + # We do not expect to see the pillar beacuse it does not exist yet + val = self.run_function('pillar.items') + assert key not in val + + self.create_pillar(key) + + # A pillar.items call sees the pillar right away because a + # refresh_pillar event is fired. + val = self.run_function('pillar.items') + assert key in val + assert val[key] is True + + def test_pillar_refresh_pillar_ping(self): + ''' + Validate the minion's test.ping does not update pillars + + See: https://github.com/saltstack/salt/issues/54941 + ''' + key = 'issue-54941-ping' + + # We do not expect to see the pillar beacuse it does not exist yet + val = self.run_function('pillar.item', arg=(key,)) + assert key in val + assert val[key] == '' + + self.create_pillar(key) + + val = self.run_function('test.ping') + assert val is True + + # The pillar exists now but get reads it from in-memory pillars, no + # refresh happens + val = self.run_function('pillar.item', arg=(key,)) + assert key in val + assert val[key] == '' + + # Calling refresh_pillar to update in-memory pillars + ret = self.run_function('saltutil.refresh_pillar', arg=(True,)) + assert ret is True + + # The pillar can now be read from in-memory pillars + val = self.run_function('pillar.item', arg=(key,)) + assert key in val + assert val[key] is True diff --git a/tests/integration/modules/test_archive.py b/tests/integration/modules/test_archive.py index ca77c6d42f2f..56f603ea4321 100644 --- a/tests/integration/modules/test_archive.py +++ b/tests/integration/modules/test_archive.py @@ -9,9 +9,9 @@ import textwrap # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.unit import skipIf -from tests.support.paths import TMP from tests.support.helpers import destructiveTest # Import salt libs @@ -35,7 +35,7 @@ class ArchiveTest(ModuleCase): Validate the archive module ''' # Base path used for test artifacts - base_path = os.path.join(TMP, 'modules', 'archive') + base_path = os.path.join(RUNTIME_VARS.TMP, 'modules', 'archive') def _set_artifact_paths(self, arch_fmt): ''' diff --git a/tests/integration/modules/test_beacons.py b/tests/integration/modules/test_beacons.py index 6c8d95a61742..24a6b270ace1 100644 --- a/tests/integration/modules/test_beacons.py +++ b/tests/integration/modules/test_beacons.py @@ -11,6 +11,7 @@ from salt.exceptions import CommandExecutionError # Salttesting libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.unit import skipIf @@ -21,7 +22,7 @@ class BeaconsAddDeleteTest(ModuleCase): ''' def setUp(self): self.minion_conf_d_dir = os.path.join( - self.minion_opts['config_dir'], + RUNTIME_VARS.TMP_CONF_DIR, os.path.dirname(self.minion_opts['default_include'])) if not os.path.isdir(self.minion_conf_d_dir): os.makedirs(self.minion_conf_d_dir) @@ -71,7 +72,7 @@ def tearDownClass(cls): def setUp(self): if self.minion_conf_d_dir is None: self.minion_conf_d_dir = os.path.join( - self.minion_opts['config_dir'], + RUNTIME_VARS.TMP_CONF_DIR, os.path.dirname(self.minion_opts['default_include'])) if not os.path.isdir(self.minion_conf_d_dir): os.makedirs(self.minion_conf_d_dir) diff --git a/tests/integration/modules/test_cmdmod.py b/tests/integration/modules/test_cmdmod.py index 0f3a9f8c0d95..008b750423f6 100644 --- a/tests/integration/modules/test_cmdmod.py +++ b/tests/integration/modules/test_cmdmod.py @@ -14,9 +14,8 @@ destructiveTest, skip_if_binaries_missing, skip_if_not_root, - this_user, ) -from tests.support.paths import TMP +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import skipIf # Import salt libs @@ -189,7 +188,7 @@ def test_script_cwd(self): ''' cmd.script with cwd ''' - tmp_cwd = tempfile.mkdtemp(dir=TMP) + tmp_cwd = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) args = 'saltines crackers biscuits=yes' script = 'salt://script.py' ret = self.run_function('cmd.script', [script, args], cwd=tmp_cwd) @@ -199,7 +198,7 @@ def test_script_cwd_with_space(self): ''' cmd.script with cwd ''' - tmp_cwd = "{0}{1}test 2".format(tempfile.mkdtemp(dir=TMP), os.path.sep) + tmp_cwd = "{0}{1}test 2".format(tempfile.mkdtemp(dir=RUNTIME_VARS.TMP), os.path.sep) os.mkdir(tmp_cwd) args = 'saltines crackers biscuits=yes' @@ -304,7 +303,7 @@ def test_quotes_runas(self): expected_result = 'SELECT * FROM foo WHERE bar="baz"' - runas = this_user() + runas = RUNTIME_VARS.RUNNING_TESTS_USER result = self.run_function('cmd.run_stdout', [cmd], runas=runas).strip() @@ -321,7 +320,7 @@ def test_avoid_injecting_shell_code_as_root(self): cmd = 'echo $(id -u)' root_id = self.run_function('cmd.run_stdout', [cmd]) - runas_root_id = self.run_function('cmd.run_stdout', [cmd], runas=this_user()) + runas_root_id = self.run_function('cmd.run_stdout', [cmd], runas=RUNTIME_VARS.RUNNING_TESTS_USER) with self._ensure_user_exists(self.runas_usr): user_id = self.run_function('cmd.run_stdout', [cmd], runas=self.runas_usr) @@ -338,7 +337,7 @@ def test_cwd_runas(self): or not runas is in use. ''' cmd = 'pwd' - tmp_cwd = tempfile.mkdtemp(dir=TMP) + tmp_cwd = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) os.chmod(tmp_cwd, 0o711) cwd_normal = self.run_function('cmd.run_stdout', [cmd], cwd=tmp_cwd).rstrip('\n') @@ -377,7 +376,7 @@ def test_runas_complex_command_bad_cwd(self): buggy behaviour, but its purpose is to ensure that the greater bug of running commands after failing to cd does not occur. ''' - tmp_cwd = tempfile.mkdtemp(dir=TMP) + tmp_cwd = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) os.chmod(tmp_cwd, 0o700) with self._ensure_user_exists(self.runas_usr): diff --git a/tests/integration/modules/test_config.py b/tests/integration/modules/test_config.py index cb00160db8a4..be9aed3396c1 100644 --- a/tests/integration/modules/test_config.py +++ b/tests/integration/modules/test_config.py @@ -69,7 +69,7 @@ def test_option(self): self.run_function( 'config.option', ['master_port']), - 64506) + self.get_config('minion')['master_port']) # pillar conf opt self.assertEqual( self.run_function( diff --git a/tests/integration/modules/test_cp.py b/tests/integration/modules/test_cp.py index 5c7173b50be9..a46284e05b10 100644 --- a/tests/integration/modules/test_cp.py +++ b/tests/integration/modules/test_cp.py @@ -12,6 +12,7 @@ import signal import tempfile import textwrap +import time # Import Salt Testing libs from tests.support.case import ModuleCase @@ -20,7 +21,7 @@ skip_if_not_root, with_tempfile) from tests.support.unit import skipIf -import tests.support.paths as paths +from tests.support.runtests import RUNTIME_VARS # Import 3rd party libs import salt.ext.six as six @@ -71,7 +72,7 @@ def test_get_file_to_dir(self): ''' cp.get_file ''' - tgt = os.path.join(paths.TMP, '') + tgt = os.path.join(RUNTIME_VARS.TMP, '') self.run_function( 'cp.get_file', [ @@ -107,7 +108,7 @@ def test_get_file_gzipped(self, tgt): ''' cp.get_file ''' - src = os.path.join(paths.FILES, 'file', 'base', 'file.big') + src = os.path.join(RUNTIME_VARS.FILES, 'file', 'base', 'file.big') with salt.utils.files.fopen(src, 'rb') as fp_: hash_str = hashlib.md5(fp_.read()).hexdigest() @@ -130,7 +131,7 @@ def test_get_file_makedirs(self): ''' cp.get_file ''' - tgt = os.path.join(paths.TMP, 'make', 'dirs', 'scene33') + tgt = os.path.join(RUNTIME_VARS.TMP, 'make', 'dirs', 'scene33') self.run_function( 'cp.get_file', [ @@ -139,7 +140,7 @@ def test_get_file_makedirs(self): ], makedirs=True ) - self.addCleanup(shutil.rmtree, os.path.join(paths.TMP, 'make'), ignore_errors=True) + self.addCleanup(shutil.rmtree, os.path.join(RUNTIME_VARS.TMP, 'make'), ignore_errors=True) with salt.utils.files.fopen(tgt, 'r') as scene: data = salt.utils.stringutils.to_unicode(scene.read()) self.assertIn('KNIGHT: They\'re nervous, sire.', data) @@ -163,7 +164,7 @@ def test_get_dir(self): ''' cp.get_dir ''' - tgt = os.path.join(paths.TMP, 'many') + tgt = os.path.join(RUNTIME_VARS.TMP, 'many') self.run_function( 'cp.get_dir', [ @@ -179,7 +180,7 @@ def test_get_dir_templated_paths(self): ''' cp.get_dir ''' - tgt = os.path.join(paths.TMP, 'many') + tgt = os.path.join(RUNTIME_VARS.TMP, 'many') self.run_function( 'cp.get_dir', [ @@ -214,7 +215,7 @@ def test_get_url_makedirs(self): ''' cp.get_url ''' - tgt = os.path.join(paths.TMP, 'make', 'dirs', 'scene33') + tgt = os.path.join(RUNTIME_VARS.TMP, 'make', 'dirs', 'scene33') self.run_function( 'cp.get_url', [ @@ -223,7 +224,7 @@ def test_get_url_makedirs(self): ], makedirs=True ) - self.addCleanup(shutil.rmtree, os.path.join(paths.TMP, 'make'), ignore_errors=True) + self.addCleanup(shutil.rmtree, os.path.join(RUNTIME_VARS.TMP, 'make'), ignore_errors=True) with salt.utils.files.fopen(tgt, 'r') as scene: data = salt.utils.stringutils.to_unicode(scene.read()) self.assertIn('KNIGHT: They\'re nervous, sire.', data) @@ -273,7 +274,7 @@ def test_get_url_to_dir(self): ''' cp.get_url with salt:// source ''' - tgt = os.path.join(paths.TMP, '') + tgt = os.path.join(RUNTIME_VARS.TMP, '') self.run_function( 'cp.get_url', [ @@ -316,6 +317,7 @@ def test_get_url_https_dest_empty(self): [ 'https://repo.saltstack.com/index.html', ]) + with salt.utils.files.fopen(ret, 'r') as instructions: data = salt.utils.stringutils.to_unicode(instructions.read()) self.assertIn('Bootstrap', data) @@ -329,13 +331,24 @@ def test_get_url_https_no_dest(self): ''' cp.get_url with https:// source given and destination set as None ''' + timeout = 500 + start = time.time() + sleep = 5 tgt = None - ret = self.run_function( - 'cp.get_url', - [ - 'https://repo.saltstack.com/index.html', - tgt, - ]) + while time.time() - start <= timeout: + ret = self.run_function( + 'cp.get_url', + [ + 'https://repo.saltstack.com/index.html', + tgt, + ]) + if ret.find('HTTP 599') == -1: + break + time.sleep(sleep) + if ret.find('HTTP 599') != -1: + raise Exception( + 'https://repo.saltstack.com/index.html returned 599 error' + ) self.assertIn('Bootstrap', ret) self.assertIn('Debian', ret) self.assertIn('Windows', ret) @@ -346,7 +359,7 @@ def test_get_url_file(self): cp.get_url with file:// source given ''' tgt = '' - src = os.path.join('file://', paths.FILES, 'file', 'base', 'file.big') + src = os.path.join('file://', RUNTIME_VARS.FILES, 'file', 'base', 'file.big') ret = self.run_function( 'cp.get_url', [ @@ -363,7 +376,7 @@ def test_get_url_file_no_dest(self): cp.get_url with file:// source given and destination set as None ''' tgt = None - src = os.path.join('file://', paths.FILES, 'file', 'base', 'file.big') + src = os.path.join('file://', RUNTIME_VARS.FILES, 'file', 'base', 'file.big') ret = self.run_function( 'cp.get_url', [ @@ -420,7 +433,7 @@ def test_get_file_str_local(self): ''' cp.get_file_str with file:// source given ''' - src = os.path.join('file://', paths.FILES, 'file', 'base', 'file.big') + src = os.path.join('file://', RUNTIME_VARS.FILES, 'file', 'base', 'file.big') ret = self.run_function( 'cp.get_file_str', [ @@ -476,7 +489,7 @@ def test_cache_local_file(self): ''' cp.cache_local_file ''' - src = os.path.join(paths.TMP, 'random') + src = os.path.join(RUNTIME_VARS.TMP, 'random') with salt.utils.files.fopen(src, 'w+') as fn_: fn_.write(salt.utils.stringutils.to_str('foo')) ret = self.run_function( @@ -496,7 +509,7 @@ def test_cache_remote_file(self): ''' nginx_port = get_unused_localhost_port() url_prefix = 'http://localhost:{0}/'.format(nginx_port) - temp_dir = tempfile.mkdtemp(dir=paths.TMP) + temp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True) nginx_root_dir = os.path.join(temp_dir, 'root') nginx_conf_dir = os.path.join(temp_dir, 'conf') @@ -505,7 +518,7 @@ def test_cache_remote_file(self): file_contents = 'Hello world!' for dirname in (nginx_root_dir, nginx_conf_dir): - os.mkdir(dirname) + os.makedirs(dirname) # Write the temp file with salt.utils.files.fopen(os.path.join(nginx_root_dir, 'actual_file'), 'w') as fp_: @@ -642,7 +655,7 @@ def test_get_file_from_env_predefined(self, tgt): ''' cp.get_file ''' - tgt = os.path.join(paths.TMP, 'cheese') + tgt = os.path.join(RUNTIME_VARS.TMP, 'cheese') try: self.run_function('cp.get_file', ['salt://cheese', tgt]) with salt.utils.files.fopen(tgt, 'r') as cheese: @@ -654,7 +667,7 @@ def test_get_file_from_env_predefined(self, tgt): @with_tempfile() def test_get_file_from_env_in_url(self, tgt): - tgt = os.path.join(paths.TMP, 'cheese') + tgt = os.path.join(RUNTIME_VARS.TMP, 'cheese') try: self.run_function('cp.get_file', ['salt://cheese?saltenv=prod', tgt]) with salt.utils.files.fopen(tgt, 'r') as cheese: @@ -665,18 +678,18 @@ def test_get_file_from_env_in_url(self, tgt): os.unlink(tgt) def test_push(self): - log_to_xfer = os.path.join(paths.TMP, uuid.uuid4().hex) + log_to_xfer = os.path.join(RUNTIME_VARS.TMP, uuid.uuid4().hex) open(log_to_xfer, 'w').close() # pylint: disable=resource-leakage try: self.run_function('cp.push', [log_to_xfer]) tgt_cache_file = os.path.join( - paths.TMP, + RUNTIME_VARS.TMP, 'master-minion-root', 'cache', 'minions', 'minion', 'files', - paths.TMP, + RUNTIME_VARS.TMP, log_to_xfer) self.assertTrue(os.path.isfile(tgt_cache_file), 'File was not cached on the master') finally: diff --git a/tests/integration/modules/test_dockermod.py b/tests/integration/modules/test_dockermod.py index 2379f4404afb..c8288777b8b4 100644 --- a/tests/integration/modules/test_dockermod.py +++ b/tests/integration/modules/test_dockermod.py @@ -5,7 +5,6 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import functools import random import string import sys @@ -30,30 +29,19 @@ def _random_name(prefix=''): return ret -def with_random_name(func): - ''' - generate a randomized name for a container - ''' - @functools.wraps(func) - def wrapper(self, *args, **kwargs): - name = _random_name(prefix='salt_') - return func(self, _random_name(prefix='salt_test_'), *args, **kwargs) - return wrapper - - @destructiveTest @skipIf(not salt.utils.path.which('dockerd'), 'Docker not installed') class DockerCallTestCase(ModuleCase, SaltReturnAssertsMixin): ''' Test docker_container states ''' - @with_random_name - def setUp(self, name): + + def setUp(self): ''' setup docker.call tests ''' # Create temp dir - self.random_name = name + self.random_name = _random_name(prefix='salt_test_') self.image_tag = sys.version_info[0] self.run_state('docker_image.present', diff --git a/tests/integration/modules/test_file.py b/tests/integration/modules/test_file.py index 234f5404fdfb..aac9272c8c27 100644 --- a/tests/integration/modules/test_file.py +++ b/tests/integration/modules/test_file.py @@ -21,9 +21,9 @@ pass # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.unit import skipIf -from tests.support.paths import FILES, TMP # Import salt libs import salt.utils.files @@ -45,18 +45,18 @@ class FileModuleTest(ModuleCase): Validate the file module ''' def setUp(self): - self.myfile = os.path.join(TMP, 'myfile') + self.myfile = os.path.join(RUNTIME_VARS.TMP, 'myfile') with salt.utils.files.fopen(self.myfile, 'w+') as fp: fp.write(salt.utils.stringutils.to_str('Hello' + os.linesep)) - self.mydir = os.path.join(TMP, 'mydir/isawesome') + self.mydir = os.path.join(RUNTIME_VARS.TMP, 'mydir/isawesome') if not os.path.isdir(self.mydir): # left behind... Don't fail because of this! os.makedirs(self.mydir) - self.mysymlink = os.path.join(TMP, 'mysymlink') + self.mysymlink = os.path.join(RUNTIME_VARS.TMP, 'mysymlink') if os.path.islink(self.mysymlink) or os.path.isfile(self.mysymlink): os.remove(self.mysymlink) symlink(self.myfile, self.mysymlink) - self.mybadsymlink = os.path.join(TMP, 'mybadsymlink') + self.mybadsymlink = os.path.join(RUNTIME_VARS.TMP, 'mybadsymlink') if os.path.islink(self.mybadsymlink) or os.path.isfile(self.mybadsymlink): os.remove(self.mybadsymlink) symlink('/nonexistentpath', self.mybadsymlink) @@ -143,8 +143,8 @@ def test_patch(self): self.skipTest('patch is not installed') src_patch = os.path.join( - FILES, 'file', 'base', 'hello.patch') - src_file = os.path.join(TMP, 'src.txt') + RUNTIME_VARS.FILES, 'file', 'base', 'hello.patch') + src_file = os.path.join(RUNTIME_VARS.TMP, 'src.txt') with salt.utils.files.fopen(src_file, 'w+') as fp: fp.write(salt.utils.stringutils.to_str('Hello\n')) diff --git a/tests/integration/modules/test_gem.py b/tests/integration/modules/test_gem.py index 12f7e6bf7481..39d97726b148 100644 --- a/tests/integration/modules/test_gem.py +++ b/tests/integration/modules/test_gem.py @@ -17,13 +17,6 @@ # Import 3rd-party libs from tornado.httpclient import HTTPClient -GEM = 'tidy' -GEM_VER = '1.1.2' -OLD_GEM = 'brass' -OLD_VERSION = '1.0.0' -NEW_VERSION = '1.2.1' -GEM_LIST = [GEM, OLD_GEM] - def check_status(): ''' @@ -46,64 +39,72 @@ def setUp(self): if check_status() is False: self.skipTest('External resource \'https://rubygems.org\' is not available') + self.GEM = 'tidy' + self.GEM_VER = '1.1.2' + self.OLD_GEM = 'brass' + self.OLD_VERSION = '1.0.0' + self.NEW_VERSION = '1.2.1' + self.GEM_LIST = [self.GEM, self.OLD_GEM] + for name in ('GEM', 'GEM_VER', 'OLD_GEM', 'OLD_VERSION', 'NEW_VERSION', 'GEM_LIST'): + self.addCleanup(delattr, self, name) + + def uninstall_gem(): + # Remove gem if it is already installed + if self.run_function('gem.list', [self.GEM]): + self.run_function('gem.uninstall', [self.GEM]) + + self.addCleanup(uninstall_gem) + def test_install_uninstall(self): ''' gem.install gem.uninstall ''' - # Remove gem if it is already installed - if self.run_function('gem.list', [GEM]): - self.run_function('gem.uninstall', [GEM]) + self.run_function('gem.install', [self.GEM]) + gem_list = self.run_function('gem.list', [self.GEM]) + self.assertIn(self.GEM, gem_list) - self.run_function('gem.install', [GEM]) - gem_list = self.run_function('gem.list', [GEM]) - self.assertIn(GEM, gem_list) - - self.run_function('gem.uninstall', [GEM]) - self.assertFalse(self.run_function('gem.list', [GEM])) + self.run_function('gem.uninstall', [self.GEM]) + self.assertFalse(self.run_function('gem.list', [self.GEM])) def test_install_version(self): ''' gem.install rake version=11.1.2 ''' - # Remove gem if it is already installed - if self.run_function('gem.list', [GEM]): - self.run_function('gem.uninstall', [GEM]) - - self.run_function('gem.install', [GEM], version=GEM_VER) - gem_list = self.run_function('gem.list', [GEM]) - self.assertIn(GEM, gem_list) - self.assertIn(GEM_VER, gem_list[GEM]) + self.run_function('gem.install', [self.GEM], version=self.GEM_VER) + gem_list = self.run_function('gem.list', [self.GEM]) + self.assertIn(self.GEM, gem_list) + self.assertIn(self.GEM_VER, gem_list[self.GEM]) - self.run_function('gem.uninstall', [GEM]) - self.assertFalse(self.run_function('gem.list', [GEM])) + self.run_function('gem.uninstall', [self.GEM]) + self.assertFalse(self.run_function('gem.list', [self.GEM])) def test_list(self): ''' gem.list ''' - self.run_function('gem.install', [' '.join(GEM_LIST)]) + self.run_function('gem.install', [' '.join(self.GEM_LIST)]) all_ret = self.run_function('gem.list') - for gem in GEM_LIST: + for gem in self.GEM_LIST: self.assertIn(gem, all_ret) - single_ret = self.run_function('gem.list', [GEM]) - self.assertIn(GEM, single_ret) + single_ret = self.run_function('gem.list', [self.GEM]) + self.assertIn(self.GEM, single_ret) - self.run_function('gem.uninstall', [' '.join(GEM_LIST)]) + self.run_function('gem.uninstall', [' '.join(self.GEM_LIST)]) def test_list_upgrades(self): ''' gem.list_upgrades ''' # install outdated gem - self.run_function('gem.install', [OLD_GEM], version=OLD_VERSION) + self.run_function('gem.install', [self.OLD_GEM], version=self.OLD_VERSION) ret = self.run_function('gem.list_upgrades') - self.assertIn(OLD_GEM, ret) + self.assertIn(self.OLD_GEM, ret) - self.run_function('gem.uninstall', [OLD_GEM]) + self.run_function('gem.uninstall', [self.OLD_GEM]) def test_sources_add_remove(self): ''' @@ -124,20 +125,16 @@ def test_update(self): ''' gem.update ''' - # Remove gem if it is already installed - if self.run_function('gem.list', [OLD_GEM]): - self.run_function('gem.uninstall', [OLD_GEM]) - - self.run_function('gem.install', [OLD_GEM], version=OLD_VERSION) - gem_list = self.run_function('gem.list', [OLD_GEM]) - self.assertEqual({OLD_GEM: [OLD_VERSION]}, gem_list) + self.run_function('gem.install', [self.OLD_GEM], version=self.OLD_VERSION) + gem_list = self.run_function('gem.list', [self.OLD_GEM]) + self.assertEqual({self.OLD_GEM: [self.OLD_VERSION]}, gem_list) - self.run_function('gem.update', [OLD_GEM]) - gem_list = self.run_function('gem.list', [OLD_GEM]) - self.assertEqual({OLD_GEM: [NEW_VERSION, OLD_VERSION]}, gem_list) + self.run_function('gem.update', [self.OLD_GEM]) + gem_list = self.run_function('gem.list', [self.OLD_GEM]) + self.assertEqual({self.OLD_GEM: [self.NEW_VERSION, self.OLD_VERSION]}, gem_list) - self.run_function('gem.uninstall', [OLD_GEM]) - self.assertFalse(self.run_function('gem.list', [OLD_GEM])) + self.run_function('gem.uninstall', [self.OLD_GEM]) + self.assertFalse(self.run_function('gem.list', [self.OLD_GEM])) def test_update_system(self): ''' diff --git a/tests/integration/modules/test_git.py b/tests/integration/modules/test_git.py index f0341871a32c..671d1f18cc6e 100644 --- a/tests/integration/modules/test_git.py +++ b/tests/integration/modules/test_git.py @@ -20,9 +20,9 @@ import tempfile # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.unit import skipIf -from tests.support.paths import TMP from tests.support.helpers import skip_if_binaries_missing # Import salt libs @@ -83,7 +83,7 @@ def setUp(self): super(GitModuleTest, self).setUp() self.orig_cwd = os.getcwd() self.addCleanup(os.chdir, self.orig_cwd) - self.repo = tempfile.mkdtemp(dir=TMP) + self.repo = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.repo, ignore_errors=True) self.files = ('foo', 'bar', 'baz', 'питон') self.dirs = ('', 'qux') @@ -193,7 +193,7 @@ def test_archive(self): ''' Test git.archive ''' - tar_archive = os.path.join(TMP, 'test_archive.tar.gz') + tar_archive = os.path.join(RUNTIME_VARS.TMP, 'test_archive.tar.gz') try: self.assertTrue( self.run_function( @@ -224,7 +224,7 @@ def test_archive_subdir(self): Test git.archive on a subdir, giving only a partial copy of the repo in the resulting archive ''' - tar_archive = os.path.join(TMP, 'test_archive.tar.gz') + tar_archive = os.path.join(RUNTIME_VARS.TMP, 'test_archive.tar.gz') try: self.assertTrue( self.run_function( @@ -306,7 +306,7 @@ def test_clone(self): ''' Test cloning an existing repo ''' - clone_parent_dir = tempfile.mkdtemp(dir=TMP) + clone_parent_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.assertTrue( self.run_function('git.clone', [clone_parent_dir, self.repo]) ) @@ -317,7 +317,7 @@ def test_clone_with_alternate_name(self): ''' Test cloning an existing repo with an alternate name for the repo dir ''' - clone_parent_dir = tempfile.mkdtemp(dir=TMP) + clone_parent_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) clone_name = os.path.basename(self.repo) # Change to newly-created temp dir self.assertTrue( @@ -617,7 +617,7 @@ def test_init(self): ''' Use git.init to init a new repo ''' - new_repo = tempfile.mkdtemp(dir=TMP) + new_repo = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # `tempfile.mkdtemp` gets the path to the Temp directory using # environment variables. As a result, folder names longer than 8 @@ -971,9 +971,9 @@ def test_worktree_add_rm(self): else: worktree_add_prefix = 'Enter ' - worktree_path = tempfile.mkdtemp(dir=TMP) + worktree_path = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) worktree_basename = os.path.basename(worktree_path) - worktree_path2 = tempfile.mkdtemp(dir=TMP) + worktree_path2 = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) worktree_basename2 = os.path.basename(worktree_path2) # Even though this is Windows, git commands return a unix style path @@ -997,7 +997,7 @@ def test_worktree_add_rm(self): # Check if the main repo is a worktree self.assertFalse(self.run_function('git.is_worktree', [self.repo])) # Check if a non-repo directory is a worktree - empty_dir = tempfile.mkdtemp(dir=TMP) + empty_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.assertFalse(self.run_function('git.is_worktree', [empty_dir])) shutil.rmtree(empty_dir) # Remove the first worktree diff --git a/tests/integration/modules/test_grains.py b/tests/integration/modules/test_grains.py index 8dbe22c5f6dc..c24ba50b87d7 100644 --- a/tests/integration/modules/test_grains.py +++ b/tests/integration/modules/test_grains.py @@ -10,10 +10,13 @@ import time import pprint +# Import Salt libs +from salt.ext.six.moves import range + # Import Salt Testing libs from tests.support.case import ModuleCase -from tests.support.unit import skipIf from tests.support.helpers import flaky +from tests.support.unit import skipIf log = logging.getLogger(__name__) @@ -156,7 +159,14 @@ class GrainsAppendTestCase(ModuleCase): def setUp(self): # Start off with an empty list self.run_function('grains.setval', [self.GRAIN_KEY, []]) - self.addCleanup(self.run_function, 'grains.setval', [self.GRAIN_KEY, []]) + if not self.wait_for_grain(self.GRAIN_KEY, []): + raise Exception('Failed to set grain') + self.addCleanup(self.cleanup_grain) + + def cleanup_grain(self): + self.run_function('grains.setval', [self.GRAIN_KEY, []]) + if not self.wait_for_grain(self.GRAIN_KEY, []): + raise Exception('Failed to set grain') def test_grains_append(self): ''' @@ -174,10 +184,12 @@ def test_grains_append_val_already_present(self): 'salttesting-grain-key'.format(self.GRAIN_VAL) # First, make sure the test grain is present - self.run_function('grains.append', [self.GRAIN_KEY, self.GRAIN_VAL]) + ret = self.run_function('grains.append', [self.GRAIN_KEY, self.GRAIN_VAL]) + self.assertEqual(ret[self.GRAIN_KEY], [self.GRAIN_VAL]) # Now try to append again ret = self.run_function('grains.append', [self.GRAIN_KEY, self.GRAIN_VAL]) + self.assertTrue(self.wait_for_grain(self.GRAIN_KEY, [self.GRAIN_VAL])) if not ret or isinstance(ret, dict): # Sleep for a bit, sometimes the second "append" runs too quickly time.sleep(5) @@ -226,3 +238,28 @@ def test_grains_append_call_twice(self): pprint.pformat(append_2) ) ) + + def wait_for_grain(self, key, val, timeout=60, sleep=.3): + start = time.time() + while time.time() - start <= timeout: + ret = self.run_function('grains.get', [key]) + if ret == val: + return True + time.sleep(sleep) + return False + + def test_grains_remove_add(self): + second_grain = self.GRAIN_VAL + '-2' + ret = self.run_function('grains.get', [self.GRAIN_KEY]) + self.assertEqual(ret, []) + + for i in range(10): + ret = self.run_function('grains.setval', [self.GRAIN_KEY, []]) + self.assertEqual(ret[self.GRAIN_KEY], []) + ret = self.run_function('grains.append', [self.GRAIN_KEY, self.GRAIN_VAL]) + self.assertEqual(ret[self.GRAIN_KEY], [self.GRAIN_VAL]) + + ret = self.run_function('grains.setval', [self.GRAIN_KEY, []]) + self.assertTrue(self.wait_for_grain(self.GRAIN_KEY, [])) + ret = self.run_function('grains.append', [self.GRAIN_KEY, [self.GRAIN_VAL, second_grain]]) + self.assertEqual(ret[self.GRAIN_KEY], [self.GRAIN_VAL, second_grain]) diff --git a/tests/integration/modules/test_hosts.py b/tests/integration/modules/test_hosts.py index 2c601af890b1..fd30b8fa9634 100644 --- a/tests/integration/modules/test_hosts.py +++ b/tests/integration/modules/test_hosts.py @@ -6,16 +6,17 @@ from __future__ import absolute_import, print_function, unicode_literals import os import shutil +import logging # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import FILES, TMP # Import Salt libs import salt.utils.files import salt.utils.stringutils -HFN = os.path.join(TMP, 'hosts') +log = logging.getLogger(__name__) class HostsModuleTest(ModuleCase): @@ -25,30 +26,25 @@ class HostsModuleTest(ModuleCase): maxDiff = None - def __clean_hosts(self): - ''' - Clean out the hosts file - ''' - shutil.copyfile(os.path.join(FILES, 'hosts'), HFN) + @classmethod + def setUpClass(cls): + cls.hosts_file = os.path.join(RUNTIME_VARS.TMP, 'hosts') def __clear_hosts(self): ''' Delete the tmp hosts file ''' - if os.path.isfile(HFN): - os.remove(HFN) + if os.path.isfile(self.hosts_file): + os.remove(self.hosts_file) - def tearDown(self): - ''' - Make sure the tmp hosts file is gone - ''' - self.__clear_hosts() + def setUp(self): + shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, 'hosts'), self.hosts_file) + self.addCleanup(self.__clear_hosts) def test_list_hosts(self): ''' hosts.list_hosts ''' - self.__clean_hosts() hosts = self.run_function('hosts.list_hosts') self.assertEqual(len(hosts), 10) self.assertEqual(hosts['::1'], ['ip6-localhost', 'ip6-loopback']) @@ -59,8 +55,8 @@ def test_list_hosts_nofile(self): hosts.list_hosts without a hosts file ''' - if os.path.isfile(HFN): - os.remove(HFN) + if os.path.isfile(self.hosts_file): + os.remove(self.hosts_file) hosts = self.run_function('hosts.list_hosts') self.assertEqual(hosts, {}) @@ -68,7 +64,6 @@ def test_get_ip(self): ''' hosts.get_ip ''' - self.__clean_hosts() self.assertEqual( self.run_function('hosts.get_ip', ['myname']), '127.0.0.1' ) @@ -80,7 +75,6 @@ def test_get_alias(self): ''' hosts.get_alias ''' - self.__clean_hosts() self.assertEqual( self.run_function('hosts.get_alias', ['127.0.0.1']), ['localhost', 'myname'] @@ -99,7 +93,6 @@ def test_has_pair(self): ''' hosts.has_pair ''' - self.__clean_hosts() self.assertTrue( self.run_function('hosts.has_pair', ['127.0.0.1', 'myname']) ) @@ -111,7 +104,6 @@ def test_set_host(self): ''' hosts.set_hosts ''' - self.__clean_hosts() self.assertTrue( self.run_function('hosts.set_host', ['192.168.1.123', 'newip']) ) @@ -131,7 +123,6 @@ def test_add_host(self): ''' hosts.add_host ''' - self.__clean_hosts() self.assertTrue( self.run_function('hosts.add_host', ['192.168.1.123', 'newip']) ) @@ -145,7 +136,6 @@ def test_add_host(self): self.assertEqual(len(self.run_function('hosts.list_hosts')), 11) def test_rm_host(self): - self.__clean_hosts() self.assertTrue( self.run_function('hosts.has_pair', ['127.0.0.1', 'myname']) ) @@ -168,7 +158,7 @@ def test_add_host_formatting(self): # use an empty one so we can prove the syntax of the entries # being added by the hosts module self.__clear_hosts() - with salt.utils.files.fopen(HFN, 'w'): + with salt.utils.files.fopen(self.hosts_file, 'w'): pass self.assertTrue( @@ -207,7 +197,7 @@ def test_add_host_formatting(self): ) # now read the lines and ensure they're formatted correctly - with salt.utils.files.fopen(HFN, 'r') as fp_: + with salt.utils.files.fopen(self.hosts_file, 'r') as fp_: lines = salt.utils.stringutils.to_unicode(fp_.read()).splitlines() self.assertEqual(lines, [ '192.168.1.3\t\thost3.fqdn.com', diff --git a/tests/integration/modules/test_jinja.py b/tests/integration/modules/test_jinja.py new file mode 100644 index 000000000000..832c0785e7c3 --- /dev/null +++ b/tests/integration/modules/test_jinja.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +''' +Test the jinja module +''' + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals +import os + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.case import ModuleCase +from tests.support.helpers import requires_system_grains + +# Import Salt libs +import salt.utils.json +import salt.utils.files +import salt.utils.yaml + + +class TestModulesJinja(ModuleCase): + ''' + Test the jinja map module + ''' + + def _path(self, name, absolute=False): + path = os.path.join('modules', 'jinja', name) + if absolute: + return os.path.join(RUNTIME_VARS.BASE_FILES, path) + else: + return path + + def test_import_json(self): + json_file = 'osarchmap.json' + ret = self.run_function('jinja.import_json', [self._path(json_file)]) + with salt.utils.files.fopen(self._path(json_file, absolute=True)) as fh_: + self.assertDictEqual(salt.utils.json.load(fh_), ret) + + def test_import_yaml(self): + yaml_file = 'defaults.yaml' + ret = self.run_function('jinja.import_yaml', [self._path(yaml_file)]) + with salt.utils.files.fopen(self._path(yaml_file, absolute=True)) as fh_: + self.assertDictEqual(salt.utils.yaml.safe_load(fh_), ret) + + @requires_system_grains + def test_load_map(self, grains): + ret = self.run_function('jinja.load_map', [self._path('map.jinja'), 'template']) + + with salt.utils.files.fopen(self._path('defaults.yaml', absolute=True)) as fh_: + defaults = salt.utils.yaml.safe_load(fh_) + with salt.utils.files.fopen(self._path('osarchmap.json', absolute=True)) as fh_: + osarchmap = salt.utils.json.load(fh_) + with salt.utils.files.fopen(self._path('osfamilymap.yaml', absolute=True)) as fh_: + osfamilymap = salt.utils.yaml.safe_load(fh_) + with salt.utils.files.fopen(self._path('osmap.yaml', absolute=True)) as fh_: + osmap = salt.utils.yaml.safe_load(fh_) + with salt.utils.files.fopen(self._path('osfingermap.yaml', absolute=True)) as fh_: + osfingermap = salt.utils.yaml.safe_load(fh_) + + self.assertEqual(ret.get('arch'), osarchmap.get(grains['osarch'], {}).get('arch')) + self.assertEqual( + ret.get('config'), + osfingermap.get( + grains['osfinger'], {} + ).get('config', osmap.get( + grains['os'], {} + ).get('config', osfamilymap.get( + grains['os_family'], {} + ).get('config', defaults.get( + 'template' + ).get('config')))) + ) diff --git a/tests/integration/modules/test_linux_acl.py b/tests/integration/modules/test_linux_acl.py index c6139c33f6e9..bb7f2af16036 100644 --- a/tests/integration/modules/test_linux_acl.py +++ b/tests/integration/modules/test_linux_acl.py @@ -6,8 +6,8 @@ import shutil # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import TMP from tests.support.mixins import AdaptedConfigurationTestCaseMixin from tests.support.helpers import skip_if_binaries_missing @@ -27,18 +27,18 @@ class LinuxAclModuleTest(ModuleCase, AdaptedConfigurationTestCaseMixin): ''' def setUp(self): # Blindly copied from tests.integration.modules.file; Refactoring? - self.myfile = os.path.join(TMP, 'myfile') + self.myfile = os.path.join(RUNTIME_VARS.TMP, 'myfile') with salt.utils.files.fopen(self.myfile, 'w+') as fp: fp.write('Hello\n') - self.mydir = os.path.join(TMP, 'mydir/isawesome') + self.mydir = os.path.join(RUNTIME_VARS.TMP, 'mydir/isawesome') if not os.path.isdir(self.mydir): # left behind... Don't fail because of this! os.makedirs(self.mydir) - self.mysymlink = os.path.join(TMP, 'mysymlink') + self.mysymlink = os.path.join(RUNTIME_VARS.TMP, 'mysymlink') if os.path.islink(self.mysymlink): os.remove(self.mysymlink) os.symlink(self.myfile, self.mysymlink) - self.mybadsymlink = os.path.join(TMP, 'mybadsymlink') + self.mybadsymlink = os.path.join(RUNTIME_VARS.TMP, 'mybadsymlink') if os.path.islink(self.mybadsymlink): os.remove(self.mybadsymlink) os.symlink('/nonexistentpath', self.mybadsymlink) diff --git a/tests/integration/modules/test_mac_keychain.py b/tests/integration/modules/test_mac_keychain.py index 763191f812b8..2f16901e20ab 100644 --- a/tests/integration/modules/test_mac_keychain.py +++ b/tests/integration/modules/test_mac_keychain.py @@ -9,7 +9,7 @@ # Import Salt Testing Libs from tests.support.case import ModuleCase -from tests.support.paths import FILES +from tests.support.runtests import RUNTIME_VARS from tests.support.helpers import destructiveTest, skip_if_not_root # Import Salt Libs @@ -18,16 +18,6 @@ # Import 3rd-party libs from salt.ext import six -CERT = os.path.join( - FILES, - 'file', - 'base', - 'certs', - 'salttest.p12' -) -CERT_ALIAS = 'Salt Test' -PASSWD = 'salttest' - @destructiveTest @skip_if_not_root @@ -35,6 +25,19 @@ class MacKeychainModuleTest(ModuleCase): ''' Integration tests for the mac_keychain module ''' + + @classmethod + def setUpClass(cls): + cls.cert = os.path.join( + RUNTIME_VARS.FILES, + 'file', + 'base', + 'certs', + 'salttest.p12' + ) + cls.cert_alias = 'Salt Test' + cls.passwd = 'salttest' + def setUp(self): ''' Sets up the test requirements @@ -54,53 +57,53 @@ def tearDown(self): ''' # Remove the salttest cert, if left over. certs_list = self.run_function('keychain.list_certs') - if CERT_ALIAS in certs_list: - self.run_function('keychain.uninstall', [CERT_ALIAS]) + if self.cert_alias in certs_list: + self.run_function('keychain.uninstall', [self.cert_alias]) def test_mac_keychain_install(self): ''' Tests that attempts to install a certificate ''' - install_cert = self.run_function('keychain.install', [CERT, PASSWD]) + install_cert = self.run_function('keychain.install', [self.cert, self.passwd]) self.assertTrue(install_cert) # check to ensure the cert was installed certs_list = self.run_function('keychain.list_certs') - self.assertIn(CERT_ALIAS, certs_list) + self.assertIn(self.cert_alias, certs_list) def test_mac_keychain_uninstall(self): ''' Tests that attempts to uninstall a certificate ''' - self.run_function('keychain.install', [CERT, PASSWD]) + self.run_function('keychain.install', [self.cert, self.passwd]) certs_list = self.run_function('keychain.list_certs') - if CERT_ALIAS not in certs_list: - self.run_function('keychain.uninstall', [CERT_ALIAS]) + if self.cert_alias not in certs_list: + self.run_function('keychain.uninstall', [self.cert_alias]) self.skipTest('Failed to install keychain') # uninstall cert - self.run_function('keychain.uninstall', [CERT_ALIAS]) + self.run_function('keychain.uninstall', [self.cert_alias]) certs_list = self.run_function('keychain.list_certs') # check to ensure the cert was uninstalled try: - self.assertNotIn(CERT_ALIAS, six.text_type(certs_list)) + self.assertNotIn(self.cert_alias, six.text_type(certs_list)) except CommandExecutionError: - self.run_function('keychain.uninstall', [CERT_ALIAS]) + self.run_function('keychain.uninstall', [self.cert_alias]) def test_mac_keychain_get_friendly_name(self): ''' Test that attempts to get friendly name of a cert ''' - self.run_function('keychain.install', [CERT, PASSWD]) + self.run_function('keychain.install', [self.cert, self.passwd]) certs_list = self.run_function('keychain.list_certs') - if CERT_ALIAS not in certs_list: - self.run_function('keychain.uninstall', [CERT_ALIAS]) + if self.cert_alias not in certs_list: + self.run_function('keychain.uninstall', [self.cert_alias]) self.skipTest('Failed to install keychain') - get_name = self.run_function('keychain.get_friendly_name', [CERT, PASSWD]) - self.assertEqual(get_name, CERT_ALIAS) + get_name = self.run_function('keychain.get_friendly_name', [self.cert, self.passwd]) + self.assertEqual(get_name, self.cert_alias) def test_mac_keychain_get_default_keychain(self): ''' diff --git a/tests/integration/modules/test_mac_pkgutil.py b/tests/integration/modules/test_mac_pkgutil.py index b02e65388a7c..d5502c30e690 100644 --- a/tests/integration/modules/test_mac_pkgutil.py +++ b/tests/integration/modules/test_mac_pkgutil.py @@ -8,8 +8,8 @@ import os # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import TMP from tests.support.helpers import destructiveTest, skip_if_not_root # Import Salt libs @@ -18,7 +18,6 @@ TEST_PKG_URL = 'https://distfiles.macports.org/MacPorts/MacPorts-2.3.4-10.11-ElCapitan.pkg' TEST_PKG_NAME = 'org.macports.MacPorts' -TEST_PKG = os.path.join(TMP, 'MacPorts-2.3.4-10.11-ElCapitan.pkg') @skip_if_not_root @@ -27,6 +26,10 @@ class MacPkgutilModuleTest(ModuleCase): Validate the mac_pkgutil module ''' + @classmethod + def setUpClass(cls): + cls.test_pkg = os.path.join(RUNTIME_VARS.TMP, 'MacPorts-2.3.4-10.11-ElCapitan.pkg') + def setUp(self): ''' Get current settings @@ -81,11 +84,11 @@ def test_install_forget(self): self.run_function('pkgutil.is_installed', [TEST_PKG_NAME])) # Download the package - self.run_function('cp.get_url', [TEST_PKG_URL, TEST_PKG]) + self.run_function('cp.get_url', [TEST_PKG_URL, self.test_pkg]) # Test install self.assertTrue( - self.run_function('pkgutil.install', [TEST_PKG, TEST_PKG_NAME])) + self.run_function('pkgutil.install', [self.test_pkg, TEST_PKG_NAME])) self.assertIn( 'Unsupported scheme', self.run_function('pkgutil.install', ['ftp://test', 'spongebob'])) diff --git a/tests/integration/modules/test_mac_xattr.py b/tests/integration/modules/test_mac_xattr.py index 5fc98878045e..22d2771f1cd7 100644 --- a/tests/integration/modules/test_mac_xattr.py +++ b/tests/integration/modules/test_mac_xattr.py @@ -8,22 +8,24 @@ import os # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import TMP # Import Salt libs import salt.utils.path import salt.utils.platform -TEST_FILE = os.path.join(TMP, 'xattr_test_file.txt') -NO_FILE = os.path.join(TMP, 'xattr_no_file.txt') - class MacXattrModuleTest(ModuleCase): ''' Validate the mac_xattr module ''' + @classmethod + def setUpClass(cls): + cls.test_file = os.path.join(RUNTIME_VARS.TMP, 'xattr_test_file.txt') + cls.no_file = os.path.join(RUNTIME_VARS.TMP, 'xattr_no_file.txt') + def setUp(self): ''' Create test file for testing extended attributes @@ -34,84 +36,84 @@ def setUp(self): if not salt.utils.path.which('xattr'): self.skipTest('Test requires xattr binary') - self.run_function('file.touch', [TEST_FILE]) + self.run_function('file.touch', [self.test_file]) def tearDown(self): ''' Clean up test file ''' - if os.path.exists(TEST_FILE): - os.remove(TEST_FILE) + if os.path.exists(self.test_file): + os.remove(self.test_file) def test_list_no_xattr(self): ''' Make sure there are no attributes ''' # Clear existing attributes - self.assertTrue(self.run_function('xattr.clear', [TEST_FILE])) + self.assertTrue(self.run_function('xattr.clear', [self.test_file])) # Test no attributes - self.assertEqual(self.run_function('xattr.list', [TEST_FILE]), {}) + self.assertEqual(self.run_function('xattr.list', [self.test_file]), {}) # Test file not found - self.assertEqual(self.run_function('xattr.list', [NO_FILE]), - 'ERROR: File not found: {0}'.format(NO_FILE)) + self.assertEqual(self.run_function('xattr.list', [self.no_file]), + 'ERROR: File not found: {0}'.format(self.no_file)) def test_write(self): ''' Write an attribute ''' # Clear existing attributes - self.assertTrue(self.run_function('xattr.clear', [TEST_FILE])) + self.assertTrue(self.run_function('xattr.clear', [self.test_file])) # Write some attributes self.assertTrue( self.run_function('xattr.write', - [TEST_FILE, 'spongebob', 'squarepants'])) + [self.test_file, 'spongebob', 'squarepants'])) self.assertTrue( self.run_function('xattr.write', - [TEST_FILE, 'squidward', 'plankton'])) + [self.test_file, 'squidward', 'plankton'])) self.assertTrue( self.run_function('xattr.write', - [TEST_FILE, 'crabby', 'patty'])) + [self.test_file, 'crabby', 'patty'])) # Test that they were actually added self.assertEqual( - self.run_function('xattr.list', [TEST_FILE]), + self.run_function('xattr.list', [self.test_file]), {'spongebob': 'squarepants', 'squidward': 'plankton', 'crabby': 'patty'}) # Test file not found self.assertEqual( - self.run_function('xattr.write', [NO_FILE, 'patrick', 'jellyfish']), - 'ERROR: File not found: {0}'.format(NO_FILE)) + self.run_function('xattr.write', [self.no_file, 'patrick', 'jellyfish']), + 'ERROR: File not found: {0}'.format(self.no_file)) def test_read(self): ''' Test xattr.read ''' # Clear existing attributes - self.assertTrue(self.run_function('xattr.clear', [TEST_FILE])) + self.assertTrue(self.run_function('xattr.clear', [self.test_file])) # Write an attribute self.assertTrue( self.run_function('xattr.write', - [TEST_FILE, 'spongebob', 'squarepants'])) + [self.test_file, 'spongebob', 'squarepants'])) # Read the attribute self.assertEqual( - self.run_function('xattr.read', [TEST_FILE, 'spongebob']), + self.run_function('xattr.read', [self.test_file, 'spongebob']), 'squarepants') # Test file not found self.assertEqual( - self.run_function('xattr.read', [NO_FILE, 'spongebob']), - 'ERROR: File not found: {0}'.format(NO_FILE)) + self.run_function('xattr.read', [self.no_file, 'spongebob']), + 'ERROR: File not found: {0}'.format(self.no_file)) # Test attribute not found self.assertEqual( - self.run_function('xattr.read', [TEST_FILE, 'patrick']), + self.run_function('xattr.read', [self.test_file, 'patrick']), 'ERROR: Attribute not found: patrick') def test_delete(self): @@ -119,36 +121,36 @@ def test_delete(self): Test xattr.delete ''' # Clear existing attributes - self.assertTrue(self.run_function('xattr.clear', [TEST_FILE])) + self.assertTrue(self.run_function('xattr.clear', [self.test_file])) # Write some attributes self.assertTrue( self.run_function('xattr.write', - [TEST_FILE, 'spongebob', 'squarepants'])) + [self.test_file, 'spongebob', 'squarepants'])) self.assertTrue( self.run_function('xattr.write', - [TEST_FILE, 'squidward', 'plankton'])) + [self.test_file, 'squidward', 'plankton'])) self.assertTrue( self.run_function('xattr.write', - [TEST_FILE, 'crabby', 'patty'])) + [self.test_file, 'crabby', 'patty'])) # Delete an attribute self.assertTrue( - self.run_function('xattr.delete', [TEST_FILE, 'squidward'])) + self.run_function('xattr.delete', [self.test_file, 'squidward'])) # Make sure it was actually deleted self.assertEqual( - self.run_function('xattr.list', [TEST_FILE]), + self.run_function('xattr.list', [self.test_file]), {'spongebob': 'squarepants', 'crabby': 'patty'}) # Test file not found self.assertEqual( - self.run_function('xattr.delete', [NO_FILE, 'spongebob']), - 'ERROR: File not found: {0}'.format(NO_FILE)) + self.run_function('xattr.delete', [self.no_file, 'spongebob']), + 'ERROR: File not found: {0}'.format(self.no_file)) # Test attribute not found self.assertEqual( - self.run_function('xattr.delete', [TEST_FILE, 'patrick']), + self.run_function('xattr.delete', [self.test_file, 'patrick']), 'ERROR: Attribute not found: patrick') def test_clear(self): @@ -156,22 +158,22 @@ def test_clear(self): Test xattr.clear ''' # Clear existing attributes - self.assertTrue(self.run_function('xattr.clear', [TEST_FILE])) + self.assertTrue(self.run_function('xattr.clear', [self.test_file])) # Write some attributes self.assertTrue( self.run_function('xattr.write', - [TEST_FILE, 'spongebob', 'squarepants'])) + [self.test_file, 'spongebob', 'squarepants'])) self.assertTrue( self.run_function('xattr.write', - [TEST_FILE, 'squidward', 'plankton'])) + [self.test_file, 'squidward', 'plankton'])) self.assertTrue( self.run_function('xattr.write', - [TEST_FILE, 'crabby', 'patty'])) + [self.test_file, 'crabby', 'patty'])) # Test Clear - self.assertTrue(self.run_function('xattr.clear', [TEST_FILE])) + self.assertTrue(self.run_function('xattr.clear', [self.test_file])) # Test file not found - self.assertEqual(self.run_function('xattr.clear', [NO_FILE]), - 'ERROR: File not found: {0}'.format(NO_FILE)) + self.assertEqual(self.run_function('xattr.clear', [self.no_file]), + 'ERROR: File not found: {0}'.format(self.no_file)) diff --git a/tests/integration/modules/test_network.py b/tests/integration/modules/test_network.py index 0738fbb97d6d..09176b317a30 100644 --- a/tests/integration/modules/test_network.py +++ b/tests/integration/modules/test_network.py @@ -23,7 +23,7 @@ def test_network_ping(self): network.ping ''' ret = self.run_function('network.ping', [URL]) - exp_out = ['ping', URL, 'ttl', 'time'] + exp_out = ['ping', URL, 'ms', 'time'] for out in exp_out: self.assertIn(out, ret.lower()) diff --git a/tests/integration/modules/test_pillar.py b/tests/integration/modules/test_pillar.py index 1591a6db0840..f23d3d519b38 100644 --- a/tests/integration/modules/test_pillar.py +++ b/tests/integration/modules/test_pillar.py @@ -4,8 +4,8 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import TMP_STATE_TREE class PillarModuleTest(ModuleCase): @@ -33,7 +33,7 @@ def test_issue_5449_report_actual_file_roots_in_pillar(self): the pillar back to the minion. ''' self.assertIn( - TMP_STATE_TREE, + RUNTIME_VARS.TMP_STATE_TREE, self.run_function('pillar.data')['master']['file_roots']['base'] ) @@ -47,7 +47,7 @@ def test_ext_cmd_yaml(self): def test_issue_5951_actual_file_roots_in_opts(self): self.assertIn( - TMP_STATE_TREE, + RUNTIME_VARS.TMP_STATE_TREE, self.run_function('pillar.data')['ext_pillar_opts']['file_roots']['base'] ) diff --git a/tests/integration/modules/test_pip.py b/tests/integration/modules/test_pip.py index 77962b798261..5092a109ab2c 100644 --- a/tests/integration/modules/test_pip.py +++ b/tests/integration/modules/test_pip.py @@ -14,9 +14,10 @@ import tempfile # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.unit import skipIf -from tests.support.paths import TMP +from tests.support.helpers import patched_environ # Import salt libs import salt.utils.files @@ -30,29 +31,20 @@ class PipModuleTest(ModuleCase): def setUp(self): super(PipModuleTest, self).setUp() - - # Restore the environ - def cleanup_environ(environ): - os.environ.clear() - os.environ.update(environ) - - self.addCleanup(cleanup_environ, os.environ.copy()) - - self.venv_test_dir = tempfile.mkdtemp(dir=TMP) + self.venv_test_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # Remove the venv test directory self.addCleanup(shutil.rmtree, self.venv_test_dir, ignore_errors=True) self.venv_dir = os.path.join(self.venv_test_dir, 'venv') - for key in os.environ.copy(): - if key.startswith('PIP_'): - os.environ.pop(key) self.pip_temp = os.path.join(self.venv_test_dir, '.pip-temp') - # Remove the pip-temp directory - self.addCleanup(shutil.rmtree, self.pip_temp, ignore_errors=True) if not os.path.isdir(self.pip_temp): os.makedirs(self.pip_temp) - os.environ['PIP_SOURCE_DIR'] = os.environ['PIP_BUILD_DIR'] = '' - for item in ('venv_dir', 'venv_test_dir', 'pip_temp'): - self.addCleanup(delattr, self, item) + self.patched_environ = patched_environ( + PIP_SOURCE_DIR='', + PIP_BUILD_DIR='', + __cleanup__=[k for k in os.environ if k.startswith('PIP_')] + ) + self.patched_environ.__enter__() + self.addCleanup(self.patched_environ.__exit__) def _create_virtualenv(self, path): ''' @@ -68,7 +60,22 @@ def _create_virtualenv(self, path): if salt.utils.is_windows(): python = os.path.join(sys.real_prefix, os.path.basename(sys.executable)) else: - python = os.path.join(sys.real_prefix, 'bin', os.path.basename(sys.executable)) + python_binary_names = [ + 'python{}.{}'.format(*sys.version_info), + 'python{}'.format(*sys.version_info), + 'python' + ] + for binary_name in python_binary_names: + python = os.path.join(sys.real_prefix, 'bin', binary_name) + if os.path.exists(python): + break + else: + self.fail( + 'Couldn\'t find a python binary name under \'{}\' matching: {}'.format( + os.path.join(sys.real_prefix, 'bin'), + python_binary_names + ) + ) # We're running off a virtualenv, and we don't want to create a virtualenv off of # a virtualenv kwargs = {'python': python} diff --git a/tests/integration/modules/test_pkg.py b/tests/integration/modules/test_pkg.py index 20099215362f..43bf130c872b 100644 --- a/tests/integration/modules/test_pkg.py +++ b/tests/integration/modules/test_pkg.py @@ -12,10 +12,13 @@ destructiveTest, requires_network, requires_salt_modules, - requires_system_grains) + requires_system_grains, + skip_if_not_root) +from tests.support.unit import skipIf # Import Salt libs from salt.utils import six +import salt.utils.path import salt.utils.pkg import salt.utils.platform @@ -27,16 +30,16 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): @classmethod @requires_system_grains - def setUpClass(cls, grains): + def setUpClass(cls, grains): # pylint: disable=arguments-differ cls.ctx = {} cls.pkg = 'figlet' if salt.utils.platform.is_windows(): cls.pkg = 'putty' - elif salt.utils.platform.is_darwin(): - cls.pkg = 'wget' if int(grains['osmajorrelease']) >= 13 else 'htop' elif grains['os_family'] == 'RedHat': cls.pkg = 'units' + @skip_if_not_root + @requires_salt_modules('pkg.refresh_db') def setUp(self): if 'refresh' not in self.ctx: self.run_function('pkg.refresh_db') @@ -95,7 +98,6 @@ def test_mod_del_repo(self, grains): self.assertIsInstance(ret, dict, 'The \'pkg.get_repo\' command did not return the excepted dictionary. ' 'Output:\n{}'.format(ret)) - self.assertEqual( ret['uri'], uri, @@ -225,8 +227,6 @@ def test_refresh_db(self, grains): if not isinstance(ret, dict): self.skipTest('Upstream repo did not return coherent results. Skipping test.') self.assertNotEqual(ret, {}) - elif grains['os_family'] == 'Suse': - self.assertNotEqual(ret, {}) for source, state in ret.items(): self.assertIn(state, (True, False, None)) @@ -255,6 +255,10 @@ def test_pkg_info(self, grains): keys = ret.keys() self.assertIn('less', keys) self.assertIn('zypper', keys) + else: + ret = self.run_function(func, [self.pkg]) + keys = ret.keys() + self.assertIn(self.pkg, keys) @destructiveTest @requires_network() @@ -264,6 +268,9 @@ def test_pkg_upgrade_has_pending_upgrades(self, grains): ''' Test running a system upgrade when there are packages that need upgrading ''' + if grains['os'] == 'Arch': + self.skipTest('Arch moved to Python 3.8 and we\'re not ready for it yet') + func = 'pkg.upgrade' # First make sure that an up-to-date copy of the package db is available @@ -326,6 +333,7 @@ def test_pkg_upgrade_has_pending_upgrades(self, grains): self.assertNotEqual(ret, {}) @destructiveTest + @skipIf(salt.utils.platform.is_darwin(), 'The jenkins user is equivalent to root on mac, causing the test to be unrunnable') @requires_salt_modules('pkg.remove', 'pkg.latest_version') @requires_system_grains def test_pkg_latest_version(self, grains): @@ -347,8 +355,11 @@ def test_pkg_latest_version(self, grains): elif grains['os_family'] == 'Suse': cmd_pkg = self.run_function('cmd.run', ['zypper info {0}'.format(self.pkg)]) elif grains['os_family'] == 'MacOS': - self.skipTest('TODO the following command needs to be run as a non-root user') - #cmd_pkg = self.run_function('cmd.run', ['brew info {0}'.format(self.pkg)]) + brew_bin = salt.utils.path.which('brew') + mac_user = self.run_function('file.get_user', [brew_bin]) + if mac_user == 'root': + self.skipTest('brew cannot run as root, try a user in {}'.format(os.listdir('/Users/'))) + cmd_pkg = self.run_function('cmd.run', ['brew info {0}'.format(self.pkg)], run_as=mac_user) else: self.skipTest('TODO: test not configured for {}'.format(grains['os_family'])) pkg_latest = self.run_function('pkg.latest_version', [self.pkg]) diff --git a/tests/integration/modules/test_saltutil.py b/tests/integration/modules/test_saltutil.py index e3c218509809..9f5bdfa012ff 100644 --- a/tests/integration/modules/test_saltutil.py +++ b/tests/integration/modules/test_saltutil.py @@ -10,9 +10,9 @@ import textwrap # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.helpers import flaky -from tests.support.paths import TMP_PILLAR_TREE from tests.support.unit import skipIf # Import Salt Libs @@ -65,6 +65,12 @@ def test_wheel_with_kwarg(self): self.assertIn('priv', ret['return']) +class SyncGrainsTest(ModuleCase): + def test_sync_grains(self): + ret = self.run_function('saltutil.sync_grains') + self.assertEqual(ret, []) + + class SaltUtilSyncModuleTest(ModuleCase): ''' Testcase for the saltutil sync execution module @@ -98,6 +104,7 @@ def test_sync_all(self): 'states': [], 'sdb': [], 'proxymodules': [], + 'executors': [], 'output': [], 'thorium': [], 'serializers': []} @@ -121,6 +128,7 @@ def test_sync_all_whitelist(self): 'states': [], 'sdb': [], 'proxymodules': [], + 'executors': [], 'output': [], 'thorium': [], 'serializers': []} @@ -147,6 +155,7 @@ def test_sync_all_blacklist(self): 'states': [], 'sdb': [], 'proxymodules': [], + 'executors': [], 'output': [], 'thorium': [], 'serializers': []} @@ -163,6 +172,7 @@ def test_sync_all_blacklist_and_whitelist(self): 'beacons': [], 'utils': [], 'returners': [], + 'executors': [], 'modules': [], 'renderers': [], 'log_handlers': [], @@ -194,12 +204,12 @@ def test_pillar_refresh(self): pre_pillar = self.run_function('pillar.raw') self.assertNotIn(pillar_key, pre_pillar.get(pillar_key, 'didnotwork')) - with salt.utils.files.fopen(os.path.join(TMP_PILLAR_TREE, 'add_pillar.sls'), 'w') as fp: + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'add_pillar.sls'), 'w') as fp: fp.write(salt.utils.stringutils.to_str( '{0}: itworked'.format(pillar_key) )) - with salt.utils.files.fopen(os.path.join(TMP_PILLAR_TREE, 'top.sls'), 'w') as fp: + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls'), 'w') as fp: fp.write(textwrap.dedent('''\ base: '*': @@ -224,5 +234,5 @@ def test_pillar_refresh(self): self.assertIn(pillar_key, post_pillar.get(pillar_key, 'didnotwork')) def tearDown(self): - for filename in os.listdir(TMP_PILLAR_TREE): - os.remove(os.path.join(TMP_PILLAR_TREE, filename)) + for filename in os.listdir(RUNTIME_VARS.TMP_PILLAR_TREE): + os.remove(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, filename)) diff --git a/tests/integration/modules/test_service.py b/tests/integration/modules/test_service.py index 6128a13b5316..e2cc16343eae 100644 --- a/tests/integration/modules/test_service.py +++ b/tests/integration/modules/test_service.py @@ -134,8 +134,8 @@ def test_service_disable_doesnot_exist(self): self.assertTrue(self.run_function('service.disable', [srv_name])) elif self.run_function('grains.item', ['os'])['os'] == 'Debian' and \ self.run_function('grains.item', ['osmajorrelease'])['osmajorrelease'] < 9 and systemd: - # currently disabling a service via systemd that does not exist - # on Debian 8 results in a True return code + # currently disabling a service via systemd that does not exist + # on Debian 8 results in a True return code self.assertTrue(self.run_function('service.disable', [srv_name])) else: try: diff --git a/tests/integration/modules/test_ssh.py b/tests/integration/modules/test_ssh.py index 34bbcb966a90..b3af96cda036 100644 --- a/tests/integration/modules/test_ssh.py +++ b/tests/integration/modules/test_ssh.py @@ -9,8 +9,8 @@ import shutil # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import FILES, TMP from tests.support.helpers import skip_if_binaries_missing # Import salt libs @@ -20,9 +20,6 @@ # Import 3rd-party libs from tornado.httpclient import HTTPClient -SUBSALT_DIR = os.path.join(TMP, 'subsalt') -AUTHORIZED_KEYS = os.path.join(SUBSALT_DIR, 'authorized_keys') -KNOWN_HOSTS = os.path.join(SUBSALT_DIR, 'known_hosts') GITHUB_FINGERPRINT = '9d:38:5b:83:a9:17:52:92:56:1a:5e:c4:d4:81:8e:0a:ca:51:a2:64:f1:74:20:11:2e:f8:8a:c3:a1:39:49:8f' @@ -41,6 +38,12 @@ class SSHModuleTest(ModuleCase): ''' Test the ssh module ''' + @classmethod + def setUpClass(cls): + cls.subsalt_dir = os.path.join(RUNTIME_VARS.TMP, 'subsalt') + cls.authorized_keys = os.path.join(cls.subsalt_dir, 'authorized_keys') + cls.known_hosts = os.path.join(cls.subsalt_dir, 'known_hosts') + def setUp(self): ''' Set up the ssh module tests @@ -48,10 +51,10 @@ def setUp(self): if not check_status(): self.skipTest('External source, github.com is down') super(SSHModuleTest, self).setUp() - if not os.path.isdir(SUBSALT_DIR): - os.makedirs(SUBSALT_DIR) + if not os.path.isdir(self.subsalt_dir): + os.makedirs(self.subsalt_dir) - ssh_raw_path = os.path.join(FILES, 'ssh', 'raw') + ssh_raw_path = os.path.join(RUNTIME_VARS.FILES, 'ssh', 'raw') with salt.utils.files.fopen(ssh_raw_path) as fd: self.key = fd.read().strip() @@ -59,8 +62,8 @@ def tearDown(self): ''' Tear down the ssh module tests ''' - if os.path.isdir(SUBSALT_DIR): - shutil.rmtree(SUBSALT_DIR) + if os.path.isdir(self.subsalt_dir): + shutil.rmtree(self.subsalt_dir) super(SSHModuleTest, self).tearDown() del self.key @@ -69,12 +72,12 @@ def test_auth_keys(self): test ssh.auth_keys ''' shutil.copyfile( - os.path.join(FILES, 'ssh', 'authorized_keys'), - AUTHORIZED_KEYS) + os.path.join(RUNTIME_VARS.FILES, 'ssh', 'authorized_keys'), + self.authorized_keys) user = 'root' if salt.utils.platform.is_windows(): user = 'Administrator' - ret = self.run_function('ssh.auth_keys', [user, AUTHORIZED_KEYS]) + ret = self.run_function('ssh.auth_keys', [user, self.authorized_keys]) self.assertEqual(len(list(ret.items())), 1) # exactly one key is found key_data = list(ret.items())[0][1] try: @@ -97,9 +100,9 @@ def test_bad_enctype(self): invalid key entry in authorized_keys ''' shutil.copyfile( - os.path.join(FILES, 'ssh', 'authorized_badkeys'), - AUTHORIZED_KEYS) - ret = self.run_function('ssh.auth_keys', ['root', AUTHORIZED_KEYS]) + os.path.join(RUNTIME_VARS.FILES, 'ssh', 'authorized_badkeys'), + self.authorized_keys) + ret = self.run_function('ssh.auth_keys', ['root', self.authorized_keys]) # The authorized_badkeys file contains a key with an invalid ssh key # encoding (dsa-sha2-nistp256 instead of ecdsa-sha2-nistp256) @@ -113,10 +116,10 @@ def test_get_known_host_entries(self): Check that known host information is returned from ~/.ssh/config ''' shutil.copyfile( - os.path.join(FILES, 'ssh', 'known_hosts'), - KNOWN_HOSTS) + os.path.join(RUNTIME_VARS.FILES, 'ssh', 'known_hosts'), + self.known_hosts) arg = ['root', 'github.com'] - kwargs = {'config': KNOWN_HOSTS} + kwargs = {'config': self.known_hosts} ret = self.run_function('ssh.get_known_host_entries', arg, **kwargs)[0] try: self.assertEqual(ret['enc'], 'ssh-rsa') @@ -151,7 +154,7 @@ def test_check_known_host_add(self): Check known hosts by its fingerprint. File needs to be updated ''' arg = ['root', 'github.com'] - kwargs = {'fingerprint': GITHUB_FINGERPRINT, 'config': KNOWN_HOSTS} + kwargs = {'fingerprint': GITHUB_FINGERPRINT, 'config': self.known_hosts} ret = self.run_function('ssh.check_known_host', arg, **kwargs) self.assertEqual(ret, 'add') @@ -160,10 +163,10 @@ def test_check_known_host_update(self): ssh.check_known_host update verification ''' shutil.copyfile( - os.path.join(FILES, 'ssh', 'known_hosts'), - KNOWN_HOSTS) + os.path.join(RUNTIME_VARS.FILES, 'ssh', 'known_hosts'), + self.known_hosts) arg = ['root', 'github.com'] - kwargs = {'config': KNOWN_HOSTS} + kwargs = {'config': self.known_hosts} # wrong fingerprint ret = self.run_function('ssh.check_known_host', arg, **dict(kwargs, fingerprint='aa:bb:cc:dd')) @@ -178,10 +181,10 @@ def test_check_known_host_exists(self): Verify check_known_host_exists ''' shutil.copyfile( - os.path.join(FILES, 'ssh', 'known_hosts'), - KNOWN_HOSTS) + os.path.join(RUNTIME_VARS.FILES, 'ssh', 'known_hosts'), + self.known_hosts) arg = ['root', 'github.com'] - kwargs = {'config': KNOWN_HOSTS} + kwargs = {'config': self.known_hosts} # wrong fingerprint ret = self.run_function('ssh.check_known_host', arg, **dict(kwargs, fingerprint=GITHUB_FINGERPRINT)) @@ -196,15 +199,15 @@ def test_rm_known_host(self): ssh.rm_known_host ''' shutil.copyfile( - os.path.join(FILES, 'ssh', 'known_hosts'), - KNOWN_HOSTS) + os.path.join(RUNTIME_VARS.FILES, 'ssh', 'known_hosts'), + self.known_hosts) arg = ['root', 'github.com'] - kwargs = {'config': KNOWN_HOSTS, 'key': self.key} + kwargs = {'config': self.known_hosts, 'key': self.key} # before removal ret = self.run_function('ssh.check_known_host', arg, **kwargs) self.assertEqual(ret, 'exists') # remove - self.run_function('ssh.rm_known_host', arg, config=KNOWN_HOSTS) + self.run_function('ssh.rm_known_host', arg, config=self.known_hosts) # after removal ret = self.run_function('ssh.check_known_host', arg, **kwargs) self.assertEqual(ret, 'add') @@ -215,7 +218,7 @@ def test_set_known_host(self): ''' # add item ret = self.run_function('ssh.set_known_host', ['root', 'github.com'], - config=KNOWN_HOSTS) + config=self.known_hosts) try: self.assertEqual(ret['status'], 'updated') self.assertEqual(ret['old'], None) @@ -228,7 +231,7 @@ def test_set_known_host(self): ) # check that item does exist ret = self.run_function('ssh.get_known_host_entries', ['root', 'github.com'], - config=KNOWN_HOSTS)[0] + config=self.known_hosts)[0] try: self.assertEqual(ret['fingerprint'], GITHUB_FINGERPRINT) except AssertionError as exc: @@ -239,7 +242,7 @@ def test_set_known_host(self): ) # add the same item once again ret = self.run_function('ssh.set_known_host', ['root', 'github.com'], - config=KNOWN_HOSTS) + config=self.known_hosts) try: self.assertEqual(ret['status'], 'exists') except AssertionError as exc: diff --git a/tests/integration/modules/test_state.py b/tests/integration/modules/test_state.py index 987b79a11006..f0cef1bbe0f9 100644 --- a/tests/integration/modules/test_state.py +++ b/tests/integration/modules/test_state.py @@ -12,10 +12,10 @@ import time # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.helpers import with_tempdir, flaky +from tests.support.helpers import with_tempdir from tests.support.unit import skipIf -from tests.support.paths import BASE_FILES, TMP, TMP_PILLAR_TREE, TMP_STATE_TREE from tests.support.mixins import SaltReturnAssertsMixin # Import Salt libs @@ -82,9 +82,8 @@ def _reline(path, ending=DEFAULT_ENDING): for line in lines: fhw.write(line + ending) - destpath = os.path.join(BASE_FILES, 'testappend', 'firstif') - _reline(destpath) - destpath = os.path.join(BASE_FILES, 'testappend', 'secondif') + destpath = os.path.join(RUNTIME_VARS.BASE_FILES, 'testappend', 'firstif') + destpath = os.path.join(RUNTIME_VARS.BASE_FILES, 'testappend', 'secondif') _reline(destpath) cls.TIMEOUT = 600 if salt.utils.platform.is_windows() else 10 @@ -93,7 +92,7 @@ def test_show_highstate(self): state.show_highstate ''' high = self.run_function('state.show_highstate') - destpath = os.path.join(TMP, 'testfile') + destpath = os.path.join(RUNTIME_VARS.TMP, 'testfile') self.assertTrue(isinstance(high, dict)) self.assertTrue(destpath in high) self.assertEqual(high[destpath]['__env__'], 'base') @@ -123,7 +122,8 @@ def test_show_states_missing_sls(self): Test state.show_states with a sls file defined in a top file is missing ''' - with salt.utils.files.fopen(os.path.join(TMP_STATE_TREE, 'top.sls'), 'w') as top_file: + topfile = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'top.sls') + with salt.utils.files.fopen(topfile, 'w') as top_file: top_file.write(textwrap.dedent('''\ base: '*': @@ -338,7 +338,7 @@ def test_issue_1876_syntax_error(self): - text: foo ''' - testfile = os.path.join(TMP, 'issue-1876') + testfile = os.path.join(RUNTIME_VARS.TMP, 'issue-1876') sls = self.run_function('state.sls', mods='issue-1876') self.assertIn( @@ -364,7 +364,7 @@ def test_issue_1879_too_simple_contains_check(self): expected = os.linesep.join(new_contents) expected += os.linesep - testfile = os.path.join(TMP, 'issue-1879') + testfile = os.path.join(RUNTIME_VARS.TMP, 'issue-1879') # Delete if exiting if os.path.isfile(testfile): os.unlink(testfile) @@ -413,7 +413,7 @@ def test_issue_1879_too_simple_contains_check(self): os.unlink(testfile) def test_include(self): - tempdir = tempfile.mkdtemp(dir=TMP) + tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, tempdir, ignore_errors=True) pillar = {} for path in ('include-test', 'to-include-test', 'exclude-test'): @@ -425,7 +425,7 @@ def test_include(self): self.assertFalse(os.path.isfile(pillar['exclude-test'])) def test_exclude(self): - tempdir = tempfile.mkdtemp(dir=TMP) + tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, tempdir, ignore_errors=True) pillar = {} for path in ('include-test', 'exclude-test', 'to-include-test'): @@ -439,7 +439,7 @@ def test_exclude(self): @skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed') def test_issue_2068_template_str(self): venv_dir = os.path.join( - TMP, 'issue-2068-template-str' + RUNTIME_VARS.TMP, 'issue-2068-template-str' ) try: @@ -815,9 +815,9 @@ def test_requisites_require_any(self): 'result': True, 'changes': True, }, - 'cmd_|-C_|-/bin/false_|-run': { + 'cmd_|-C_|-$(which false)_|-run': { '__run_num__': 1, - 'comment': 'Command "/bin/false" run', + 'comment': 'Command "$(which false)" run', 'result': False, 'changes': True, }, @@ -1366,7 +1366,7 @@ def test_requisites_use_no_state_module(self): self.assertEqual(descr['comment'], 'onlyif condition is false') def test_get_file_from_env_in_top_match(self): - tgt = os.path.join(TMP, 'prod-cheese-file') + tgt = os.path.join(RUNTIME_VARS.TMP, 'prod-cheese-file') try: ret = self.run_function( 'state.highstate', minion_tgt='sub_minion' @@ -1764,7 +1764,7 @@ def test_retry_option_success(self): ''' test a state with the retry option that should return True immedietly (i.e. no retries) ''' - testfile = os.path.join(TMP, 'retry_file') + testfile = os.path.join(RUNTIME_VARS.TMP, 'retry_file_option_success') state_run = self.run_function( 'state.sls', mods='retry.retry_success' @@ -1773,29 +1773,35 @@ def test_retry_option_success(self): retry_state = 'file_|-file_test_|-{0}_|-exists'.format(testfile) self.assertNotIn('Attempt', state_run[retry_state]['comment']) - def run_create(self): + def run_create(self, testfile): ''' helper function to wait 30 seconds and then create the temp retry file ''' - testfile = os.path.join(TMP, 'retry_file') + # Wait for the requisite stae 'file_test_a' to complete before creating + # test_file + while True: + if os.path.exists(testfile + '_a'): + break + time.sleep(1) time.sleep(30) with salt.utils.files.fopen(testfile, 'a'): pass - @flaky def test_retry_option_eventual_success(self): ''' test a state with the retry option that should return True after at least 4 retry attmempt but never run 15 attempts ''' - testfile = os.path.join(TMP, 'retry_file') - create_thread = threading.Thread(target=self.run_create) + testfile = os.path.join(RUNTIME_VARS.TMP, 'retry_file_eventual_success') + assert not os.path.exists(testfile + '_a') + assert not os.path.exists(testfile) + create_thread = threading.Thread(target=self.run_create, args=(testfile,)) create_thread.start() state_run = self.run_function( 'state.sls', mods='retry.retry_success2' ) - retry_state = 'file_|-file_test_|-{0}_|-exists'.format(testfile) + retry_state = 'file_|-file_test_b_|-{0}_|-exists'.format(testfile) self.assertIn('Attempt 1:', state_run[retry_state]['comment']) self.assertIn('Attempt 2:', state_run[retry_state]['comment']) self.assertIn('Attempt 3:', state_run[retry_state]['comment']) @@ -1861,7 +1867,7 @@ def test_state_nonbase_environment(self): test state.sls with saltenv using a nonbase environment with a salt source ''' - filename = os.path.join(TMP, 'nonbase_env') + filename = os.path.join(RUNTIME_VARS.TMP, 'nonbase_env') try: ret = self.run_function( 'state.sls', @@ -1912,11 +1918,11 @@ def _add_runtime_pillar(self, pillar): helper class to add pillar data at runtime ''' import salt.utils.yaml - with salt.utils.files.fopen(os.path.join(TMP_PILLAR_TREE, + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'pillar.sls'), 'w') as fp: salt.utils.yaml.safe_dump(pillar, fp) - with salt.utils.files.fopen(os.path.join(TMP_PILLAR_TREE, 'top.sls'), 'w') as fp: + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls'), 'w') as fp: fp.write(textwrap.dedent('''\ base: '*': @@ -1932,7 +1938,7 @@ def test_state_sls_id_test(self): to true in pillar data ''' self._add_runtime_pillar(pillar={'test': True}) - testfile = os.path.join(TMP, 'testfile') + testfile = os.path.join(RUNTIME_VARS.TMP, 'testfile') comment = 'The file {0} is set to be changed\nNote: No changes made, actual changes may\nbe different due to other states.'.format(testfile) ret = self.run_function('state.sls', ['core']) @@ -1945,7 +1951,7 @@ def test_state_sls_id_test_state_test_post_run(self): test state.sls_id when test is set to true post the state already being run previously ''' - file_name = os.path.join(TMP, 'testfile') + file_name = os.path.join(RUNTIME_VARS.TMP, 'testfile') ret = self.run_function('state.sls', ['core']) for key, val in ret.items(): self.assertEqual(val['comment'], @@ -1965,7 +1971,7 @@ def test_state_sls_id_test_true(self): ''' test state.sls_id when test=True is passed as arg ''' - file_name = os.path.join(TMP, 'testfile') + file_name = os.path.join(RUNTIME_VARS.TMP, 'testfile') ret = self.run_function('state.sls', ['core'], test=True) for key, val in ret.items(): self.assertEqual( @@ -1978,7 +1984,7 @@ def test_state_sls_id_test_true_post_run(self): test state.sls_id when test is set to true as an arg post the state already being run previously ''' - file_name = os.path.join(TMP, 'testfile') + file_name = os.path.join(RUNTIME_VARS.TMP, 'testfile') ret = self.run_function('state.sls', ['core']) for key, val in ret.items(): self.assertEqual(val['comment'], @@ -1999,7 +2005,7 @@ def test_state_sls_id_test_false_pillar_true(self): arg and minion_state_test is set to True. Should return test=False. ''' - file_name = os.path.join(TMP, 'testfile') + file_name = os.path.join(RUNTIME_VARS.TMP, 'testfile') self._add_runtime_pillar(pillar={'test': True}) ret = self.run_function('state.sls', ['core'], test=False) @@ -2008,6 +2014,7 @@ def test_state_sls_id_test_false_pillar_true(self): 'File {0} updated'.format(file_name)) self.assertEqual(val['changes']['diff'], 'New file') + @skipIf(six.PY3 and salt.utils.platform.is_darwin(), 'Test is broken on macosx and PY3') def test_issue_30161_unless_and_onlyif_together(self): ''' test cmd.run using multiple unless options where the first cmd in the @@ -2021,29 +2028,29 @@ def test_issue_30161_unless_and_onlyif_together(self): # command in the state. If the comment reads "unless condition is true", or similar, # then the unless state run bailed out after the first unless command succeeded, # which is the bug we're regression testing for. - _expected = {'file_|-unless_false_onlyif_false_|-{0}{1}test.txt_|-managed'.format(TMP, os.path.sep): + _expected = {'file_|-unless_false_onlyif_false_|-{0}{1}test.txt_|-managed'.format(RUNTIME_VARS.TMP, os.path.sep): {'comment': 'onlyif condition is false\nunless condition is false', - 'name': '{0}{1}test.txt'.format(TMP, os.path.sep), + 'name': '{0}{1}test.txt'.format(RUNTIME_VARS.TMP, os.path.sep), 'skip_watch': True, 'changes': {}, 'result': True}, - 'file_|-unless_false_onlyif_true_|-{0}{1}test.txt_|-managed'.format(TMP, os.path.sep): + 'file_|-unless_false_onlyif_true_|-{0}{1}test.txt_|-managed'.format(RUNTIME_VARS.TMP, os.path.sep): {'comment': 'Empty file', 'pchanges': {}, - 'name': '{0}{1}test.txt'.format(TMP, os.path.sep), + 'name': '{0}{1}test.txt'.format(RUNTIME_VARS.TMP, os.path.sep), 'start_time': '18:10:20.341753', 'result': True, - 'changes': {'new': 'file {0}{1}test.txt created'.format(TMP, os.path.sep)}}, - 'file_|-unless_true_onlyif_false_|-{0}{1}test.txt_|-managed'.format(TMP, os.path.sep): + 'changes': {'new': 'file {0}{1}test.txt created'.format(RUNTIME_VARS.TMP, os.path.sep)}}, + 'file_|-unless_true_onlyif_false_|-{0}{1}test.txt_|-managed'.format(RUNTIME_VARS.TMP, os.path.sep): {'comment': 'onlyif condition is false\nunless condition is true', - 'name': '{0}{1}test.txt'.format(TMP, os.path.sep), + 'name': '{0}{1}test.txt'.format(RUNTIME_VARS.TMP, os.path.sep), 'start_time': '18:10:22.936446', 'skip_watch': True, 'changes': {}, 'result': True}, - 'file_|-unless_true_onlyif_true_|-{0}{1}test.txt_|-managed'.format(TMP, os.path.sep): + 'file_|-unless_true_onlyif_true_|-{0}{1}test.txt_|-managed'.format(RUNTIME_VARS.TMP, os.path.sep): {'comment': 'onlyif condition is true\nunless condition is true', - 'name': '{0}{1}test.txt'.format(TMP, os.path.sep), + 'name': '{0}{1}test.txt'.format(RUNTIME_VARS.TMP, os.path.sep), 'skip_watch': True, 'changes': {}, 'result': True}} @@ -2080,18 +2087,18 @@ def test_state_sls_unicode_characters_cmd_output(self): self.assertEqual(_expected, ret[key]['changes']['stdout']) def tearDown(self): - rm_files = [os.path.join(TMP, 'nonbase_env'), - os.path.join(TMP, 'testfile'), - os.path.join(TMP, 'test.txt'), - os.path.join(TMP_STATE_TREE, 'top.sls')] + rm_files = [os.path.join(RUNTIME_VARS.TMP, 'nonbase_env'), + os.path.join(RUNTIME_VARS.TMP, 'testfile'), + os.path.join(RUNTIME_VARS.TMP, 'test.txt'), + os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'top.sls')] for file_ in rm_files: if os.path.isfile(file_): os.remove(file_) # remove old pillar data - for filename in os.listdir(TMP_PILLAR_TREE): - os.remove(os.path.join(TMP_PILLAR_TREE, filename)) + for filename in os.listdir(RUNTIME_VARS.TMP_PILLAR_TREE): + os.remove(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, filename)) self.run_function('saltutil.refresh_pillar') self.run_function('test.sleep', [5]) @@ -2110,3 +2117,16 @@ def test_state_sls_integer_name(self): self.assertEqual(state_run[state_id]['comment'], 'Success!') self.assertTrue(state_run[state_id]['result']) + + def test_state_sls_lazyloader_allows_recursion(self): + ''' + This tests that referencing dunders like __salt__ work + context: https://github.com/saltstack/salt/pull/51499 + ''' + state_run = self.run_function('state.sls', mods='issue-51499') + + state_id = 'test_|-always-passes_|-foo_|-succeed_without_changes' + self.assertIn(state_id, state_run) + self.assertEqual(state_run[state_id]['comment'], + 'Success!') + self.assertTrue(state_run[state_id]['result']) diff --git a/tests/integration/modules/test_supervisord.py b/tests/integration/modules/test_supervisord.py index 6a508f8d0877..0c215469f30b 100644 --- a/tests/integration/modules/test_supervisord.py +++ b/tests/integration/modules/test_supervisord.py @@ -7,9 +7,9 @@ import subprocess # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.unit import skipIf -from tests.support.paths import TMP # Import salt libs import salt.utils.path @@ -29,7 +29,7 @@ class SupervisordModuleTest(ModuleCase): def setUp(self): super(SupervisordModuleTest, self).setUp() - self.venv_test_dir = os.path.join(TMP, 'supervisortests') + self.venv_test_dir = os.path.join(RUNTIME_VARS.TMP, 'supervisortests') self.venv_dir = os.path.join(self.venv_test_dir, 'venv') self.supervisor_sock = os.path.join(self.venv_dir, 'supervisor.sock') diff --git a/tests/integration/modules/test_system.py b/tests/integration/modules/test_system.py index f4bf307d6e99..7beeb17754b2 100644 --- a/tests/integration/modules/test_system.py +++ b/tests/integration/modules/test_system.py @@ -444,6 +444,7 @@ def test_set_system_time(self): In order for this test to pass, time sync must be disabled for the VM in the hypervisor ''' + self.run_function('service.stop', ['w32time']) try: current_time = datetime.datetime.now().strftime('%H:%M:%S') test_time = '10:55' @@ -453,6 +454,7 @@ def test_set_system_time(self): self.assertEqual(new_time, test_time) finally: self.run_function('system.set_system_time', [current_time]) + self.run_function('service.start', ['w32time']) def test_get_system_date(self): ''' @@ -473,6 +475,7 @@ def test_set_system_date(self): In order for this test to pass, time sync must be disabled for the VM in the hypervisor ''' + self.run_function('service.stop', ['w32time']) try: # If the test still fails, the hypervisor may be maintaining time # sync @@ -482,3 +485,4 @@ def test_set_system_date(self): self.assertEqual(new_date, '2018/03/25') finally: self.run_function('system.set_system_date', [current_date]) + self.run_function('service.start', ['w32time']) diff --git a/tests/integration/modules/test_virtualenv_mod.py b/tests/integration/modules/test_virtualenv_mod.py index 23e9d98fbf8d..52bd3fc3f8b8 100644 --- a/tests/integration/modules/test_virtualenv_mod.py +++ b/tests/integration/modules/test_virtualenv_mod.py @@ -6,9 +6,9 @@ import tempfile # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.unit import skipIf -from tests.support.paths import TMP # Import salt libs import salt.utils.path @@ -22,7 +22,7 @@ class VirtualenvModuleTest(ModuleCase): ''' def setUp(self): super(VirtualenvModuleTest, self).setUp() - self.venv_test_dir = tempfile.mkdtemp(dir=TMP) + self.venv_test_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.venv_dir = os.path.join(self.venv_test_dir, 'venv') def test_create_defaults(self): diff --git a/tests/integration/modules/test_win_pkg.py b/tests/integration/modules/test_win_pkg.py index 8e20bfba3f00..00924684b0b5 100644 --- a/tests/integration/modules/test_win_pkg.py +++ b/tests/integration/modules/test_win_pkg.py @@ -15,9 +15,6 @@ import salt.utils.files import salt.utils.platform -REPO_DIR = os.path.join(RUNTIME_VARS.FILES, 'file', 'base', 'win', 'repo-ng') -CURL = os.path.join(REPO_DIR, 'curl.sls') - @skipIf(not salt.utils.platform.is_windows(), 'windows test only') class WinPKGTest(ModuleCase): @@ -28,6 +25,15 @@ class WinPKGTest(ModuleCase): specific windows software respository tests while using the win_pkg module. ''' + @classmethod + def setUpClass(cls): + cls.repo_dir = os.path.join(RUNTIME_VARS.FILES, 'file', 'base', 'win', 'repo-ng') + cls.curl_sls_path = os.path.join(cls.repo_dir, 'curl.sls') + + def tearDown(self): + if os.path.isfile(self.curl_sls_path): + os.remove(self.curl_sls_path) + @destructiveTest def test_adding_removing_pkg_sls(self): ''' @@ -57,7 +63,7 @@ def _check_pkg(pkgs, check_refresh, exists=True): _check_pkg(pkgs, 2) # now add new sls - with salt.utils.files.fopen(CURL, 'w') as fp_: + with salt.utils.files.fopen(self.curl_sls_path, 'w') as fp_: fp_.write(textwrap.dedent(''' curl: '7.46.0': @@ -78,13 +84,9 @@ def _check_pkg(pkgs, check_refresh, exists=True): # now check if curl is also in cache and repo query pkgs.append('curl') for pkg in pkgs: - self.assertIn(pkg + '.sls', os.listdir(REPO_DIR)) + self.assertIn(pkg + '.sls', os.listdir(self.repo_dir)) _check_pkg(pkgs, 3) # remove curl sls and check its not in cache and repo query - os.remove(CURL) + os.remove(self.curl_sls_path) _check_pkg(['curl'], 2, exists=False) - - def tearDown(self): - if os.path.isfile(CURL): - os.remove(CURL) diff --git a/tests/integration/netapi/rest_tornado/test_app.py b/tests/integration/netapi/rest_tornado/test_app.py index 2c348a679d65..891a4d97094d 100644 --- a/tests/integration/netapi/rest_tornado/test_app.py +++ b/tests/integration/netapi/rest_tornado/test_app.py @@ -2,6 +2,7 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +import os import time import threading @@ -36,6 +37,11 @@ def mod_opts(self): @skipIf(HAS_ZMQ_IOLOOP is False, 'PyZMQ version must be >= 14.0.1 to run these tests.') @skipIf(StrictVersion(zmq.__version__) < StrictVersion('14.0.1'), 'PyZMQ must be >= 14.0.1 to run these tests.') class TestSaltAPIHandler(_SaltnadoIntegrationTestCase): + + def setUp(self): + super(TestSaltAPIHandler, self).setUp() + os.environ['ASYNC_TEST_TIMEOUT'] = '300' + def get_app(self): urls = [('/', saltnado.SaltAPIHandler)] @@ -335,7 +341,7 @@ def test_simple_local_runner_post(self): headers={'Content-Type': self.content_type_map['json'], saltnado.AUTH_TOKEN_HEADER: self.token['token']}, connect_timeout=30, - request_timeout=30, + request_timeout=300, ) response_obj = salt.utils.json.loads(response.body) self.assertEqual(len(response_obj['return']), 1) diff --git a/tests/integration/netapi/test_client.py b/tests/integration/netapi/test_client.py index 503bbaf335f5..98408b4656c0 100644 --- a/tests/integration/netapi/test_client.py +++ b/tests/integration/netapi/test_client.py @@ -6,7 +6,7 @@ import time # Import Salt Testing libs -from tests.support.paths import TMP_CONF_DIR +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase, skipIf # Import Salt libs @@ -25,14 +25,14 @@ def setUp(self): ''' Set up a NetapiClient instance ''' - opts = salt.config.client_config(os.path.join(TMP_CONF_DIR, 'master')) + opts = salt.config.client_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master')) self.netapi = salt.netapi.NetapiClient(opts) def tearDown(self): del self.netapi def test_local(self): - low = {'client': 'local', 'tgt': '*', 'fun': 'test.ping'} + low = {'client': 'local', 'tgt': '*', 'fun': 'test.ping', 'timeout': 300} low.update(self.eauth_creds) ret = self.netapi.run(low) @@ -44,7 +44,7 @@ def test_local(self): self.assertEqual(ret, {'minion': True, 'sub_minion': True}) def test_local_batch(self): - low = {'client': 'local_batch', 'tgt': '*', 'fun': 'test.ping'} + low = {'client': 'local_batch', 'tgt': '*', 'fun': 'test.ping', 'timeout': 300} low.update(self.eauth_creds) ret = self.netapi.run(low) diff --git a/tests/integration/pillar/test_git_pillar.py b/tests/integration/pillar/test_git_pillar.py index 9ade88840bf9..034fc5870a38 100644 --- a/tests/integration/pillar/test_git_pillar.py +++ b/tests/integration/pillar/test_git_pillar.py @@ -81,7 +81,6 @@ requires_system_grains, skip_if_not_root ) -from tests.support.mock import NO_MOCK, NO_MOCK_REASON from tests.support.unit import skipIf # Import Salt libs @@ -517,7 +516,6 @@ def test_all_saltenvs_base(self): @destructiveTest -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(_windows_or_mac(), 'minion is windows or mac') @skip_if_not_root @skipIf(not HAS_GITPYTHON, 'GitPython >= {0} required'.format(GITPYTHON_MINVER)) @@ -532,7 +530,6 @@ class TestGitPythonSSH(GitPillarSSHTestBase, GitPythonMixin): passphrase = PASSWORD -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(_windows_or_mac(), 'minion is windows or mac') @skip_if_not_root @skipIf(not HAS_GITPYTHON, 'GitPython >= {0} required'.format(GITPYTHON_MINVER)) @@ -545,7 +542,6 @@ class TestGitPythonHTTP(GitPillarHTTPTestBase, GitPythonMixin): pass -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(_windows_or_mac(), 'minion is windows or mac') @skip_if_not_root @skipIf(not HAS_GITPYTHON, 'GitPython >= {0} required'.format(GITPYTHON_MINVER)) @@ -581,7 +577,6 @@ def setUpClass(cls): @destructiveTest -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(_windows_or_mac(), 'minion is windows or mac') @skip_if_not_root @skipIf(not HAS_PYGIT2, 'pygit2 >= {0} and libgit2 >= {1} required'.format(PYGIT2_MINVER, LIBGIT2_MINVER)) @@ -1867,7 +1862,6 @@ def test_all_saltenvs_base(self, grains): self.assertEqual(ret, expected) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(_windows_or_mac(), 'minion is windows or mac') @skip_if_not_root @skipIf(not HAS_PYGIT2, 'pygit2 >= {0} and libgit2 >= {1} required'.format(PYGIT2_MINVER, LIBGIT2_MINVER)) @@ -2271,7 +2265,6 @@ def test_all_saltenvs_base(self): ) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(_windows_or_mac(), 'minion is windows or mac') @skip_if_not_root @skipIf(not HAS_PYGIT2, 'pygit2 >= {0} and libgit2 >= {1} required'.format(PYGIT2_MINVER, LIBGIT2_MINVER)) diff --git a/tests/integration/proxy/conftest.py b/tests/integration/proxy/conftest.py new file mode 100644 index 000000000000..e482f046c04f --- /dev/null +++ b/tests/integration/proxy/conftest.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +''' + tests.integration.proxy.conftest + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Proxy related fixtures +''' +# pylint: disable=unused-argument,redefined-outer-name + +# Import Python libs +from __future__ import absolute_import, unicode_literals +import os +import logging + +# Import 3rd-party libs +import psutil +import pytest + +log = logging.getLogger(__name__) + + +@pytest.fixture(scope='package', autouse=True) +def session_salt_proxy(request, + session_salt_proxy, + session_proxy_id, + session_master_config): + + stats_key = ' Salt Proxy' + request.session.stats_processes[stats_key] = psutil.Process(session_salt_proxy.pid) + yield session_salt_proxy + # Terminate Proxy now, we want to cleanup it's key before we move along + session_salt_proxy.terminate() + del request.session.stats_processes[stats_key] + + proxy_key_file = os.path.join(session_master_config['pki_dir'], 'minions', session_proxy_id) + log.warning('KEY FILE: %s', proxy_key_file) + if os.path.exists(proxy_key_file): + os.unlink(proxy_key_file) + else: + log.warning('The proxy minion key was not found at %s', proxy_key_file) diff --git a/tests/integration/proxy/test_shell.py b/tests/integration/proxy/test_shell.py index 9ca514b10188..5fbde9a5781c 100644 --- a/tests/integration/proxy/test_shell.py +++ b/tests/integration/proxy/test_shell.py @@ -8,13 +8,19 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals +import sys +import json +import logging # Import Salt Libs +import salt.ext.six as six import salt.utils.json as json # Import salt tests libs from tests.support.case import ShellCase +log = logging.getLogger(__name__) + class ProxyCallerSimpleTestCase(ShellCase): ''' @@ -22,7 +28,11 @@ class ProxyCallerSimpleTestCase(ShellCase): ''' @staticmethod def _load_return(ret): - return json.loads('\n'.join(ret)) + try: + return json.loads('\n'.join(ret)) + except ValueError: + log.warning('Failed to JSON decode: \'%s\'', ret) + six.reraise(*sys.exc_info()) def test_can_it_ping(self): ''' diff --git a/tests/integration/reactor/test_reactor.py b/tests/integration/reactor/test_reactor.py index 596119d34780..5120b43a1229 100644 --- a/tests/integration/reactor/test_reactor.py +++ b/tests/integration/reactor/test_reactor.py @@ -12,19 +12,17 @@ # Import Salt testing libs from tests.support.case import ModuleCase -from tests.support.helpers import flaky from tests.support.mixins import SaltMinionEventAssertsMixin # Import Salt libs import salt.utils.event -class ReactorTest(ModuleCase, SaltMinionEventAssertsMixin): +class ReactorTest(SaltMinionEventAssertsMixin, ModuleCase): ''' Test Salt's reactor system ''' - @flaky def test_ping_reaction(self): ''' Fire an event on the master and ensure diff --git a/tests/integration/returners/test_noop_return.py b/tests/integration/returners/test_noop_return.py index 148be195732e..cf2dac65e118 100644 --- a/tests/integration/returners/test_noop_return.py +++ b/tests/integration/returners/test_noop_return.py @@ -14,7 +14,7 @@ # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.unit import skipIf -from tests.support.helpers import TestsLoggingHandler +from tests.support.helpers import TstSuiteLoggingHandler # Import 3rd-party tests import salt.ext.six as six @@ -27,6 +27,6 @@ class TestEventReturn(ModuleCase): def test_noop_return(self): - with TestsLoggingHandler(format='%(message)s', level=logging.DEBUG) as handler: + with TstSuiteLoggingHandler(format='%(message)s', level=logging.DEBUG) as handler: self.run_function('test.ping') assert any('NOOP_RETURN' in s for s in handler.messages) is True, 'NOOP_RETURN not found in log messages' diff --git a/tests/integration/runners/test_state.py b/tests/integration/runners/test_state.py index a2cd81555ad9..4cdfac9d1f82 100644 --- a/tests/integration/runners/test_state.py +++ b/tests/integration/runners/test_state.py @@ -16,10 +16,10 @@ import threading # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ShellCase from tests.support.helpers import flaky, expensiveTest from tests.support.mock import MagicMock, patch -from tests.support.paths import TMP from tests.support.unit import skipIf # Import Salt Libs @@ -347,7 +347,7 @@ def setUp(self): dir=self.master_d_dir, delete=True, ) - self.base_env = tempfile.mkdtemp(dir=TMP) + self.base_env = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.base_env) self.addCleanup(self.conf.close) for attr in ('timeout', 'master_d_dir', 'conf', 'base_env'): @@ -734,7 +734,7 @@ def test_orchestration_onchanges_and_prereq(self): __reload_config=True).get('jid') finally: try: - os.remove(os.path.join(TMP, 'orch.req_test')) + os.remove(os.path.join(RUNTIME_VARS.TMP, 'orch.req_test')) except OSError: pass diff --git a/tests/integration/scheduler/test_error.py b/tests/integration/scheduler/test_error.py index f045265ce932..062e8004704e 100644 --- a/tests/integration/scheduler/test_error.py +++ b/tests/integration/scheduler/test_error.py @@ -11,11 +11,9 @@ # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin - -# Import Salt Testing Libs from tests.support.mock import MagicMock, patch from tests.support.unit import skipIf -import tests.integration as integration +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.utils.schedule @@ -29,7 +27,7 @@ HAS_CRONITER = False log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(integration.TMP, 'schedule-unit-tests') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') DEFAULT_CONFIG = salt.config.minion_config(None) diff --git a/tests/integration/scheduler/test_eval.py b/tests/integration/scheduler/test_eval.py index 2095e7a7ea9e..b2286b8430d5 100644 --- a/tests/integration/scheduler/test_eval.py +++ b/tests/integration/scheduler/test_eval.py @@ -15,11 +15,9 @@ # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin - -# Import Salt Testing Libs from tests.support.mock import MagicMock, patch from tests.support.unit import skipIf -import tests.integration as integration +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.utils.schedule @@ -34,7 +32,7 @@ HAS_CRONITER = False log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(integration.TMP, 'schedule-unit-tests') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') DEFAULT_CONFIG = salt.config.minion_config(None) diff --git a/tests/integration/scheduler/test_helpers.py b/tests/integration/scheduler/test_helpers.py index 91a8e06f1ed3..55deedc6c232 100644 --- a/tests/integration/scheduler/test_helpers.py +++ b/tests/integration/scheduler/test_helpers.py @@ -9,10 +9,8 @@ # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin - -# Import Salt Testing Libs from tests.support.mock import MagicMock, patch -import tests.integration as integration +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.utils.schedule @@ -21,7 +19,7 @@ from salt.modules.test import ping as ping log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(integration.TMP, 'schedule-unit-tests') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') DEFAULT_CONFIG = salt.config.minion_config(None) diff --git a/tests/integration/scheduler/test_maxrunning.py b/tests/integration/scheduler/test_maxrunning.py index 063e6e2dc39c..64e19ea6430e 100644 --- a/tests/integration/scheduler/test_maxrunning.py +++ b/tests/integration/scheduler/test_maxrunning.py @@ -11,10 +11,8 @@ # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin - -# Import Salt Testing Libs from tests.support.mock import MagicMock, patch -import tests.integration as integration +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.utils.schedule @@ -28,7 +26,7 @@ HAS_CRONITER = False log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(integration.TMP, 'schedule-unit-tests') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') DEFAULT_CONFIG = salt.config.minion_config(None) diff --git a/tests/integration/scheduler/test_postpone.py b/tests/integration/scheduler/test_postpone.py index 568f59fb2235..e6912e7b9061 100644 --- a/tests/integration/scheduler/test_postpone.py +++ b/tests/integration/scheduler/test_postpone.py @@ -12,10 +12,8 @@ # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin - -# Import Salt Testing Libs from tests.support.mock import MagicMock, patch -import tests.integration as integration +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.utils.schedule @@ -23,7 +21,7 @@ from salt.modules.test import ping as ping log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(integration.TMP, 'schedule-unit-tests') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') DEFAULT_CONFIG = salt.config.minion_config(None) diff --git a/tests/integration/scheduler/test_run_job.py b/tests/integration/scheduler/test_run_job.py index c8cdcb6b24b5..8265654149a2 100644 --- a/tests/integration/scheduler/test_run_job.py +++ b/tests/integration/scheduler/test_run_job.py @@ -9,10 +9,8 @@ # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin - -# Import Salt Testing Libs from tests.support.mock import MagicMock, patch -import tests.integration as integration +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.utils.schedule @@ -27,7 +25,7 @@ HAS_CRONITER = False log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(integration.TMP, 'schedule-unit-tests') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') DEFAULT_CONFIG = salt.config.minion_config(None) diff --git a/tests/integration/scheduler/test_skip.py b/tests/integration/scheduler/test_skip.py index da8e3758ca16..ccbe90fdbd87 100644 --- a/tests/integration/scheduler/test_skip.py +++ b/tests/integration/scheduler/test_skip.py @@ -11,10 +11,8 @@ # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin - -# Import Salt Testing Libs from tests.support.mock import MagicMock, patch -import tests.integration as integration +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.utils.schedule @@ -22,7 +20,7 @@ from salt.modules.test import ping as ping log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(integration.TMP, 'schedule-unit-tests') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') DEFAULT_CONFIG = salt.config.minion_config(None) diff --git a/tests/integration/sdb/test_env.py b/tests/integration/sdb/test_env.py index ac26de5a5502..9d03dc0763cd 100644 --- a/tests/integration/sdb/test_env.py +++ b/tests/integration/sdb/test_env.py @@ -8,21 +8,19 @@ # Import Salt Testing libs from tests.support.case import ModuleCase -from tests.support.paths import FILES +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import SaltReturnAssertsMixin # Import salt libs import salt.utils.files -STATE_DIR = os.path.join(FILES, 'file', 'base') - class EnvTestCase(ModuleCase, SaltReturnAssertsMixin): def setUp(self): self.state_name = 'test_sdb_env' self.state_file_name = self.state_name + '.sls' - self.state_file_set_var = os.path.join(STATE_DIR, self.state_file_name) + self.state_file_set_var = os.path.join(RUNTIME_VARS.BASE_FILES, self.state_file_name) with salt.utils.files.fopen(self.state_file_set_var, 'w') as wfh: wfh.write(textwrap.dedent('''\ set some env var: diff --git a/tests/integration/sdb/test_vault.py b/tests/integration/sdb/test_vault.py index 6f0ee8c4bc5c..1a950b9a6256 100644 --- a/tests/integration/sdb/test_vault.py +++ b/tests/integration/sdb/test_vault.py @@ -12,7 +12,7 @@ from tests.support.unit import skipIf from tests.support.case import ModuleCase, ShellCase from tests.support.helpers import destructiveTest, flaky -from tests.support.paths import FILES +from tests.support.runtests import RUNTIME_VARS # Import Salt Libs import salt.utils.path @@ -78,7 +78,7 @@ def setUp(self): self.skipTest('unable to login to vault') ret = self.run_function( 'cmd.retcode', - cmd='/usr/local/bin/vault policy write testpolicy {0}/vault.hcl'.format(FILES), + cmd='/usr/local/bin/vault policy write testpolicy {0}/vault.hcl'.format(RUNTIME_VARS.FILES), env={'VAULT_ADDR': 'http://127.0.0.1:8200'}, ) if ret != 0: diff --git a/tests/integration/shell/test_call.py b/tests/integration/shell/test_call.py index 1778dc2cec2c..b65e974014f9 100644 --- a/tests/integration/shell/test_call.py +++ b/tests/integration/shell/test_call.py @@ -16,9 +16,9 @@ import sys # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ShellCase from tests.support.unit import skipIf -from tests.support.paths import FILES, TMP from tests.support.mixins import ShellCaseCommonTestsMixin from tests.support.helpers import flaky, with_tempfile from tests.integration.utils import testprogram @@ -64,8 +64,8 @@ def test_json_out_indent(self): self.assertIn('"local": true', ''.join(out)) def test_local_sls_call(self): - fileroot = os.path.join(FILES, 'file', 'base') - out = self.run_call('--file-root {0} --local state.sls saltcalllocal'.format(fileroot)) + fileroot = os.path.join(RUNTIME_VARS.FILES, 'file', 'base') + out = self.run_call('--file-root {0} state.sls saltcalllocal'.format(fileroot), local=True) self.assertIn('Name: test.echo', ''.join(out)) self.assertIn('Result: True', ''.join(out)) self.assertIn('hello', ''.join(out)) @@ -78,8 +78,8 @@ def test_local_salt_call(self, name): function twice, see https://github.com/saltstack/salt/pull/49552 ''' def _run_call(cmd): - cmd = '--out=json --local ' + cmd - return salt.utils.json.loads(''.join(self.run_call(cmd)))['local'] + cmd = '--out=json ' + cmd + return salt.utils.json.loads(''.join(self.run_call(cmd, local=True)))['local'] ret = _run_call('state.single file.append name={0} text="foo"'.format(name)) ret = ret[next(iter(ret))] @@ -92,8 +92,7 @@ def _run_call(cmd): contents = fp_.read() assert contents.count('foo') == 1, contents - @skipIf(sys.platform.startswith('win'), 'This test does not apply on Win') - @flaky + @skipIf(salt.utils.platform.is_windows() or salt.utils.platform.is_darwin(), 'This test requires a supported master') def test_user_delete_kw_output(self): ret = self.run_call('-l quiet -d user.delete') assert 'salt \'*\' user.delete name remove=True force=True' in ''.join(ret) @@ -111,8 +110,8 @@ def test_issue_6973_state_highstate_exit_code(self): for this minion, salt-call should exit non-zero if invoked with option --retcode-passthrough ''' - src = os.path.join(FILES, 'file/base/top.sls') - dst = os.path.join(FILES, 'file/base/top.sls.bak') + src = os.path.join(RUNTIME_VARS.BASE_FILES, 'top.sls') + dst = os.path.join(RUNTIME_VARS.BASE_FILES, 'top.sls.bak') shutil.move(src, dst) expected_comment = 'No states found for this minion' try: @@ -156,7 +155,7 @@ def test_syslog_file_not_found(self): test when log_file is set to a syslog file that does not exist ''' old_cwd = os.getcwd() - config_dir = os.path.join(TMP, 'log_file_incorrect') + config_dir = os.path.join(RUNTIME_VARS.TMP, 'log_file_incorrect') if not os.path.isdir(config_dir): os.makedirs(config_dir) @@ -196,7 +195,7 @@ def test_syslog_file_not_found(self): @skipIf(True, 'This test is unreliable. Need to investigate why more deeply.') @flaky def test_issue_15074_output_file_append(self): - output_file_append = os.path.join(TMP, 'issue-15074') + output_file_append = os.path.join(RUNTIME_VARS.TMP, 'issue-15074') try: # Let's create an initial output file with some data _ = self.run_script( @@ -230,7 +229,7 @@ def test_issue_15074_output_file_append(self): @skipIf(True, 'This test is unreliable. Need to investigate why more deeply.') @flaky def test_issue_14979_output_file_permissions(self): - output_file = os.path.join(TMP, 'issue-14979') + output_file = os.path.join(RUNTIME_VARS.TMP, 'issue-14979') with salt.utils.files.set_umask(0o077): try: # Let's create an initial output file with some data @@ -322,7 +321,7 @@ def tearDown(self): Teardown method to remove installed packages ''' user = '' - user_info = self.run_call('--local grains.get username') + user_info = self.run_call(' grains.get username', local=True) if user_info and isinstance(user_info, (list, tuple)) and isinstance(user_info[-1], six.string_types): user = user_info[-1].strip() super(CallTest, self).tearDown() @@ -356,7 +355,7 @@ def test_masterless_highstate(self): ''' ret = self.run_call('state.highstate', local=True) - destpath = os.path.join(TMP, 'testfile') + destpath = os.path.join(RUNTIME_VARS.TMP, 'testfile') exp_out = [' Function: file.managed', ' Result: True', ' ID: {0}'.format(destpath)] diff --git a/tests/integration/shell/test_cloud.py b/tests/integration/shell/test_cloud.py index ba963c4266de..ab9398a6dd99 100644 --- a/tests/integration/shell/test_cloud.py +++ b/tests/integration/shell/test_cloud.py @@ -37,16 +37,16 @@ class SaltCloudCliTest(ShellCase, def test_function_arguments(self): self.assertIn( - 'salt-cloud: error: --function expects two arguments: ' + 'error: --function expects two arguments: ' ' ', - self.run_cloud('--function show_image -h', catch_stderr=True)[1] + '\n'.join(self.run_cloud('--function show_image -h', catch_stderr=True)[1]) ) def test_list_providers_accepts_no_arguments(self): self.assertIn( - 'salt-cloud: error: \'--list-providers\' does not accept any ' + 'error: \'--list-providers\' does not accept any ' 'arguments', - self.run_cloud('--list-providers ec2', catch_stderr=True)[1] + '\n'.join(self.run_cloud('--list-providers ec2', catch_stderr=True)[1]) ) def test_mutually_exclusive_query_options(self): @@ -56,13 +56,15 @@ def test_mutually_exclusive_query_options(self): while True: for idx in range(1, len(test_options)): self.assertIn( - 'salt-cloud: error: The options {0}/{1} are mutually ' + 'error: The options {0}/{1} are mutually ' 'exclusive. Please only choose one of them'.format( test_options[0], test_options[idx] ), - self.run_cloud( - '{0} {1}'.format(test_options[0], test_options[idx]), - catch_stderr=True)[1] + '\n'.join( + self.run_cloud( + '{0} {1}'.format(test_options[0], test_options[idx]), + catch_stderr=True)[1] + ) ) # Remove the first option from the list test_options.pop(0) @@ -81,11 +83,11 @@ def test_mutually_exclusive_list_options(self): ) try: self.assertIn( - 'salt-cloud: error: The options {0}/{1} are mutually ' + 'error: The options {0}/{1} are mutually ' 'exclusive. Please only choose one of them'.format( test_options[0], test_options[idx] ), - output[1] + '\n'.join(output[1]) ) except AssertionError: print(output) diff --git a/tests/integration/shell/test_cp.py b/tests/integration/shell/test_cp.py index 80875e643b20..60901bd4f87f 100644 --- a/tests/integration/shell/test_cp.py +++ b/tests/integration/shell/test_cp.py @@ -14,8 +14,8 @@ import logging # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ShellCase -from tests.support.paths import TMP from tests.support.mixins import ShellCaseCommonTestsMixin # Import salt libs @@ -32,6 +32,8 @@ class CopyTest(ShellCase, ShellCaseCommonTestsMixin): + _call_binary_ = 'salt-cp' + def test_cp_testfile(self): ''' test salt-cp @@ -64,7 +66,7 @@ def quote(arg): continue ret = self.run_salt( '--out yaml {0} file.directory_exists {1}'.format( - quote(minion), TMP + quote(minion), RUNTIME_VARS.TMP ) ) data = salt.utils.yaml.safe_load('\n'.join(ret)) @@ -72,7 +74,7 @@ def quote(arg): ret = self.run_salt( '--out yaml {0} file.makedirs {1}'.format( quote(minion), - TMP + RUNTIME_VARS.TMP ) ) @@ -80,7 +82,7 @@ def quote(arg): self.assertTrue(data[minion]) minion_testfile = os.path.join( - TMP, 'cp_{0}_testfile'.format(idx) + RUNTIME_VARS.TMP, 'cp_{0}_testfile'.format(idx) ) ret = self.run_cp('--out pprint {0} {1} {2}'.format( diff --git a/tests/integration/shell/test_enabled.py b/tests/integration/shell/test_enabled.py index f7a44b38adbe..40ff02404120 100644 --- a/tests/integration/shell/test_enabled.py +++ b/tests/integration/shell/test_enabled.py @@ -7,17 +7,14 @@ # Import Salt Testing libs from tests.support.case import ModuleCase -from tests.support.paths import FILES from tests.support.unit import skipIf +from tests.support.runtests import RUNTIME_VARS # Import Salt Libs import salt.utils.platform import salt.utils.files -STATE_DIR = os.path.join(FILES, 'file', 'base') - - class EnabledTest(ModuleCase): ''' validate the use of shell processing for cmd.run on the salt command line @@ -57,7 +54,7 @@ def test_template_shell(self): ''' state_name = 'template_shell_enabled' state_filename = state_name + '.sls' - state_file = os.path.join(STATE_DIR, state_filename) + state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) enabled_ret = '3 saltines' # the result of running self.cmd in a shell ret_key = 'test_|-shell_enabled_|-{0}_|-configurable_test_state'.format(enabled_ret) @@ -85,7 +82,7 @@ def test_template_default_disabled(self): ''' state_name = 'template_shell_disabled' state_filename = state_name + '.sls' - state_file = os.path.join(STATE_DIR, state_filename) + state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) # the result of running self.cmd not in a shell disabled_ret = ('first second third | wc -l ; export SALTY_VARIABLE=saltines ' diff --git a/tests/integration/shell/test_key.py b/tests/integration/shell/test_key.py index 96d806efcb2a..9f2287fd9f02 100644 --- a/tests/integration/shell/test_key.py +++ b/tests/integration/shell/test_key.py @@ -8,9 +8,10 @@ import textwrap # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ShellCase -from tests.support.paths import TMP from tests.support.mixins import ShellCaseCommonTestsMixin +from tests.support.helpers import skip_if_not_root, destructiveTest # Import 3rd-party libs from salt.ext import six @@ -187,6 +188,8 @@ def test_list_acc(self): expect = ['Accepted Keys:', 'minion', 'sub_minion'] self.assertEqual(data, expect) + @skip_if_not_root + @destructiveTest def test_list_acc_eauth(self): ''' test salt-key -l with eauth @@ -197,6 +200,8 @@ def test_list_acc_eauth(self): self.assertEqual(data, expect) self._remove_user() + @skip_if_not_root + @destructiveTest def test_list_acc_eauth_bad_creds(self): ''' test salt-key -l with eauth and bad creds @@ -224,7 +229,7 @@ def test_list_un(self): self.assertEqual(data, expect) def test_keys_generation(self): - tempdir = tempfile.mkdtemp(dir=TMP) + tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) arg_str = '--gen-keys minibar --gen-keys-dir {0}'.format(tempdir) self.run_key(arg_str) try: @@ -240,22 +245,22 @@ def test_keys_generation(self): shutil.rmtree(tempdir) def test_keys_generation_keysize_minmax(self): - tempdir = tempfile.mkdtemp(dir=TMP) + tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) arg_str = '--gen-keys minion --gen-keys-dir {0}'.format(tempdir) try: data, error = self.run_key( arg_str + ' --keysize=1024', catch_stderr=True ) self.assertIn( - 'salt-key: error: The minimum value for keysize is 2048', error + 'error: The minimum value for keysize is 2048', '\n'.join(error) ) data, error = self.run_key( arg_str + ' --keysize=32769', catch_stderr=True ) self.assertIn( - 'salt-key: error: The maximum value for keysize is 32768', - error + 'error: The maximum value for keysize is 32768', + '\n'.join(error) ) finally: shutil.rmtree(tempdir) diff --git a/tests/integration/shell/test_master.py b/tests/integration/shell/test_master.py index 902bd48219c2..c7c6ada0e87c 100644 --- a/tests/integration/shell/test_master.py +++ b/tests/integration/shell/test_master.py @@ -21,6 +21,8 @@ @skipIf(True, 'This test file should be in an isolated test space.') class MasterTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin): + _call_binary_ = 'salt-master' + def test_exit_status_unknown_user(self): ''' Ensure correct exit status when the master is configured to run as an unknown user. diff --git a/tests/integration/shell/test_matcher.py b/tests/integration/shell/test_matcher.py index 814ab6e74693..326aa9411793 100644 --- a/tests/integration/shell/test_matcher.py +++ b/tests/integration/shell/test_matcher.py @@ -24,6 +24,8 @@ class MatchTest(ShellCase, ShellCaseCommonTestsMixin): Test salt matchers ''' + _call_binary_ = 'salt' + def test_list(self): ''' test salt -L matcher diff --git a/tests/integration/shell/test_minion.py b/tests/integration/shell/test_minion.py index 5cf73b1ea3bb..65630ae4a151 100644 --- a/tests/integration/shell/test_minion.py +++ b/tests/integration/shell/test_minion.py @@ -19,9 +19,9 @@ import tests.integration.utils from tests.support.case import ShellCase from tests.support.unit import skipIf -from tests.support.paths import CODE_DIR from tests.support.mixins import ShellCaseCommonTestsMixin from tests.integration.utils import testprogram +from tests.support.runtests import RUNTIME_VARS # Import 3rd-party libs from salt.ext import six @@ -41,6 +41,8 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix Various integration tests for the salt-minion executable. ''' + _call_binary_ = 'salt-minion' + _test_minions = ( 'minion', 'subminion', @@ -165,7 +167,7 @@ def _initscript_setup(self, minions): init_script = testprogram.TestProgram( name='init:salt-minion', - program=os.path.join(CODE_DIR, 'pkg', 'rpm', 'salt-minion'), + program=os.path.join(RUNTIME_VARS.CODE_DIR, 'pkg', 'rpm', 'salt-minion'), env=cmd_env, ) diff --git a/tests/integration/shell/test_runner.py b/tests/integration/shell/test_runner.py index ac36d64792ee..d0a2f2a5a80d 100644 --- a/tests/integration/shell/test_runner.py +++ b/tests/integration/shell/test_runner.py @@ -29,6 +29,8 @@ class RunTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin) Test the salt-run command ''' + _call_binary_ = 'salt-run' + def _add_user(self): ''' helper method to add user diff --git a/tests/integration/shell/test_syndic.py b/tests/integration/shell/test_syndic.py index 698fe9212e7c..c695ca3226ba 100644 --- a/tests/integration/shell/test_syndic.py +++ b/tests/integration/shell/test_syndic.py @@ -10,6 +10,7 @@ # Import python libs from __future__ import absolute_import import logging +from collections import OrderedDict # Import Salt Testing libs from tests.support.case import ShellCase @@ -22,10 +23,29 @@ import salt.utils.yaml import salt.utils.platform +# Import 3rd-party libs +import psutil +#import pytest + log = logging.getLogger(__name__) -SIGKILL = 9 +#@pytest.fixture(scope='module', autouse=True) +def session_salt_syndic(request, session_salt_master_of_masters, session_salt_syndic): + request.session.stats_processes.update(OrderedDict(( + ('Salt Syndic Master', psutil.Process(session_salt_master_of_masters.pid)), + ('Salt Syndic', psutil.Process(session_salt_syndic.pid)), + )).items()) + yield session_salt_syndic + request.session.stats_processes.pop('Salt Syndic Master') + request.session.stats_processes.pop('Salt Syndic') + + # Stop daemons now(they would be stopped at the end of the test run session + for daemon in (session_salt_syndic, session_salt_master_of_masters): + try: + daemon.terminate() + except Exception as exc: # pylint: disable=broad-except + log.warning('Failed to terminate daemon: %s', daemon.__class__.__name__) class SyndicTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin): @@ -33,6 +53,8 @@ class SyndicTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix Test the salt-syndic command ''' + _call_binary_ = 'salt-syndic' + @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') def test_exit_status_unknown_user(self): ''' diff --git a/tests/integration/ssh/test_grains.py b/tests/integration/ssh/test_grains.py index 841537b4c5c2..ddfb1a2196b1 100644 --- a/tests/integration/ssh/test_grains.py +++ b/tests/integration/ssh/test_grains.py @@ -24,5 +24,7 @@ def test_grains_items(self): grain = 'Linux' if salt.utils.platform.is_darwin(): grain = 'Darwin' + if salt.utils.platform.is_aix(): + grain = 'AIX' self.assertEqual(ret['kernel'], grain) self.assertTrue(isinstance(ret, dict)) diff --git a/tests/integration/ssh/test_state.py b/tests/integration/ssh/test_state.py index 263ec7347a06..e96343a7fffa 100644 --- a/tests/integration/ssh/test_state.py +++ b/tests/integration/ssh/test_state.py @@ -9,9 +9,9 @@ import logging # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import SSHCase from tests.support.helpers import flaky -from tests.support.paths import TMP # Import Salt Libs from salt.ext import six @@ -123,7 +123,7 @@ def test_show_highstate(self): state.show_highstate with salt-ssh ''' high = self.run_function('state.show_highstate') - destpath = os.path.join(TMP, 'testfile') + destpath = os.path.join(RUNTIME_VARS.TMP, 'testfile') self.assertIsInstance(high, dict) self.assertIn(destpath, high) self.assertEqual(high[destpath]['__env__'], 'base') diff --git a/tests/integration/states/test_alternatives.py b/tests/integration/states/test_alternatives.py index b206f7cb57a8..715d3548f6b4 100644 --- a/tests/integration/states/test_alternatives.py +++ b/tests/integration/states/test_alternatives.py @@ -9,9 +9,10 @@ # Import Salt Testing libs from tests.support.case import ModuleCase -from tests.support.unit import skipIf from tests.support.helpers import destructiveTest from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf NO_ALTERNATIVES = False if not os.path.exists('/etc/alternatives'): @@ -22,38 +23,38 @@ class AlterantivesStateTest(ModuleCase, SaltReturnAssertsMixin): @destructiveTest def test_install_set_and_remove(self): - ret = self.run_state('alternatives.set', name='alt-test', path='/bin/true') + ret = self.run_state('alternatives.set', name='alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH) self.assertSaltFalseReturn(ret) ret = self.run_state('alternatives.install', name='alt-test', - link='/usr/local/bin/alt-test', path='/bin/true', priority=50) + link='/usr/local/bin/alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH, priority=50) self.assertSaltTrueReturn(ret) - self.assertSaltStateChangesEqual(ret, '/bin/true', keys=['path']) + self.assertSaltStateChangesEqual(ret, RUNTIME_VARS.SHELL_TRUE_PATH, keys=['path']) ret = self.run_state('alternatives.install', name='alt-test', - link='/usr/local/bin/alt-test', path='/bin/true', priority=50) + link='/usr/local/bin/alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH, priority=50) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual(ret, {}) ret = self.run_state('alternatives.install', name='alt-test', - link='/usr/local/bin/alt-test', path='/bin/false', priority=90) + link='/usr/local/bin/alt-test', path=RUNTIME_VARS.SHELL_FALSE_PATH, priority=90) self.assertSaltTrueReturn(ret) - self.assertSaltStateChangesEqual(ret, '/bin/false', keys=['path']) + self.assertSaltStateChangesEqual(ret, RUNTIME_VARS.SHELL_FALSE_PATH, keys=['path']) - ret = self.run_state('alternatives.set', name='alt-test', path='/bin/false') + ret = self.run_state('alternatives.set', name='alt-test', path=RUNTIME_VARS.SHELL_FALSE_PATH) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual(ret, {}) - ret = self.run_state('alternatives.set', name='alt-test', path='/bin/true') + ret = self.run_state('alternatives.set', name='alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH) self.assertSaltTrueReturn(ret) - self.assertSaltStateChangesEqual(ret, '/bin/true', keys=['path']) + self.assertSaltStateChangesEqual(ret, RUNTIME_VARS.SHELL_TRUE_PATH, keys=['path']) - ret = self.run_state('alternatives.set', name='alt-test', path='/bin/true') + ret = self.run_state('alternatives.set', name='alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual(ret, {}) - ret = self.run_state('alternatives.remove', name='alt-test', path='/bin/true') + ret = self.run_state('alternatives.remove', name='alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH) self.assertSaltTrueReturn(ret) - ret = self.run_state('alternatives.remove', name='alt-test', path='/bin/false') + ret = self.run_state('alternatives.remove', name='alt-test', path=RUNTIME_VARS.SHELL_FALSE_PATH) self.assertSaltTrueReturn(ret) diff --git a/tests/integration/states/test_ansiblegate.py b/tests/integration/states/test_ansiblegate.py index e84712c0f9e7..5d2f0ad5127a 100644 --- a/tests/integration/states/test_ansiblegate.py +++ b/tests/integration/states/test_ansiblegate.py @@ -24,7 +24,7 @@ ) from tests.support.mixins import SaltReturnAssertsMixin from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import skipIf +from tests.support.unit import skipIf, SkipTest @destructiveTest @@ -35,11 +35,13 @@ class AnsiblePlaybooksTestCase(ModuleCase, SaltReturnAssertsMixin): Test ansible.playbooks states ''' + @classmethod @requires_system_grains - def setUp(self, grains=None): + def setUpClass(cls, grains=None): # pylint: disable=arguments-differ if grains.get('os_family') == 'RedHat' and grains.get('osmajorrelease') == 6: - self.skipTest('This test hangs the test suite on RedHat 6. Skipping for now.') + raise SkipTest('This test hangs the test suite on RedHat 6. Skipping for now.') + def setUp(self): priv_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test') data = { 'all': { diff --git a/tests/integration/states/test_archive.py b/tests/integration/states/test_archive.py index 66fbf76d78b4..9ecd414bef7b 100644 --- a/tests/integration/states/test_archive.py +++ b/tests/integration/states/test_archive.py @@ -12,7 +12,7 @@ from tests.support.case import ModuleCase from tests.support.helpers import skip_if_not_root, Webserver from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.paths import FILES +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.utils.files @@ -27,8 +27,6 @@ ARCHIVE_NAME = 'custom.tar.gz' ARCHIVE_TAR_SOURCE = 'http://localhost:{0}/{1}'.format(9999, ARCHIVE_NAME) -ARCHIVE_LOCAL_TAR_SOURCE = 'file://{0}'.format(os.path.join(FILES, 'file', 'base', ARCHIVE_NAME)) -UNTAR_FILE = os.path.join(ARCHIVE_DIR, 'custom/README') ARCHIVE_TAR_HASH = 'md5=7643861ac07c30fe7d2310e9f25ca514' ARCHIVE_TAR_BAD_HASH = 'md5=d41d8cd98f00b204e9800998ecf8427e' ARCHIVE_TAR_HASH_UPPER = 'md5=7643861AC07C30FE7D2310E9F25CA514' @@ -43,6 +41,8 @@ def setUpClass(cls): cls.webserver = Webserver() cls.webserver.start() cls.archive_tar_source = cls.webserver.url('custom.tar.gz') + cls.archive_local_tar_source = 'file://{0}'.format(os.path.join(RUNTIME_VARS.BASE_FILES, ARCHIVE_NAME)) + cls.untar_file = os.path.join(ARCHIVE_DIR, 'custom/README') @classmethod def tearDownClass(cls): @@ -90,7 +90,7 @@ def test_archive_extracted_skip_verify(self): self.skipTest('Timeout talking to local tornado server.') self.assertSaltTrueReturn(ret) - self._check_extracted(UNTAR_FILE) + self._check_extracted(self.untar_file) def test_archive_extracted_with_source_hash(self): ''' @@ -106,7 +106,7 @@ def test_archive_extracted_with_source_hash(self): self.assertSaltTrueReturn(ret) - self._check_extracted(UNTAR_FILE) + self._check_extracted(self.untar_file) @skip_if_not_root def test_archive_extracted_with_root_user_and_group(self): @@ -125,7 +125,7 @@ def test_archive_extracted_with_root_user_and_group(self): self.assertSaltTrueReturn(ret) - self._check_extracted(UNTAR_FILE) + self._check_extracted(self.untar_file) def test_archive_extracted_with_strip_in_options(self): ''' @@ -170,7 +170,7 @@ def test_archive_extracted_without_archive_format(self): self.skipTest('Timeout talking to local tornado server.') self.assertSaltTrueReturn(ret) - self._check_extracted(UNTAR_FILE) + self._check_extracted(self.untar_file) def test_archive_extracted_with_cmd_unzip_false(self): ''' @@ -186,49 +186,49 @@ def test_archive_extracted_with_cmd_unzip_false(self): self.skipTest('Timeout talking to local tornado server.') self.assertSaltTrueReturn(ret) - self._check_extracted(UNTAR_FILE) + self._check_extracted(self.untar_file) def test_local_archive_extracted(self): ''' test archive.extracted with local file ''' ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=ARCHIVE_LOCAL_TAR_SOURCE, archive_format='tar') + source=self.archive_local_tar_source, archive_format='tar') self.assertSaltTrueReturn(ret) - self._check_extracted(UNTAR_FILE) + self._check_extracted(self.untar_file) def test_local_archive_extracted_skip_verify(self): ''' test archive.extracted with local file, bad hash and skip_verify ''' ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=ARCHIVE_LOCAL_TAR_SOURCE, archive_format='tar', + source=self.archive_local_tar_source, archive_format='tar', source_hash=ARCHIVE_TAR_BAD_HASH, skip_verify=True) self.assertSaltTrueReturn(ret) - self._check_extracted(UNTAR_FILE) + self._check_extracted(self.untar_file) def test_local_archive_extracted_with_source_hash(self): ''' test archive.extracted with local file and valid hash ''' ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=ARCHIVE_LOCAL_TAR_SOURCE, archive_format='tar', + source=self.archive_local_tar_source, archive_format='tar', source_hash=ARCHIVE_TAR_HASH) self.assertSaltTrueReturn(ret) - self._check_extracted(UNTAR_FILE) + self._check_extracted(self.untar_file) def test_local_archive_extracted_with_bad_source_hash(self): ''' test archive.extracted with local file and bad hash ''' ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=ARCHIVE_LOCAL_TAR_SOURCE, archive_format='tar', + source=self.archive_local_tar_source, archive_format='tar', source_hash=ARCHIVE_TAR_BAD_HASH) self.assertSaltFalseReturn(ret) @@ -238,12 +238,12 @@ def test_local_archive_extracted_with_uppercase_source_hash(self): test archive.extracted with local file and bad hash ''' ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=ARCHIVE_LOCAL_TAR_SOURCE, archive_format='tar', + source=self.archive_local_tar_source, archive_format='tar', source_hash=ARCHIVE_TAR_HASH_UPPER) self.assertSaltTrueReturn(ret) - self._check_extracted(UNTAR_FILE) + self._check_extracted(self.untar_file) def test_archive_extracted_with_non_base_saltenv(self): ''' @@ -255,4 +255,4 @@ def test_archive_extracted_with_non_base_saltenv(self): pillar={'issue45893.name': ARCHIVE_DIR}, saltenv='prod') self.assertSaltTrueReturn(ret) - self._check_extracted(os.path.join(ARCHIVE_DIR, UNTAR_FILE)) + self._check_extracted(os.path.join(ARCHIVE_DIR, self.untar_file)) diff --git a/tests/integration/states/test_beacon.py b/tests/integration/states/test_beacon.py index d84f52d3648d..a73f1fb74bc9 100644 --- a/tests/integration/states/test_beacon.py +++ b/tests/integration/states/test_beacon.py @@ -17,6 +17,7 @@ def setUp(self): ''' ''' self.run_function('beacons.reset', f_timeout=300) + self.wait_for_all_jobs() def tearDown(self): self.run_function('beacons.reset', f_timeout=300) diff --git a/tests/integration/states/test_cmd.py b/tests/integration/states/test_cmd.py index 778d4200ef34..b1b21c740e09 100644 --- a/tests/integration/states/test_cmd.py +++ b/tests/integration/states/test_cmd.py @@ -9,36 +9,41 @@ import textwrap import tempfile import time +import sys # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import TMP_STATE_TREE from tests.support.mixins import SaltReturnAssertsMixin # Import Salt libs import salt.utils.files import salt.utils.platform -IS_WINDOWS = salt.utils.platform.is_windows() +# Import 3rd-party libs +from salt.ext import six class CMDTest(ModuleCase, SaltReturnAssertsMixin): ''' Validate the cmd state ''' + @classmethod + def setUpClass(cls): + cls.__cmd = 'dir' if salt.utils.platform.is_windows() else 'ls' + def test_run_simple(self): ''' cmd.run ''' - cmd = 'dir' if IS_WINDOWS else 'ls' - ret = self.run_state('cmd.run', name=cmd, cwd=tempfile.gettempdir()) + ret = self.run_state('cmd.run', name=self.__cmd, cwd=tempfile.gettempdir()) self.assertSaltTrueReturn(ret) def test_test_run_simple(self): ''' cmd.run test interface ''' - ret = self.run_state('cmd.run', name='ls', + ret = self.run_state('cmd.run', name=self.__cmd, cwd=tempfile.gettempdir(), test=True) self.assertSaltNoneReturn(ret) @@ -48,12 +53,12 @@ def test_run_hide_output(self): ''' ret = self.run_state( u'cmd.run', - name=u'ls', + name=self.__cmd, hide_output=True) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret[u'changes'][u'stdout'], u'') - self.assertEqual(ret[u'changes'][u'stderr'], u'') + self.assertEqual(ret['changes']['stdout'], '') + self.assertEqual(ret['changes']['stderr'], '') class CMDRunRedirectTest(ModuleCase, SaltReturnAssertsMixin): @@ -63,7 +68,7 @@ class CMDRunRedirectTest(ModuleCase, SaltReturnAssertsMixin): def setUp(self): self.state_name = 'run_redirect' state_filename = self.state_name + '.sls' - self.state_file = os.path.join(TMP_STATE_TREE, state_filename) + self.state_file = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, state_filename) # Create the testfile and release the handle fd, self.test_file = tempfile.mkstemp() @@ -71,7 +76,7 @@ def setUp(self): os.close(fd) except OSError as exc: if exc.errno != errno.EBADF: - raise exc + six.reraise(*sys.exc_info()) # Create the testfile and release the handle fd, self.test_tmp_path = tempfile.mkstemp() @@ -79,7 +84,7 @@ def setUp(self): os.close(fd) except OSError as exc: if exc.errno != errno.EBADF: - raise exc + six.reraise(*sys.exc_info()) super(CMDRunRedirectTest, self).setUp() @@ -181,7 +186,7 @@ class CMDRunWatchTest(ModuleCase, SaltReturnAssertsMixin): def setUp(self): self.state_name = 'run_watch' state_filename = self.state_name + '.sls' - self.state_file = os.path.join(TMP_STATE_TREE, state_filename) + self.state_file = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, state_filename) super(CMDRunWatchTest, self).setUp() def tearDown(self): diff --git a/tests/integration/states/test_docker_container.py b/tests/integration/states/test_docker_container.py index bd49cf4ea09d..f5d822dfe766 100644 --- a/tests/integration/states/test_docker_container.py +++ b/tests/integration/states/test_docker_container.py @@ -4,26 +4,29 @@ ''' # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import errno import functools import logging import os import subprocess +import sys import tempfile # Import Salt Testing Libs -from tests.support.unit import skipIf from tests.support.case import ModuleCase from tests.support.docker import with_network, random_name -from tests.support.paths import FILES, TMP from tests.support.helpers import destructiveTest, with_tempdir from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf # Import Salt Libs import salt.utils.files import salt.utils.network import salt.utils.path from salt.exceptions import CommandExecutionError +from salt.modules.config import DEFAULTS as _config_defaults # Import 3rd-party libs from salt.ext import six @@ -63,11 +66,11 @@ def setUpClass(cls): ''' ''' # Create temp dir - cls.image_build_rootdir = tempfile.mkdtemp(dir=TMP) + cls.image_build_rootdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # Generate image name cls.image = random_name(prefix='salt_busybox_') - script_path = os.path.join(FILES, 'file/base/mkimage-busybox-static') + script_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'mkimage-busybox-static') cmd = [script_path, cls.image_build_rootdir, cls.image] log.debug('Running \'%s\' to build busybox image', ' '.join(cmd)) process = subprocess.Popen( @@ -231,6 +234,33 @@ def test_running_start_false_without_replace(self, name): # it should not have changed self.assertTrue('state' not in ret['changes']) + @with_network(subnet='10.247.197.96/27', create=True) + @container_name + def test_running_no_changes_hostname_network(self, container_name, net): + ''' + Test that changes are not detected when a hostname is specified for a container + on a custom network + ''' + # Create a container + kwargs = { + 'name': container_name, + 'image': self.image, + 'shutdown_timeout': 1, + 'network_mode': net.name, + 'networks': [net.name], + 'hostname': 'foo' + } + ret = self.run_state('docker_container.running', **kwargs) + self.assertSaltTrueReturn(ret) + + ret = self.run_state('docker_container.running', **kwargs) + self.assertSaltTrueReturn(ret) + # Discard the outer dict with the state compiler data to make below + # asserts easier to read/write + ret = ret[next(iter(ret))] + # Should be no changes + self.assertFalse(ret['changes']) + @container_name def test_running_start_false_with_replace(self, name): ''' @@ -743,7 +773,7 @@ def _test_running(self, container_name, *nets): container_netinfo = self.run_function( 'docker.inspect_container', [container_name]).get('NetworkSettings', {}).get('Networks', {})[nets[-1].name] - autoip_keys = self.minion_opts['docker.compare_container_networks']['automatic'] + autoip_keys = _config_defaults['docker.compare_container_networks']['automatic'] autoip_config = { x: y for x, y in six.iteritems(container_netinfo) if x in autoip_keys and y @@ -969,7 +999,7 @@ def _mkstemp(): os.close(fd) except OSError as exc: if exc.errno != errno.EBADF: - raise exc + six.reraise(*sys.exc_info()) else: self.addCleanup(os.remove, ret) return ret @@ -1121,6 +1151,12 @@ def test_run_failhard(self, name): Test to make sure that we fail a state when the container exits with nonzero status if failhard is set to True, and that we don't when it is set to False. + + NOTE: We can't use RUNTIME_VARS.SHELL_FALSE_PATH here because the image + we build on-the-fly here is based on busybox and does not include + /usr/bin/false. Therefore, when the host machine running the tests + has /usr/bin/false, it will not exist in the container and the Docker + Engine API will cause an exception to be raised. ''' ret = self.run_state( 'docker_container.run', diff --git a/tests/integration/states/test_docker_network.py b/tests/integration/states/test_docker_network.py index 8613085ae2cf..8600bbda98a0 100644 --- a/tests/integration/states/test_docker_network.py +++ b/tests/integration/states/test_docker_network.py @@ -12,10 +12,10 @@ import tempfile # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import skipIf from tests.support.case import ModuleCase from tests.support.docker import with_network, random_name -from tests.support.paths import FILES, TMP from tests.support.helpers import destructiveTest, requires_system_grains from tests.support.mixins import SaltReturnAssertsMixin @@ -58,9 +58,8 @@ def container_name(func): ''' def build_image(): # Create temp dir - image_build_rootdir = tempfile.mkdtemp(dir=TMP) - script_path = \ - os.path.join(FILES, 'file/base/mkimage-busybox-static') + image_build_rootdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + script_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'mkimage-busybox-static') cmd = [script_path, image_build_rootdir, IMAGE_NAME] log.debug('Running \'%s\' to build busybox image', ' '.join(cmd)) process = subprocess.Popen( diff --git a/tests/integration/states/test_file.py b/tests/integration/states/test_file.py index db006161ad50..8665d44abece 100644 --- a/tests/integration/states/test_file.py +++ b/tests/integration/states/test_file.py @@ -20,9 +20,9 @@ log = logging.getLogger(__name__) # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.unit import skipIf -from tests.support.paths import BASE_FILES, FILES, TMP, TMP_STATE_TREE from tests.support.helpers import ( destructiveTest, skip_if_not_root, @@ -30,7 +30,6 @@ with_tempdir, with_tempfile, Webserver, - destructiveTest, dedent, ) from tests.support.mixins import SaltReturnAssertsMixin @@ -65,15 +64,6 @@ BINARY_FILE = b'GIF89a\x01\x00\x01\x00\x80\x00\x00\x05\x04\x04\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;' -if IS_WINDOWS: - FILEPILLAR = 'C:\\Windows\\Temp\\filepillar-python' - FILEPILLARDEF = 'C:\\Windows\\Temp\\filepillar-defaultvalue' - FILEPILLARGIT = 'C:\\Windows\\Temp\\filepillar-bar' -else: - FILEPILLAR = '/tmp/filepillar-python' - FILEPILLARDEF = '/tmp/filepillar-defaultvalue' - FILEPILLARGIT = '/tmp/filepillar-bar' - TEST_SYSTEM_USER = 'test_system_user' TEST_SYSTEM_GROUP = 'test_system_group' @@ -82,8 +72,8 @@ def _test_managed_file_mode_keep_helper(testcase, local=False): ''' DRY helper function to run the same test with a local or remote path ''' - name = os.path.join(TMP, 'scene33') - grail_fs_path = os.path.join(BASE_FILES, 'grail', 'scene33') + name = os.path.join(RUNTIME_VARS.TMP, 'scene33') + grail_fs_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'grail', 'scene33') grail = 'salt://grail/scene33' if not local else grail_fs_path # Get the current mode so that we can put the file back the way we @@ -147,6 +137,14 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): ''' Validate the file state ''' + + def _delete_file(self, path): + try: + os.remove(path) + except OSError as exc: + if exc.errno != errno.ENOENT: + log.error('Failed to remove %s: %s', path, exc) + def tearDown(self): ''' remove files created in previous tests @@ -155,19 +153,12 @@ def tearDown(self): if user in str(self.run_function('user.list_users')): self.run_function('user.delete', [user]) - for path in (FILEPILLAR, FILEPILLARDEF, FILEPILLARGIT): - try: - os.remove(path) - except OSError as exc: - if exc.errno != errno.ENOENT: - log.error('Failed to remove %s: %s', path, exc) - def test_symlink(self): ''' file.symlink ''' - name = os.path.join(TMP, 'symlink') - tgt = os.path.join(TMP, 'target') + name = os.path.join(RUNTIME_VARS.TMP, 'symlink') + tgt = os.path.join(RUNTIME_VARS.TMP, 'target') # Windows must have a source directory to link to if IS_WINDOWS and not os.path.isdir(tgt): @@ -184,8 +175,8 @@ def test_test_symlink(self): ''' file.symlink test interface ''' - name = os.path.join(TMP, 'symlink2') - tgt = os.path.join(TMP, 'target') + name = os.path.join(RUNTIME_VARS.TMP, 'symlink2') + tgt = os.path.join(RUNTIME_VARS.TMP, 'target') ret = self.run_state('file.symlink', test=True, name=name, target=tgt) self.assertSaltNoneReturn(ret) @@ -193,7 +184,7 @@ def test_absent_file(self): ''' file.absent ''' - name = os.path.join(TMP, 'file_to_kill') + name = os.path.join(RUNTIME_VARS.TMP, 'file_to_kill') with salt.utils.files.fopen(name, 'w+') as fp_: fp_.write('killme') ret = self.run_state('file.absent', name=name) @@ -204,7 +195,7 @@ def test_absent_dir(self): ''' file.absent ''' - name = os.path.join(TMP, 'dir_to_kill') + name = os.path.join(RUNTIME_VARS.TMP, 'dir_to_kill') if not os.path.isdir(name): # left behind... Don't fail because of this! os.makedirs(name) @@ -216,7 +207,7 @@ def test_absent_link(self): ''' file.absent ''' - name = os.path.join(TMP, 'link_to_kill') + name = os.path.join(RUNTIME_VARS.TMP, 'link_to_kill') tgt = '{0}.tgt'.format(name) # Windows must have a source directory to link to @@ -250,11 +241,11 @@ def test_managed(self): ''' file.managed ''' - name = os.path.join(TMP, 'grail_scene33') + name = os.path.join(RUNTIME_VARS.TMP, 'grail_scene33') ret = self.run_state( 'file.managed', name=name, source='salt://grail/scene33' ) - src = os.path.join(BASE_FILES, 'grail', 'scene33') + src = os.path.join(RUNTIME_VARS.BASE_FILES, 'grail', 'scene33') with salt.utils.files.fopen(src, 'r') as fp_: master_data = fp_.read() with salt.utils.files.fopen(name, 'r') as fp_: @@ -267,7 +258,7 @@ def test_managed_file_mode(self): file.managed, correct file permissions ''' desired_mode = 504 # 0770 octal - name = os.path.join(TMP, 'grail_scene33') + name = os.path.join(RUNTIME_VARS.TMP, 'grail_scene33') ret = self.run_state( 'file.managed', name=name, mode='0770', source='salt://grail/scene33' ) @@ -284,12 +275,14 @@ def test_managed_file_mode(self): self.assertEqual(oct(desired_mode), oct(resulting_mode)) self.assertSaltTrueReturn(ret) + @skipIf(IS_WINDOWS, 'Windows does not report any file modes. Skipping.') def test_managed_file_mode_keep(self): ''' Test using "mode: keep" in a file.managed state ''' _test_managed_file_mode_keep_helper(self, local=False) + @skipIf(IS_WINDOWS, 'Windows does not report any file modes. Skipping.') def test_managed_file_mode_keep_local_source(self): ''' Test using "mode: keep" in a file.managed state, with a local file path @@ -303,7 +296,7 @@ def test_managed_file_mode_file_exists_replace(self): ''' initial_mode = 504 # 0770 octal desired_mode = 384 # 0600 octal - name = os.path.join(TMP, 'grail_scene33') + name = os.path.join(RUNTIME_VARS.TMP, 'grail_scene33') ret = self.run_state( 'file.managed', name=name, mode=oct(initial_mode), source='salt://grail/scene33' ) @@ -319,7 +312,7 @@ def test_managed_file_mode_file_exists_replace(self): ) self.assertEqual(oct(initial_mode), oct(resulting_mode)) - name = os.path.join(TMP, 'grail_scene33') + name = os.path.join(RUNTIME_VARS.TMP, 'grail_scene33') ret = self.run_state( 'file.managed', name=name, replace=True, mode=oct(desired_mode), source='salt://grail/scene33' ) @@ -335,7 +328,7 @@ def test_managed_file_mode_file_exists_noreplace(self): ''' initial_mode = 504 # 0770 octal desired_mode = 384 # 0600 octal - name = os.path.join(TMP, 'grail_scene33') + name = os.path.join(RUNTIME_VARS.TMP, 'grail_scene33') ret = self.run_state( 'file.managed', name=name, replace=True, mode=oct(initial_mode), source='salt://grail/scene33' ) @@ -360,7 +353,7 @@ def test_managed_file_with_grains_data(self): Test to ensure we can render grains data into a managed file. ''' - grain_path = os.path.join(TMP, 'file-grain-test') + grain_path = os.path.join(RUNTIME_VARS.TMP, 'file-grain-test') state_file = 'file-grainget' self.run_function('state.sls', [state_file], pillar={'grain_path': grain_path}) @@ -380,13 +373,17 @@ def test_managed_file_with_pillar_sls(self): Test to ensure pillar data in sls file is rendered properly and file is created. ''' + + file_pillar = os.path.join(RUNTIME_VARS.TMP, 'filepillar-python') + self.addCleanup(self._delete_file, file_pillar) state_name = 'file-pillarget' + log.warning('File Path: %s', file_pillar) ret = self.run_function('state.sls', [state_name]) self.assertSaltTrueReturn(ret) # Check to make sure the file was created - check_file = self.run_function('file.file_exists', [FILEPILLAR]) + check_file = self.run_function('file.file_exists', [file_pillar]) self.assertTrue(check_file) def test_managed_file_with_pillardefault_sls(self): @@ -395,13 +392,16 @@ def test_managed_file_with_pillardefault_sls(self): in sls file with pillar.get it uses the default value. ''' + file_pillar_def = os.path.join(RUNTIME_VARS.TMP, 'filepillar-defaultvalue') + self.addCleanup(self._delete_file, file_pillar_def) state_name = 'file-pillardefaultget' + log.warning('File Path: %s', file_pillar_def) ret = self.run_function('state.sls', [state_name]) self.assertSaltTrueReturn(ret) # Check to make sure the file was created - check_file = self.run_function('file.file_exists', [FILEPILLARDEF]) + check_file = self.run_function('file.file_exists', [file_pillar_def]) self.assertTrue(check_file) @skip_if_not_root @@ -411,7 +411,7 @@ def test_managed_dir_mode(self): permissions requested with the dir_mode argument ''' desired_mode = 511 # 0777 in octal - name = os.path.join(TMP, 'a', 'managed_dir_mode_test_file') + name = os.path.join(RUNTIME_VARS.TMP, 'a', 'managed_dir_mode_test_file') desired_owner = 'nobody' ret = self.run_state( 'file.managed', @@ -429,9 +429,9 @@ def test_managed_dir_mode(self): return resulting_mode = stat.S_IMODE( - os.stat(os.path.join(TMP, 'a')).st_mode + os.stat(os.path.join(RUNTIME_VARS.TMP, 'a')).st_mode ) - resulting_owner = pwd.getpwuid(os.stat(os.path.join(TMP, 'a')).st_uid).pw_name + resulting_owner = pwd.getpwuid(os.stat(os.path.join(RUNTIME_VARS.TMP, 'a')).st_uid).pw_name self.assertEqual(oct(desired_mode), oct(resulting_mode)) self.assertSaltTrueReturn(ret) self.assertEqual(desired_owner, resulting_owner) @@ -440,7 +440,7 @@ def test_test_managed(self): ''' file.managed test interface ''' - name = os.path.join(TMP, 'grail_not_not_scene33') + name = os.path.join(RUNTIME_VARS.TMP, 'grail_not_not_scene33') ret = self.run_state( 'file.managed', test=True, name=name, source='salt://grail/scene33' ) @@ -451,7 +451,7 @@ def test_managed_show_changes_false(self): ''' file.managed test interface ''' - name = os.path.join(TMP, 'grail_not_scene33') + name = os.path.join(RUNTIME_VARS.TMP, 'grail_not_scene33') with salt.utils.files.fopen(name, 'wb') as fp_: fp_.write(b'test_managed_show_changes_false\n') @@ -467,7 +467,7 @@ def test_managed_show_changes_true(self): ''' file.managed test interface ''' - name = os.path.join(TMP, 'grail_not_scene33') + name = os.path.join(RUNTIME_VARS.TMP, 'grail_not_scene33') with salt.utils.files.fopen(name, 'wb') as fp_: fp_.write(b'test_managed_show_changes_false\n') @@ -486,11 +486,11 @@ def test_managed_escaped_file_path(self): funny_file = salt.utils.files.mkstemp(prefix='?f!le? n@=3&', suffix='.file type') funny_file_name = os.path.split(funny_file)[1] funny_url = 'salt://|' + funny_file_name - funny_url_path = os.path.join(BASE_FILES, funny_file_name) + funny_url_path = os.path.join(RUNTIME_VARS.BASE_FILES, funny_file_name) state_name = 'funny_file' state_file_name = state_name + '.sls' - state_file = os.path.join(BASE_FILES, state_file_name) + state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_file_name) state_key = 'file_|-{0}_|-{0}_|-managed'.format(funny_file) self.addCleanup(os.remove, state_file) @@ -517,7 +517,7 @@ def test_managed_contents(self): ''' state_name = 'file-FileTest-test_managed_contents' state_filename = state_name + '.sls' - state_file = os.path.join(BASE_FILES, state_filename) + state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) managed_files = {} state_keys = {} @@ -573,6 +573,68 @@ def test_managed_contents(self): if os.path.exists(managed_files[typ]): os.remove(managed_files[typ]) + def test_managed_contents_with_contents_newline(self): + ''' + test file.managed with contents by using the default content_newline + flag. + ''' + contents = 'test_managed_contents_with_newline_one' + name = os.path.join(RUNTIME_VARS.TMP, 'foo') + + # Create a file named foo with contents as above but with a \n at EOF + self.run_state('file.managed', name=name, contents=contents, + contents_newline=True) + with salt.utils.files.fopen(name, 'r') as fp_: + last_line = fp_.read() + self.assertEqual((contents + os.linesep), last_line) + + def test_managed_contents_with_contents_newline_false(self): + ''' + test file.managed with contents by using the non default content_newline + flag. + ''' + contents = 'test_managed_contents_with_newline_one' + name = os.path.join(RUNTIME_VARS.TMP, 'bar') + + # Create a file named foo with contents as above but with a \n at EOF + self.run_state('file.managed', name=name, contents=contents, + contents_newline=False) + with salt.utils.files.fopen(name, 'r') as fp_: + last_line = fp_.read() + self.assertEqual(contents, last_line) + + def test_managed_multiline_contents_with_contents_newline(self): + ''' + test file.managed with contents by using the non default content_newline + flag. + ''' + contents = ('this is a cookie{}this is another cookie'. + format(os.linesep)) + name = os.path.join(RUNTIME_VARS.TMP, 'bar') + + # Create a file named foo with contents as above but with a \n at EOF + self.run_state('file.managed', name=name, contents=contents, + contents_newline=True) + with salt.utils.files.fopen(name, 'r') as fp_: + last_line = fp_.read() + self.assertEqual((contents + os.linesep), last_line) + + def test_managed_multiline_contents_with_contents_newline_false(self): + ''' + test file.managed with contents by using the non default content_newline + flag. + ''' + contents = ('this is a cookie{}this is another cookie'. + format(os.linesep)) + name = os.path.join(RUNTIME_VARS.TMP, 'bar') + + # Create a file named foo with contents as above but with a \n at EOF + self.run_state('file.managed', name=name, contents=contents, + contents_newline=False) + with salt.utils.files.fopen(name, 'r') as fp_: + last_line = fp_.read() + self.assertEqual(contents, last_line) + @skip_if_not_root @skipIf(IS_WINDOWS, 'Windows does not support "mode" kwarg. Skipping.') @skipIf(not salt.utils.path.which('visudo'), 'sudo is missing') @@ -605,8 +667,8 @@ def test_managed_local_source_with_source_hash(self): ''' Make sure that we enforce the source_hash even with local files ''' - name = os.path.join(TMP, 'local_source_with_source_hash') - local_path = os.path.join(BASE_FILES, 'grail', 'scene33') + name = os.path.join(RUNTIME_VARS.TMP, 'local_source_with_source_hash') + local_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'grail', 'scene33') actual_hash = '567fd840bf1548edc35c48eb66cdd78bfdfcccff' if IS_WINDOWS: # CRLF vs LF causes a different hash on windows @@ -664,8 +726,8 @@ def test_managed_local_source_does_not_exist(self): ''' Make sure that we exit gracefully when a local source doesn't exist ''' - name = os.path.join(TMP, 'local_source_does_not_exist') - local_path = os.path.join(BASE_FILES, 'grail', 'scene99') + name = os.path.join(RUNTIME_VARS.TMP, 'local_source_does_not_exist') + local_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'grail', 'scene99') for proto in ('file://', ''): source = proto + local_path @@ -708,7 +770,7 @@ def test_managed_unicode_jinja_with_tojson_filter(self): should produce an inline data structure which is valid YAML and will be loaded properly by our YAML loader. ''' - test_file = os.path.join(TMP, 'test-tojson.txt') + test_file = os.path.join(RUNTIME_VARS.TMP, 'test-tojson.txt') ret = self.run_function( 'state.apply', mods='tojson', @@ -730,10 +792,10 @@ def test_managed_source_hash_indifferent_case(self): This is a regression test for Issue #38914 and Issue #48230 (test=true use). ''' - name = os.path.join(TMP, 'source_hash_indifferent_case') + name = os.path.join(RUNTIME_VARS.TMP, 'source_hash_indifferent_case') state_name = 'file_|-{0}_|' \ '-{0}_|-managed'.format(name) - local_path = os.path.join(BASE_FILES, 'hello_world.txt') + local_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'hello_world.txt') actual_hash = 'c98c24b677eff44860afea6f493bbaec5bb1c4cbb209c6fc2bbb47f66ff2ad31' if IS_WINDOWS: # CRLF vs LF causes a differnt hash on windows @@ -907,7 +969,7 @@ def test_directory(self): ''' file.directory ''' - name = os.path.join(TMP, 'a_new_dir') + name = os.path.join(RUNTIME_VARS.TMP, 'a_new_dir') ret = self.run_state('file.directory', name=name) self.assertSaltTrueReturn(ret) self.assertTrue(os.path.isdir(name)) @@ -918,8 +980,8 @@ def test_directory_symlink_dry_run(self): test=True ''' try: - tmp_dir = os.path.join(TMP, 'pgdata') - sym_dir = os.path.join(TMP, 'pg_data') + tmp_dir = os.path.join(RUNTIME_VARS.TMP, 'pgdata') + sym_dir = os.path.join(RUNTIME_VARS.TMP, 'pg_data') os.mkdir(tmp_dir, 0o700) @@ -955,7 +1017,7 @@ def _get_oct_mode(name): ''' return salt.utils.files.normalize_mode(oct(os.stat(name).st_mode & 0o777)) - top = os.path.join(TMP, 'top_dir') + top = os.path.join(RUNTIME_VARS.TMP, 'top_dir') sub = os.path.join(top, 'sub_dir') subsub = os.path.join(sub, 'sub_sub_dir') dirs = [top, sub, subsub] @@ -1002,7 +1064,7 @@ def test_test_directory(self): ''' file.directory ''' - name = os.path.join(TMP, 'a_not_dir') + name = os.path.join(RUNTIME_VARS.TMP, 'a_not_dir') ret = self.run_state('file.directory', test=True, name=name) self.assertSaltNoneReturn(ret) self.assertFalse(os.path.isdir(name)) @@ -1036,7 +1098,7 @@ def test_directory_is_idempotent(self): ''' Ensure the file.directory state produces no changes when rerun. ''' - name = os.path.join(TMP, 'a_dir_twice') + name = os.path.join(RUNTIME_VARS.TMP, 'a_dir_twice') if IS_WINDOWS: username = os.environ.get('USERNAME', 'Administrators') @@ -1152,7 +1214,7 @@ def test_directory_clean_require_in(self, name): ''' state_name = 'file-FileTest-test_directory_clean_require_in' state_filename = state_name + '.sls' - state_file = os.path.join(BASE_FILES, state_filename) + state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) wrong_file = os.path.join(name, "wrong") with salt.utils.files.fopen(wrong_file, "w") as fp: @@ -1185,7 +1247,7 @@ def test_directory_clean_require_in_with_id(self, name): ''' state_name = 'file-FileTest-test_directory_clean_require_in_with_id' state_filename = state_name + '.sls' - state_file = os.path.join(BASE_FILES, state_filename) + state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) wrong_file = os.path.join(name, "wrong") with salt.utils.files.fopen(wrong_file, "w") as fp: @@ -1220,7 +1282,7 @@ def test_directory_clean_require_with_name(self, name): ''' state_name = 'file-FileTest-test_directory_clean_require_in_with_id' state_filename = state_name + '.sls' - state_file = os.path.join(BASE_FILES, state_filename) + state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) wrong_file = os.path.join(name, "wrong") with salt.utils.files.fopen(wrong_file, "w") as fp: @@ -1254,7 +1316,7 @@ def test_directory_broken_symlink(self): contains broken symbolic link ''' try: - tmp_dir = os.path.join(TMP, 'foo') + tmp_dir = os.path.join(RUNTIME_VARS.TMP, 'foo') null_file = '{0}/null'.format(tmp_dir) broken_link = '{0}/broken'.format(tmp_dir) @@ -1541,10 +1603,10 @@ def test_replace_issue_18612_prepend(self, base_dir): ''' test_name = 'test_replace_issue_18612_prepend' path_in = os.path.join( - FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) ) path_out = os.path.join( - FILES, 'file.replace', '{0}.out'.format(test_name) + RUNTIME_VARS.FILES, 'file.replace', '{0}.out'.format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -1582,10 +1644,10 @@ def test_replace_issue_18612_append(self, base_dir): ''' test_name = 'test_replace_issue_18612_append' path_in = os.path.join( - FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) ) path_out = os.path.join( - FILES, 'file.replace', '{0}.out'.format(test_name) + RUNTIME_VARS.FILES, 'file.replace', '{0}.out'.format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -1623,10 +1685,10 @@ def test_replace_issue_18612_append_not_found_content(self, base_dir): ''' test_name = 'test_replace_issue_18612_append_not_found_content' path_in = os.path.join( - FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) ) path_out = os.path.join( - FILES, 'file.replace', '{0}.out'.format(test_name) + RUNTIME_VARS.FILES, 'file.replace', '{0}.out'.format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -1671,10 +1733,10 @@ def test_replace_issue_18612_change_mid_line_with_comment(self, base_dir): ''' test_name = 'test_replace_issue_18612_change_mid_line_with_comment' path_in = os.path.join( - FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) ) path_out = os.path.join( - FILES, 'file.replace', '{0}.out'.format(test_name) + RUNTIME_VARS.FILES, 'file.replace', '{0}.out'.format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -1714,7 +1776,7 @@ def test_replace_issue_18841_no_changes(self, base_dir): ''' test_name = 'test_replace_issue_18841_no_changes' path_in = os.path.join( - FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -1759,7 +1821,7 @@ def test_serialize(self): Test to ensure that file.serialize returns a data structure that's both serialized and formatted properly ''' - path_test = os.path.join(TMP, 'test_serialize') + path_test = os.path.join(RUNTIME_VARS.TMP, 'test_serialize') ret = self.run_state('file.serialize', name=path_test, dataset={'name': 'naive', @@ -1852,7 +1914,7 @@ def test_replace_issue_18841_omit_backup(self, base_dir): ''' test_name = 'test_replace_issue_18841_omit_backup' path_in = os.path.join( - FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -2233,7 +2295,7 @@ def test_issue_2726_mode_kwarg(self, dir1, dir2): @with_tempdir() def test_issue_8343_accumulated_require_in(self, base_dir): - template_path = os.path.join(TMP_STATE_TREE, 'issue-8343.sls') + template_path = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'issue-8343.sls') testcase_filedest = os.path.join(base_dir, 'issue-8343.txt') if os.path.exists(template_path): os.remove(template_path) @@ -2307,7 +2369,7 @@ def test_issue_8343_accumulated_require_in(self, base_dir): @skipIf(salt.utils.platform.is_darwin() and six.PY2, 'This test hangs on OS X on Py2') def test_issue_11003_immutable_lazy_proxy_sum(self, base_dir): # causes the Import-Module ServerManager error on Windows - template_path = os.path.join(TMP_STATE_TREE, 'issue-11003.sls') + template_path = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'issue-11003.sls') testcase_filedest = os.path.join(base_dir, 'issue-11003.txt') sls_template = [ 'a{0}:', @@ -2375,7 +2437,7 @@ def test_issue_8947_utf8_sls(self, base_dir): korean_3 = '마지막 행' test_file = os.path.join(base_dir, '{0}.txt'.format(korean_1)) test_file_encoded = test_file - template_path = os.path.join(TMP_STATE_TREE, 'issue-8947.sls') + template_path = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'issue-8947.sls') # create the sls template template = textwrap.dedent('''\ some-utf8-file-create: @@ -2596,8 +2658,8 @@ def test_issue_25250_force_copy_deletes(self, source, dest): ''' ensure force option in copy state does not delete target file ''' - shutil.copyfile(os.path.join(FILES, 'hosts'), source) - shutil.copyfile(os.path.join(FILES, 'file/base/cheese'), dest) + shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, 'hosts'), source) + shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, 'file/base/cheese'), dest) self.run_state('file.copy', name=dest, source=source, force=True) self.assertTrue(os.path.exists(dest)) @@ -2607,22 +2669,23 @@ def test_issue_25250_force_copy_deletes(self, source, dest): os.remove(dest) @destructiveTest + @skipIf(IS_WINDOWS, 'Windows does not report any file modes. Skipping.') @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') + shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, 'hosts'), source) + dest = os.path.join(RUNTIME_VARS.TMP, 'dir1', 'dir2', 'copied_file.txt') user = 'salt' mode = '0644' ret = self.run_function('user.add', [user]) self.assertTrue(ret, 'Failed to add user. Are you running as sudo?') ret = self.run_state('file.copy', name=dest, source=source, user=user, - makedirs=True, mode=mode) + makedirs=True, mode=mode) self.assertSaltTrueReturn(ret) - file_checks = [dest, os.path.join(TMP, 'dir1'), os.path.join(TMP, 'dir1', 'dir2')] + file_checks = [dest, os.path.join(RUNTIME_VARS.TMP, 'dir1'), os.path.join(RUNTIME_VARS.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]) @@ -2654,7 +2717,7 @@ def test_owner_after_setuid(self, user, group): # Desired configuration. desired = { - 'file': os.path.join(TMP, 'file_with_setuid'), + 'file': os.path.join(RUNTIME_VARS.TMP, 'file_with_setuid'), 'user': user, 'group': group, 'mode': '4750' @@ -2683,7 +2746,7 @@ def test_binary_contents(self): ''' This tests to ensure that binary contents do not cause a traceback. ''' - name = os.path.join(TMP, '1px.gif') + name = os.path.join(RUNTIME_VARS.TMP, '1px.gif') try: ret = self.run_state( 'file.managed', @@ -2696,6 +2759,26 @@ def test_binary_contents(self): except OSError: pass + def test_binary_contents_twice(self): + ''' + This test ensures that after a binary file is created, salt can confirm + that the file is in the correct state. + ''' + # Create a binary file + name = os.path.join(RUNTIME_VARS.TMP, '1px.gif') + + # First run state ensures file is created + ret = self.run_state('file.managed', name=name, contents=BINARY_FILE) + self.assertSaltTrueReturn(ret) + + # Second run of state ensures file is in correct state + ret = self.run_state('file.managed', name=name, contents=BINARY_FILE) + self.assertSaltTrueReturn(ret) + try: + os.remove(name) + except OSError: + pass + @skip_if_not_root @skipIf(not HAS_PWD, "pwd not available. Skipping test") @skipIf(not HAS_GRP, "grp not available. Skipping test") @@ -2778,11 +2861,11 @@ def test_managed_file_issue_51208(self): ''' Test to ensure we can handle a file with escaped double-quotes ''' - name = os.path.join(TMP, 'issue_51208.txt') + name = os.path.join(RUNTIME_VARS.TMP, 'issue_51208.txt') ret = self.run_state( 'file.managed', name=name, source='salt://issue-51208/vimrc.stub' ) - src = os.path.join(BASE_FILES, 'issue-51208', 'vimrc.stub') + src = os.path.join(RUNTIME_VARS.BASE_FILES, 'issue-51208', 'vimrc.stub') with salt.utils.files.fopen(src, 'r') as fp_: master_data = fp_.read() with salt.utils.files.fopen(name, 'r') as fp_: @@ -4042,7 +4125,7 @@ def tearDown(self): os.remove(self.name) except OSError as exc: if exc.errno != errno.ENOENT: - raise exc + six.reraise(*sys.exc_info()) def run_state(self, *args, **kwargs): ret = super(RemoteFileTest, self).run_state(*args, **kwargs) @@ -4151,7 +4234,7 @@ def setUpClass(cls): cls.all_patch_template_http = \ cls.webserver.url(cls.all_patch_template_path) - patches_dir = os.path.join(FILES, 'file', 'base', 'patches') + patches_dir = os.path.join(RUNTIME_VARS.FILES, 'file', 'base', 'patches') cls.numbers_patch_hash = salt.utils.hashutils.get_hash( os.path.join(patches_dir, cls.numbers_patch_name) ) @@ -4181,7 +4264,7 @@ def setUp(self): ''' Create a new unpatched set of files ''' - self.base_dir = tempfile.mkdtemp(dir=TMP) + self.base_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) os.makedirs(os.path.join(self.base_dir, 'foo', 'bar')) self.numbers_file = os.path.join(self.base_dir, 'foo', 'numbers.txt') self.math_file = os.path.join(self.base_dir, 'foo', 'bar', 'math.txt') diff --git a/tests/integration/states/test_git.py b/tests/integration/states/test_git.py index bcab361df17f..ebc4799a4b52 100644 --- a/tests/integration/states/test_git.py +++ b/tests/integration/states/test_git.py @@ -14,10 +14,10 @@ import tempfile # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.helpers import with_tempdir from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.paths import TMP # Import salt libs import salt.utils.files @@ -90,7 +90,7 @@ class WithGitMirror(object): def __init__(self, repo_url, **kwargs): self.repo_url = repo_url if 'dir' not in kwargs: - kwargs['dir'] = TMP + kwargs['dir'] = RUNTIME_VARS.TMP self.kwargs = kwargs def __call__(self, func): @@ -835,9 +835,9 @@ class LocalRepoGitTest(ModuleCase, SaltReturnAssertsMixin): Tests which do no require connectivity to github.com ''' def setUp(self): - self.repo = tempfile.mkdtemp(dir=TMP) - self.admin = tempfile.mkdtemp(dir=TMP) - self.target = tempfile.mkdtemp(dir=TMP) + self.repo = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + self.admin = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + self.target = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) for dirname in (self.repo, self.admin, self.target): self.addCleanup(shutil.rmtree, dirname, ignore_errors=True) diff --git a/tests/integration/states/test_host.py b/tests/integration/states/test_host.py index 1eac631d19c1..d16f3b17ca03 100644 --- a/tests/integration/states/test_host.py +++ b/tests/integration/states/test_host.py @@ -7,17 +7,18 @@ from __future__ import absolute_import, print_function, unicode_literals import os import shutil +import logging # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import FILES, TMP from tests.support.mixins import SaltReturnAssertsMixin # Import salt libs import salt.utils.files import salt.utils.stringutils -HFILE = os.path.join(TMP, 'hosts') +log = logging.getLogger(__name__) class HostTest(ModuleCase, SaltReturnAssertsMixin): @@ -25,15 +26,22 @@ class HostTest(ModuleCase, SaltReturnAssertsMixin): Validate the host state ''' + @classmethod + def setUpClass(cls): + cls.hosts_file = os.path.join(RUNTIME_VARS.TMP, 'hosts') + + def __clear_hosts(self): + ''' + Delete the tmp hosts file + ''' + if os.path.isfile(self.hosts_file): + os.remove(self.hosts_file) + def setUp(self): - shutil.copyfile(os.path.join(FILES, 'hosts'), HFILE) + shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, 'hosts'), self.hosts_file) + self.addCleanup(self.__clear_hosts) super(HostTest, self).setUp() - def tearDown(self): - if os.path.exists(HFILE): - os.remove(HFILE) - super(HostTest, self).tearDown() - def test_present(self): ''' host.present @@ -42,6 +50,6 @@ def test_present(self): ip = '10.10.10.10' ret = self.run_state('host.present', name=name, ip=ip) self.assertSaltTrueReturn(ret) - with salt.utils.files.fopen(HFILE) as fp_: + with salt.utils.files.fopen(self.hosts_file) as fp_: output = salt.utils.stringutils.to_unicode(fp_.read()) self.assertIn('{0}\t\t{1}'.format(ip, name), output) diff --git a/tests/integration/states/test_match.py b/tests/integration/states/test_match.py index e4abc174a66f..6a7f41168ad1 100644 --- a/tests/integration/states/test_match.py +++ b/tests/integration/states/test_match.py @@ -13,15 +13,13 @@ # Import Salt Testing libs from tests.support.case import ModuleCase -from tests.support.paths import FILES from tests.support.helpers import skip_if_not_root +from tests.support.runtests import RUNTIME_VARS # Import salt libs import salt.utils.files import salt.utils.stringutils -STATE_DIR = os.path.join(FILES, 'file', 'base') - class StateMatchTest(ModuleCase): ''' @@ -33,7 +31,7 @@ def test_issue_2167_ipcidr_no_AttributeError(self): subnets = self.run_function('network.subnets') self.assertTrue(len(subnets) > 0) top_filename = 'issue-2167-ipcidr-match.sls' - top_file = os.path.join(STATE_DIR, top_filename) + top_file = os.path.join(RUNTIME_VARS.BASE_FILES, top_filename) try: with salt.utils.files.fopen(top_file, 'w') as fp_: fp_.write( diff --git a/tests/integration/states/test_npm.py b/tests/integration/states/test_npm.py index 2398976d436b..d1406d3a0967 100644 --- a/tests/integration/states/test_npm.py +++ b/tests/integration/states/test_npm.py @@ -16,8 +16,8 @@ from tests.support.runtests import RUNTIME_VARS # Import salt libs -import salt.modules.cmdmod as cmd import salt.utils.path +import salt.utils.platform from salt.utils.versions import LooseVersion MAX_NPM_VERSION = '5.0.0' @@ -38,13 +38,15 @@ def test_npm_installed_removed(self): ret = self.run_state('npm.removed', name='pm2') self.assertSaltTrueReturn(ret) + @skipIf(salt.utils.platform.is_darwin(), 'TODO this test hangs on mac.') @requires_network() @destructiveTest def test_npm_install_url_referenced_package(self): ''' Determine if URL-referenced NPM module can be successfully installed. ''' - if LooseVersion(cmd.run('npm -v')) >= LooseVersion(MAX_NPM_VERSION): + npm_version = self.run_function('cmd.run', ['npm -v']) + if LooseVersion(npm_version) >= LooseVersion(MAX_NPM_VERSION): user = os.environ.get('SUDO_USER', 'root') npm_dir = os.path.join(RUNTIME_VARS.TMP, 'git-install-npm') self.run_state('file.directory', name=npm_dir, user=user, dir_mode='755') @@ -72,12 +74,13 @@ def test_npm_installed_pkgs(self): ret = self.run_state('npm.installed', name='unused', pkgs=['pm2@2.10.4', 'grunt@1.0.2'], registry="http://registry.npmjs.org/") self.assertSaltTrueReturn(ret) - @skipIf(salt.utils.path.which('npm') and LooseVersion(cmd.run('npm -v')) >= LooseVersion(MAX_NPM_VERSION), - 'Skip with npm >= 5.0.0 until #41770 is fixed') @destructiveTest def test_npm_cache_clean(self): ''' Basic test to determine if NPM successfully cleans its cached packages. ''' + npm_version = self.run_function('cmd.run', ['npm -v']) + if LooseVersion(npm_version) >= LooseVersion(MAX_NPM_VERSION): + self.skipTest('Skip with npm >= 5.0.0 until #41770 is fixed') ret = self.run_state('npm.cache_cleaned', name='unused', force=True) self.assertSaltTrueReturn(ret) diff --git a/tests/integration/states/test_pip_state.py b/tests/integration/states/test_pip_state.py index e80e67e7bc43..45ccac1e80fe 100644 --- a/tests/integration/states/test_pip_state.py +++ b/tests/integration/states/test_pip_state.py @@ -29,7 +29,8 @@ requires_system_grains, with_system_user, skip_if_not_root, - with_tempdir + with_tempdir, + patched_environ ) from tests.support.mixins import SaltReturnAssertsMixin from tests.support.runtests import RUNTIME_VARS @@ -72,7 +73,7 @@ def __exit__(self, *args): @skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed') class PipStateTest(ModuleCase, SaltReturnAssertsMixin): - def _create_virtualenv(self, path): + def _create_virtualenv(self, path, **kwargs): ''' The reason why the virtualenv creation is proxied by this function is mostly because under windows, we can't seem to properly create a virtualenv off of @@ -84,16 +85,31 @@ def _create_virtualenv(self, path): ''' self.addCleanup(shutil.rmtree, path, ignore_errors=True) try: - if salt.utils.is_windows(): + if salt.utils.platform.is_windows(): python = os.path.join(sys.real_prefix, os.path.basename(sys.executable)) else: - python = os.path.join(sys.real_prefix, 'bin', os.path.basename(sys.executable)) + python_binary_names = [ + 'python{}.{}'.format(*sys.version_info), + 'python{}'.format(*sys.version_info), + 'python' + ] + for binary_name in python_binary_names: + python = os.path.join(sys.real_prefix, 'bin', binary_name) + if os.path.exists(python): + break + else: + self.fail( + 'Couldn\'t find a python binary name under \'{}\' matching: {}'.format( + os.path.join(sys.real_prefix, 'bin'), + python_binary_names + ) + ) # We're running off a virtualenv, and we don't want to create a virtualenv off of # a virtualenv, let's point to the actual python that created the virtualenv - kwargs = {'python': python} + kwargs['python'] = python except AttributeError: # We're running off of the system python - kwargs = {} + pass return self.run_function('virtualenv.create', [path], **kwargs) def test_pip_installed_removed(self): @@ -123,34 +139,28 @@ def test_pip_installed_errors(self): venv_dir = os.path.join( RUNTIME_VARS.TMP, 'pip-installed-errors' ) - - def cleanup_environ(environ): - os.environ.clear() - os.environ.update(environ) - - self.addCleanup(cleanup_environ, os.environ.copy()) - + self.addCleanup(shutil.rmtree, venv_dir, ignore_errors=True) # Since we don't have the virtualenv created, pip.installed will # throw an error. # Example error strings: # * "Error installing 'pep8': /tmp/pip-installed-errors: not found" # * "Error installing 'pep8': /bin/sh: 1: /tmp/pip-installed-errors: not found" # * "Error installing 'pep8': /bin/bash: /tmp/pip-installed-errors: No such file or directory" - os.environ['SHELL'] = '/bin/sh' - ret = self.run_function('state.sls', mods='pip-installed-errors') - self.assertSaltFalseReturn(ret) - self.assertSaltCommentRegexpMatches( - ret, - 'Error installing \'pep8\':' - ) + with patched_environ(SHELL='/bin/sh'): + ret = self.run_function('state.sls', mods='pip-installed-errors') + self.assertSaltFalseReturn(ret) + self.assertSaltCommentRegexpMatches( + ret, + 'Error installing \'pep8\':' + ) - # We now create the missing virtualenv - ret = self._create_virtualenv(venv_dir) - self.assertEqual(ret['retcode'], 0) + # We now create the missing virtualenv + ret = self.run_function('virtualenv.create', [venv_dir]) + self.assertEqual(ret['retcode'], 0) - # The state should not have any issues running now - ret = self.run_function('state.sls', mods='pip-installed-errors') - self.assertSaltTrueReturn(ret) + # The state should not have any issues running now + ret = self.run_function('state.sls', mods='pip-installed-errors') + self.assertSaltTrueReturn(ret) @skipIf(six.PY3, 'Issue is specific to carbon module, which is PY2-only') @skipIf(salt.utils.platform.is_windows(), "Carbon does not install in Windows") @@ -310,9 +320,7 @@ def test_issue_6912_wrong_owner(self, temp_dir, username): os.chown(temp_dir, uid, -1) # Create the virtual environment - venv_create = self.run_function( - 'virtualenv.create', [venv_dir], user=username, - password='PassWord1!') + venv_create = self._create_virtualenv(venv_dir, user=username, password='PassWord1!') if venv_create['retcode'] > 0: self.skipTest('Failed to create testcase virtual environment: {0}' ''.format(venv_create)) @@ -354,9 +362,7 @@ def test_issue_6912_wrong_owner_requirements_file(self, temp_dir, username): os.chown(temp_dir, uid, -1) # Create the virtual environment again as it should have been removed - venv_create = self.run_function( - 'virtualenv.create', [venv_dir], user=username, - password='PassWord1!') + venv_create = self._create_virtualenv(venv_dir, user=username, password='PassWord1!') if venv_create['retcode'] > 0: self.skipTest('failed to create testcase virtual environment: {0}' ''.format(venv_create)) @@ -510,7 +516,7 @@ def test_22359_pip_installed_unless_does_not_trigger_warnings(self): ) ) - false_cmd = salt.utils.path.which('false') + false_cmd = RUNTIME_VARS.SHELL_FALSE_PATH if salt.utils.platform.is_windows(): false_cmd = 'exit 1 >nul' try: diff --git a/tests/integration/states/test_pkg.py b/tests/integration/states/test_pkg.py index c990c518dd72..ed42e0c91c65 100644 --- a/tests/integration/states/test_pkg.py +++ b/tests/integration/states/test_pkg.py @@ -31,7 +31,6 @@ try: from distro import LinuxDistribution - pre_grains = LinuxDistribution() except ImportError: pre_grains = None diff --git a/tests/integration/states/test_ssh_known_hosts.py b/tests/integration/states/test_ssh_known_hosts.py index 339f4bb05a48..26d17bba9e31 100644 --- a/tests/integration/states/test_ssh_known_hosts.py +++ b/tests/integration/states/test_ssh_known_hosts.py @@ -5,8 +5,10 @@ # Import python libs from __future__ import absolute_import, unicode_literals, print_function + import os import shutil +import sys # Import Salt Testing libs from tests.support.case import ModuleCase @@ -14,7 +16,9 @@ from tests.support.runtests import RUNTIME_VARS from tests.support.helpers import skip_if_binaries_missing -KNOWN_HOSTS = os.path.join(RUNTIME_VARS.TMP, 'known_hosts') +# Import 3rd-party libs +from salt.ext import six + GITHUB_FINGERPRINT = '9d:38:5b:83:a9:17:52:92:56:1a:5e:c4:d4:81:8e:0a:ca:51:a2:64:f1:74:20:11:2e:f8:8a:c3:a1:39:49:8f' GITHUB_IP = '192.30.253.113' @@ -24,9 +28,13 @@ class SSHKnownHostsStateTest(ModuleCase, SaltReturnAssertsMixin): ''' Validate the ssh state ''' + @classmethod + def setUpClass(cls): + cls.known_hosts = os.path.join(RUNTIME_VARS.TMP, 'known_hosts') + def tearDown(self): - if os.path.isfile(KNOWN_HOSTS): - os.remove(KNOWN_HOSTS) + if os.path.isfile(self.known_hosts): + os.remove(self.known_hosts) super(SSHKnownHostsStateTest, self).tearDown() def test_present(self): @@ -37,7 +45,7 @@ def test_present(self): 'name': 'github.com', 'user': 'root', 'fingerprint': GITHUB_FINGERPRINT, - 'config': KNOWN_HOSTS + 'config': self.known_hosts } # test first ret = self.run_state('ssh_known_hosts.present', test=True, **kwargs) @@ -54,8 +62,7 @@ def test_present(self): ) self.skipTest('Unable to receive remote host key') except AssertionError: - # raise initial assertion error - raise err + six.reraise(*sys.exc_info()) self.assertSaltStateChangesEqual( ret, GITHUB_FINGERPRINT, keys=('new', 0, 'fingerprint') @@ -82,11 +89,11 @@ def test_present(self): ) self.skipTest('Unable to receive remote host key') except AssertionError: - raise err + six.reraise(*sys.exc_info()) # record for every host must be available ret = self.run_function( - 'ssh.get_known_host_entries', ['root', 'github.com'], config=KNOWN_HOSTS + 'ssh.get_known_host_entries', ['root', 'github.com'], config=self.known_hosts )[0] try: self.assertNotIn(ret, ('', None)) @@ -95,7 +102,7 @@ def test_present(self): 'Salt return \'{0}\' is in (\'\', None).'.format(ret) ) ret = self.run_function( - 'ssh.get_known_host_entries', ['root', GITHUB_IP], config=KNOWN_HOSTS + 'ssh.get_known_host_entries', ['root', GITHUB_IP], config=self.known_hosts )[0] try: self.assertNotIn(ret, ('', None, {})) @@ -111,7 +118,7 @@ def test_present_fail(self): name='github.com', user='root', fingerprint='aa:bb:cc:dd', - config=KNOWN_HOSTS + config=self.known_hosts ) self.assertSaltFalseReturn(ret) @@ -120,15 +127,15 @@ def test_absent(self): ssh_known_hosts.absent ''' known_hosts = os.path.join(RUNTIME_VARS.FILES, 'ssh', 'known_hosts') - shutil.copyfile(known_hosts, KNOWN_HOSTS) - if not os.path.isfile(KNOWN_HOSTS): + shutil.copyfile(known_hosts, self.known_hosts) + if not os.path.isfile(self.known_hosts): self.skipTest( 'Unable to copy {0} to {1}'.format( - known_hosts, KNOWN_HOSTS + known_hosts, self.known_hosts ) ) - kwargs = {'name': 'github.com', 'user': 'root', 'config': KNOWN_HOSTS} + kwargs = {'name': 'github.com', 'user': 'root', 'config': self.known_hosts} # test first ret = self.run_state('ssh_known_hosts.absent', test=True, **kwargs) self.assertSaltNoneReturn(ret) diff --git a/tests/integration/states/test_supervisord.py b/tests/integration/states/test_supervisord.py index 5c24924a8737..9fb46167f5a9 100644 --- a/tests/integration/states/test_supervisord.py +++ b/tests/integration/states/test_supervisord.py @@ -11,9 +11,9 @@ import subprocess # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.unit import skipIf -from tests.support.paths import TMP from tests.support.mixins import SaltReturnAssertsMixin # Import salt libs @@ -34,7 +34,7 @@ class SupervisordTest(ModuleCase, SaltReturnAssertsMixin): def setUp(self): super(SupervisordTest, self).setUp() - self.venv_test_dir = os.path.join(TMP, 'supervisortests') + self.venv_test_dir = os.path.join(RUNTIME_VARS.TMP, 'supervisortests') self.venv_dir = os.path.join(self.venv_test_dir, 'venv') self.supervisor_sock = os.path.join(self.venv_dir, 'supervisor.sock') diff --git a/tests/integration/states/test_svn.py b/tests/integration/states/test_svn.py index 53a2df3adf6e..115c7133b9c9 100644 --- a/tests/integration/states/test_svn.py +++ b/tests/integration/states/test_svn.py @@ -11,8 +11,8 @@ import socket # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase -from tests.support.paths import TMP from tests.support.mixins import SaltReturnAssertsMixin @@ -37,7 +37,7 @@ def setUp(self): msg = 'error resolving {0}, possible network issue?' self.skipTest(msg.format(self.__domain)) - self.target = os.path.join(TMP, 'apache_http_test_repo') + self.target = os.path.join(RUNTIME_VARS.TMP, 'apache_http_test_repo') self.name = 'http://{0}/repos/asf/httpd/httpd/trunk/test/'.format( self.__domain ) diff --git a/tests/integration/states/test_user.py b/tests/integration/states/test_user.py index a820e97c7cab..a1efae88e513 100644 --- a/tests/integration/states/test_user.py +++ b/tests/integration/states/test_user.py @@ -269,3 +269,39 @@ def tearDown(self): self.assertSaltTrueReturn( self.run_state('user.absent', name=self.user_name) ) + + +@destructiveTest +@skip_if_not_root +@skipIf(not salt.utils.platform.is_windows(), 'Windows only tests') +class WinUserTest(ModuleCase, SaltReturnAssertsMixin): + ''' + test for user absent + ''' + def tearDown(self): + self.assertSaltTrueReturn( + self.run_state('user.absent', name=USER) + ) + + def test_user_present_existing(self): + ret = self.run_state('user.present', + name=USER, + win_homedrive='U:', + win_profile='C:\\User\\{0}'.format(USER), + win_logonscript='C:\\logon.vbs', + win_description='Test User Account') + self.assertSaltTrueReturn(ret) + ret = self.run_state('user.present', + name=USER, + win_homedrive='R:', + win_profile='C:\\Users\\{0}'.format(USER), + win_logonscript='C:\\Windows\\logon.vbs', + win_description='Temporary Account') + self.assertSaltTrueReturn(ret) + self.assertSaltStateChangesEqual(ret, 'R:', keys=['homedrive']) + self.assertSaltStateChangesEqual( + ret, 'C:\\Users\\{0}'.format(USER), keys=['profile']) + self.assertSaltStateChangesEqual( + ret, 'C:\\Windows\\logon.vbs', keys=['logonscript']) + self.assertSaltStateChangesEqual( + ret, 'Temporary Account', keys=['description']) diff --git a/tests/integration/states/test_x509.py b/tests/integration/states/test_x509.py index 1808d3c19ef2..bbb59bb49812 100644 --- a/tests/integration/states/test_x509.py +++ b/tests/integration/states/test_x509.py @@ -8,10 +8,10 @@ import textwrap from tests.support.helpers import with_tempfile -from tests.support.paths import BASE_FILES, TMP, TMP_PILLAR_TREE from tests.support.case import ModuleCase from tests.support.unit import skipIf from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS try: import M2Crypto # pylint: disable=W0611 @@ -28,12 +28,12 @@ class x509Test(ModuleCase, SaltReturnAssertsMixin): @classmethod def setUpClass(cls): - cert_path = os.path.join(BASE_FILES, 'x509_test.crt') + cert_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'x509_test.crt') with salt.utils.files.fopen(cert_path) as fp: cls.x509_cert_text = fp.read() def setUp(self): - with salt.utils.files.fopen(os.path.join(TMP_PILLAR_TREE, 'signing_policies.sls'), 'w') as fp: + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'signing_policies.sls'), 'w') as fp: fp.write(textwrap.dedent('''\ x509_signing_policies: ca_policy: @@ -48,8 +48,8 @@ def setUp(self): - authorityKeyIdentifier: keyid - days_valid: 730 - copypath: {0}/pki - '''.format(TMP))) - with salt.utils.files.fopen(os.path.join(TMP_PILLAR_TREE, 'top.sls'), 'w') as fp: + '''.format(RUNTIME_VARS.TMP))) + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls'), 'w') as fp: fp.write(textwrap.dedent('''\ base: '*': @@ -58,9 +58,9 @@ def setUp(self): self.run_function('saltutil.refresh_pillar') def tearDown(self): - os.remove(os.path.join(TMP_PILLAR_TREE, 'signing_policies.sls')) - os.remove(os.path.join(TMP_PILLAR_TREE, 'top.sls')) - certs_path = os.path.join(TMP, 'pki') + os.remove(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'signing_policies.sls')) + os.remove(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls')) + certs_path = os.path.join(RUNTIME_VARS.TMP, 'pki') if os.path.exists(certs_path): salt.utils.files.rm_rf(certs_path) self.run_function('saltutil.refresh_pillar') @@ -97,8 +97,8 @@ def test_issue_49008(self, keyfile, crtfile): assert os.path.exists(crtfile) def test_cert_signing(self): - ret = self.run_function('state.apply', ['test_cert'], pillar={'tmp_dir': TMP}) - key = 'x509_|-test_crt_|-{}/pki/test.crt_|-certificate_managed'.format(TMP) + ret = self.run_function('state.apply', ['test_cert'], pillar={'tmp_dir': RUNTIME_VARS.TMP}) + key = 'x509_|-test_crt_|-{}/pki/test.crt_|-certificate_managed'.format(RUNTIME_VARS.TMP) assert key in ret assert 'changes' in ret[key] assert 'Certificate' in ret[key]['changes'] diff --git a/tests/integration/states/test_zookeeper.py b/tests/integration/states/test_zookeeper.py index ddc139faf122..dd21cce2a05b 100644 --- a/tests/integration/states/test_zookeeper.py +++ b/tests/integration/states/test_zookeeper.py @@ -5,6 +5,7 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +import logging # Import Salt Testing Libs from tests.support.unit import skipIf @@ -21,6 +22,8 @@ except ImportError: HAS_KAZOO = False +log = logging.getLogger(__name__) + @destructiveTest @skipIf(not salt.utils.path.which('dockerd'), 'Docker not installed') @@ -29,15 +32,17 @@ class ZookeeperTestCase(ModuleCase, SaltReturnAssertsMixin): ''' Test zookeeper states ''' + @classmethod + def setUpClass(cls): + cls.container_name = 'zookeeper_salt' + def setUp(self): - ''' - ''' self.run_state('docker_image.present', name='zookeeper') - self.run_state('docker_container.running', name='zookeeper', image='zookeeper', port_bindings='2181:2181') + self.run_state('docker_container.running', name=self.container_name, image='zookeeper', port_bindings='2181:2181') def tearDown(self): - self.run_state('docker_container.stopped', name='zookeeper') - self.run_state('docker_container.absent', name='zookeeper') + self.run_state('docker_container.stopped', name=self.container_name) + self.run_state('docker_container.absent', name=self.container_name) self.run_state('docker_image.absent', name='docker.io/zookeeper', force=True) def test_zookeeper_present(self): diff --git a/tests/integration/utils/test_win_runas.py b/tests/integration/utils/test_win_runas.py index 3042a77991da..b0cd2157968f 100644 --- a/tests/integration/utils/test_win_runas.py +++ b/tests/integration/utils/test_win_runas.py @@ -17,8 +17,8 @@ import yaml from tests.support.case import ModuleCase from tests.support.mock import Mock -from tests.support.paths import CODE_DIR from tests.support.unit import skipIf +from tests.support.runtests import RUNTIME_VARS from tests.support.helpers import ( with_system_user, @@ -33,7 +33,7 @@ import win32event import servicemanager import win32api - CODE_DIR = win32api.GetLongPathName(CODE_DIR) + CODE_DIR = win32api.GetLongPathName(RUNTIME_VARS.CODE_DIR) HAS_WIN32 = True except ImportError: # Mock win32serviceutil object to avoid @@ -52,8 +52,9 @@ 'points:\n---------------------------------------------\n\n' 'INFO: No shared open files found.\n' ) -RUNAS_PATH = os.path.abspath(os.path.join(CODE_DIR, 'runas.py')) -RUNAS_OUT = os.path.abspath(os.path.join(CODE_DIR, 'runas.out')) +if HAS_WIN32: + RUNAS_PATH = os.path.abspath(os.path.join(CODE_DIR, 'runas.py')) + RUNAS_OUT = os.path.abspath(os.path.join(CODE_DIR, 'runas.out')) def default_target(service, *args, **kwargs): diff --git a/tests/integration/utils/testprogram.py b/tests/integration/utils/testprogram.py index 7f80af4aec62..289ad8fb3b22 100644 --- a/tests/integration/utils/testprogram.py +++ b/tests/integration/utils/testprogram.py @@ -30,10 +30,9 @@ from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin from tests.support.unit import TestCase -from tests.support.helpers import win32_kill_process_tree -from tests.support.paths import CODE_DIR +from tests.support.runtests import RUNTIME_VARS from tests.support.processes import terminate_process, terminate_process_list - +from tests.support.cli_scripts import ScriptPathMixin log = logging.getLogger(__name__) @@ -389,8 +388,8 @@ def run( if path not in env_pypath: env_pypath.append(path) # Always ensure that the test tree is searched first for python modules - if CODE_DIR != env_pypath[0]: - env_pypath.insert(0, CODE_DIR) + if RUNTIME_VARS.CODE_DIR != env_pypath[0]: + env_pypath.insert(0, RUNTIME_VARS.CODE_DIR) if salt.utils.platform.is_windows(): env_delta['PYTHONPATH'] = ';'.join(env_pypath) else: @@ -440,27 +439,8 @@ def detach_from_parent_group(): process.poll() if datetime.now() > stop_at: - if term_sent is False: - if salt.utils.platform.is_windows(): - _, alive = win32_kill_process_tree(process.pid) - if alive: - log.error("Child processes still alive: %s", alive) - else: - # Kill the process group since sending the term signal - # would only terminate the shell, not the command - # executed in the shell - os.killpg(os.getpgid(process.pid), signal.SIGINT) - term_sent = True - continue - try: - if salt.utils.platform.is_windows(): - _, alive = win32_kill_process_tree(process.pid) - if alive: - log.error("Child processes still alive: %s", alive) - else: - # As a last resort, kill the process group - os.killpg(os.getpgid(process.pid), signal.SIGKILL) + terminate_process(pid=process.pid, kill_children=True) process.wait() except OSError as exc: if exc.errno != errno.ESRCH: @@ -592,7 +572,7 @@ def __new__(mcs, name, bases, attrs): return super(TestSaltProgramMeta, mcs).__new__(mcs, name, bases, attrs) -class TestSaltProgram(six.with_metaclass(TestSaltProgramMeta, TestProgram)): +class TestSaltProgram(six.with_metaclass(TestSaltProgramMeta, TestProgram, ScriptPathMixin)): ''' This is like TestProgram but with some functions to run a salt-specific auxiliary program. @@ -643,9 +623,7 @@ def __init__(self, *args, **kwargs): # This is effectively a place-holder - it gets set correctly after super() kwargs['program'] = self.script super(TestSaltProgram, self).__init__(*args, **kwargs) - self.program = self.abs_path(os.path.join(self.script_dir, self.script)) - path = self.env.get('PATH', os.getenv('PATH')) - self.env['PATH'] = ':'.join([self.abs_path(self.script_dir), path]) + self.program = self.get_script_path(self.script) def config_merge(self, base, overrides): _base = self.config_cast(copy.deepcopy(base)) @@ -687,28 +665,6 @@ def config_stringify(self, config): cfg[key] = val return salt.utils.yaml.safe_dump(cfg, default_flow_style=False) - def setup(self, *args, **kwargs): - super(TestSaltProgram, self).setup(*args, **kwargs) - self.install_script() - - def install_script(self): - '''Generate the script file that calls python objects and libraries.''' - lines = [] - script_source = os.path.join(CODE_DIR, 'scripts', self.script) - with salt.utils.files.fopen(script_source, 'r') as sso: - lines.extend(sso.readlines()) - if lines[0].startswith('#!'): - lines.pop(0) - lines.insert(0, '#!{0}\n'.format(sys.executable)) - - script_path = self.abs_path(os.path.join(self.script_dir, self.script)) - log.debug('Installing "{0}" to "{1}"'.format(script_source, script_path)) - with salt.utils.files.fopen(script_path, 'w') as sdo: - sdo.write(''.join(lines)) - sdo.flush() - - os.chmod(script_path, 0o755) - def run(self, **kwargs): if not kwargs.get('verbatim_args'): args = kwargs.setdefault('args', []) diff --git a/tests/kitchen/tests/wordpress/tests/conftest.py b/tests/kitchen/tests/wordpress/tests/conftest.py index 27d163557b78..c94f18d8fb72 100644 --- a/tests/kitchen/tests/wordpress/tests/conftest.py +++ b/tests/kitchen/tests/wordpress/tests/conftest.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import import functools import os import pytest @@ -10,7 +12,7 @@ else: test_host = testinfra.get_host('paramiko://{KITCHEN_USERNAME}@{KITCHEN_HOSTNAME}:{KITCHEN_PORT}'.format(**os.environ), ssh_identity_file=os.environ.get('KITCHEN_SSH_KEY')) -else: +elif 'KITCHEN_USERNAME' in os.environ: test_host = testinfra.get_host('docker://{KITCHEN_USERNAME}@{KITCHEN_CONTAINER_ID}'.format(**os.environ)) diff --git a/tests/minionswarm.py b/tests/minionswarm.py index 86f3a24a6811..de899e5d5139 100644 --- a/tests/minionswarm.py +++ b/tests/minionswarm.py @@ -28,7 +28,7 @@ # Import third party libs from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -import tests.support.helpers +import tests.support.runtests OSES = [ @@ -148,7 +148,7 @@ def parse(): '-c', '--config-dir', default='', help=('Pass in a configuration directory containing base configuration.') ) - parser.add_option('-u', '--user', default=tests.support.helpers.this_user()) + parser.add_option('-u', '--user', default=tests.support.runtests.this_user()) options, _args = parser.parse_args() diff --git a/tests/multimaster/__init__.py b/tests/multimaster/__init__.py index 9bed204f4f59..5252903c30ac 100644 --- a/tests/multimaster/__init__.py +++ b/tests/multimaster/__init__.py @@ -14,6 +14,7 @@ import sys import threading import time +from collections import OrderedDict # Import salt tests support dirs from tests.support.paths import ( @@ -68,7 +69,7 @@ def __enter__(self): self._enter_mockbin() self.master_targets = [self.mm_master_opts, self.mm_sub_master_opts] - self.minion_targets = set(['minion', 'sub_minion']) + self.minion_targets = set(['mm-minion', 'mm-sub-minion']) if self.parser.options.transport == 'zeromq': self.start_zeromq_daemons() @@ -368,15 +369,18 @@ def clients(self): previously was, it would not receive the master events. ''' if 'runtime_clients' not in RUNTIME_VARS.RUNTIME_CONFIGS: - clients = [] - for master_opts in self.master_targets: - clients.append(salt.client.get_local_client(mopts=master_opts)) - RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] = clients - return RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] + RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] = OrderedDict() + + runtime_clients = RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] + for mopts in self.master_targets: + if mopts['id'] in runtime_clients: + continue + runtime_clients[mopts['id']] = salt.client.get_local_client(mopts=mopts) + return runtime_clients @property def client(self): - return self.clients[0] + return self.clients['mm-master'] @classmethod def transplant_configs(cls, transport='zeromq'): @@ -391,11 +395,11 @@ def transplant_configs(cls, transport='zeromq'): master_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_master'))) master_opts['known_hosts_file'] = tests_known_hosts_file - master_opts['cachedir'] = os.path.join(TMP, 'rootdir_multimaster', 'cache') + master_opts['cachedir'] = 'cache' master_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER master_opts['config_dir'] = RUNTIME_VARS.TMP_MM_CONF_DIR master_opts['root_dir'] = os.path.join(TMP, 'rootdir-multimaster') - master_opts['pki_dir'] = os.path.join(TMP, 'rootdir-multimaster', 'pki', 'master') + master_opts['pki_dir'] = 'pki' file_tree = { 'root_dir': os.path.join(FILES, 'pillar', 'base', 'file_tree'), 'follow_dir_links': False, @@ -408,11 +412,11 @@ def transplant_configs(cls, transport='zeromq'): sub_master_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_sub_master'))) sub_master_opts['known_hosts_file'] = tests_known_hosts_file - sub_master_opts['cachedir'] = os.path.join(TMP, 'rootdir-sub-multimaster', 'cache') + sub_master_opts['cachedir'] = 'cache' sub_master_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER sub_master_opts['config_dir'] = RUNTIME_VARS.TMP_MM_SUB_CONF_DIR sub_master_opts['root_dir'] = os.path.join(TMP, 'rootdir-sub-multimaster') - sub_master_opts['pki_dir'] = os.path.join(TMP, 'rootdir-sub-multimaster', 'pki', 'master') + sub_master_opts['pki_dir'] = 'pki' sub_master_opts['ext_pillar'].append({'file_tree': copy.deepcopy(file_tree)}) # Under windows we can't seem to properly create a virtualenv off of another @@ -450,11 +454,11 @@ def transplant_configs(cls, transport='zeromq'): minion_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'minion')) minion_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_minion'))) - minion_opts['cachedir'] = os.path.join(TMP, 'rootdir-multimaster', 'cache') + minion_opts['cachedir'] = 'cache' minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER minion_opts['config_dir'] = RUNTIME_VARS.TMP_MM_CONF_DIR minion_opts['root_dir'] = os.path.join(TMP, 'rootdir-multimaster') - minion_opts['pki_dir'] = os.path.join(TMP, 'rootdir-multimaster', 'pki', 'minion') + minion_opts['pki_dir'] = 'pki' minion_opts['hosts.file'] = os.path.join(TMP, 'rootdir', 'hosts') minion_opts['aliases.file'] = os.path.join(TMP, 'rootdir', 'aliases') if virtualenv_binary: @@ -464,11 +468,11 @@ def transplant_configs(cls, transport='zeromq'): sub_minion_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'sub_minion')) sub_minion_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_sub_minion'))) - sub_minion_opts['cachedir'] = os.path.join(TMP, 'rootdir-sub-multimaster', 'cache') + sub_minion_opts['cachedir'] = 'cache' sub_minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER sub_minion_opts['config_dir'] = RUNTIME_VARS.TMP_MM_SUB_CONF_DIR sub_minion_opts['root_dir'] = os.path.join(TMP, 'rootdir-sub-multimaster') - sub_minion_opts['pki_dir'] = os.path.join(TMP, 'rootdir-sub-multimaster', 'pki', 'minion') + sub_minion_opts['pki_dir'] = 'pki' sub_minion_opts['hosts.file'] = os.path.join(TMP, 'rootdir', 'hosts') sub_minion_opts['aliases.file'] = os.path.join(TMP, 'rootdir', 'aliases') if virtualenv_binary: diff --git a/tests/multimaster/beacons/test_inotify.py b/tests/multimaster/beacons/test_inotify.py index 2d79c357cac2..bf734d7a0d04 100644 --- a/tests/multimaster/beacons/test_inotify.py +++ b/tests/multimaster/beacons/test_inotify.py @@ -33,34 +33,34 @@ class TestBeaconsInotify(MultimasterModuleCase, AdaptedConfigurationTestCaseMixi ''' def setUp(self): self.tmpdir = salt.utils.stringutils.to_unicode(tempfile.mkdtemp()) - - def tearDown(self): - shutil.rmtree(self.tmpdir, ignore_errors=True) + self.addCleanup(shutil.rmtree, self.tmpdir, ignore_errors=True) def test_beacons_duplicate_53344(self): # Also add a status beacon to use it for interval checks res = self.run_function( - 'beacons.add', - ('inotify', [{'files': {self.tmpdir: {'mask': ['create']}}}]), - master_tgt=0, - ) + 'beacons.add', + ('inotify', [{'files': {self.tmpdir: {'mask': ['create']}}}]), + master_tgt='mm-master', + ) log.debug('Inotify beacon add returned: %s', res) self.assertTrue(res.get('result')) + self.addCleanup(self.run_function, 'beacons.delete', ('inotify',), master_tgt='mm-master') res = self.run_function( - 'beacons.add', - ('status', [{'time': ['all']}]), - master_tgt=0, - ) + 'beacons.add', + ('status', [{'time': ['all']}]), + master_tgt='mm-master', + ) log.debug('Status beacon add returned: %s', res) self.assertTrue(res.get('result')) + self.addCleanup(self.run_function, 'beacons.delete', ('status',), master_tgt='mm-master') # Ensure beacons are added. res = self.run_function( - 'beacons.list', - (), - return_yaml=False, - master_tgt=0, - ) + 'beacons.list', + (), + return_yaml=False, + master_tgt='mm-master', + ) log.debug('Beacons list: %s', res) self.assertEqual({ 'inotify': [{ @@ -76,86 +76,78 @@ def test_beacons_duplicate_53344(self): }, res) file_path = os.path.join(self.tmpdir, 'tmpfile') - try: - master_listener = salt.utils.event.get_event( - 'master', - sock_dir=self.mm_master_opts['sock_dir'], - transport=self.mm_master_opts['transport'], - opts=self.mm_master_opts) - sub_master_listener = salt.utils.event.get_event( - 'master', - sock_dir=self.mm_sub_master_opts['sock_dir'], - transport=self.mm_sub_master_opts['transport'], - opts=self.mm_sub_master_opts) - - # We have to wait beacon first execution that would configure the inotify watch. - # Since beacons will be executed both together waiting for the first status beacon event - # which will mean the inotify beacon was executed too. - start = time.time() - stop_at = start + self.mm_minion_opts['loop_interval'] * 2 + 60 - event = sub_event = None - while True: - if time.time() > stop_at: - break - if not event: - event = master_listener.get_event( - full=True, - wait=1, - tag='salt/beacon/minion/status', - match_type='startswith', - ) - if sub_event is None: - sub_event = sub_master_listener.get_event( - full=True, - wait=1, - tag='salt/beacon/minion/status', - match_type='startswith', - ) - if event and sub_event: - break - log.debug('Status events received: %s, %s', event, sub_event) - - with salt.utils.files.fopen(file_path, 'w') as f: - pass - - start = time.time() - # Now in successful case this test will get results at most in 2 loop intervals. - # Waiting for 2 loops intervals + some seconds to the hardware stupidity. - stop_at = start + self.mm_minion_opts['loop_interval'] * 2 + 60 - event = sub_event = None - while True: - if time.time() > stop_at: - break - if not event: - event = master_listener.get_event( - full=True, - wait=1, - tag='salt/beacon/minion/inotify/' + self.tmpdir, - match_type='startswith', - ) - if sub_event is None: - sub_event = sub_master_listener.get_event( - full=True, - wait=1, - tag='salt/beacon/minion/inotify/' + self.tmpdir, - match_type='startswith', - ) - if event and sub_event: - break - log.debug('Inotify events received: %s, %s', event, sub_event) - finally: - self.assertTrue(self.run_function( - 'beacons.delete', - ('inotify',), - master_tgt=0, - )) - self.assertTrue(self.run_function( - 'beacons.delete', - ('status',), - master_tgt=0, - )) - master_listener.destroy() - sub_master_listener.destroy() + master_listener = salt.utils.event.get_master_event( + self.mm_master_opts, + sock_dir=self.mm_master_opts['sock_dir']) + self.addCleanup(master_listener.destroy) + sub_master_listener = salt.utils.event.get_master_event( + self.mm_sub_master_opts, + sock_dir=self.mm_sub_master_opts['sock_dir']) + self.addCleanup(sub_master_listener.destroy) + + # We have to wait beacon first execution that would configure the inotify watch. + # Since beacons will be executed both together waiting for the first status beacon event + # which will mean the inotify beacon was executed too. + start = time.time() + stop_at = start + self.mm_minion_opts['loop_interval'] * 2 + 60 + event = sub_event = None + while True: + if time.time() > stop_at: + break + if not event: + event = master_listener.get_event( + full=True, + wait=1, + tag='salt/beacon/mm-minion/status', + match_type='startswith' + ) + if sub_event is None: + sub_event = sub_master_listener.get_event( + full=True, + wait=1, + tag='salt/beacon/mm-minion/status', + match_type='startswith' + ) + if event and sub_event: + break + log.debug('Status events received: %s, %s', event, sub_event) + + if not event or not sub_event: + self.fail('Failed to receive at least one of the status events') + + with salt.utils.files.fopen(file_path, 'w') as f: + pass + + start = time.time() + # Now in successful case this test will get results at most in 2 loop intervals. + # Waiting for 2 loops intervals + some seconds to the hardware stupidity. + stop_at = start + self.mm_minion_opts['loop_interval'] * 3 + 60 + event = sub_event = None + expected_tag = salt.utils.stringutils.to_str( + 'salt/beacon/mm-minion/inotify/{}'.format(self.tmpdir)) + while True: + if time.time() > stop_at: + break + if not event: + event = master_listener.get_event( + full=True, + wait=1, + tag=expected_tag, + match_type='startswith' + ) + if sub_event is None: + sub_event = sub_master_listener.get_event( + full=True, + wait=1, + tag=expected_tag, + match_type='startswith' + ) + if event and sub_event: + break + log.debug('Inotify events received: %s, %s', event, sub_event) + + if not event or not sub_event: + self.fail('Failed to receive at least one of the inotify events') # We can't determine the timestamp so remove it from results if event: @@ -167,9 +159,9 @@ def test_beacons_duplicate_53344(self): 'data': { 'path': file_path, 'change': 'IN_CREATE', - 'id': 'minion', + 'id': 'mm-minion', }, - 'tag': salt.utils.stringutils.to_str('salt/beacon/minion/inotify/' + self.tmpdir), + 'tag': expected_tag } # It's better to compare both at once to see both responses in the error log. diff --git a/tests/multimaster/conftest.py b/tests/multimaster/conftest.py new file mode 100644 index 000000000000..039529cb56cc --- /dev/null +++ b/tests/multimaster/conftest.py @@ -0,0 +1,734 @@ +# -*- coding: utf-8 -*- +''' + tests.multimaster.conftest + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Multimaster PyTest prep routines +''' +from __future__ import absolute_import, print_function, unicode_literals +import os +import shutil +import logging +from collections import OrderedDict + +import pytest +import psutil + +import salt.utils.files +from salt.serializers import yaml +from salt.utils.immutabletypes import freeze +from tests.support.runtests import RUNTIME_VARS +from pytestsalt.fixtures.ports import get_unused_localhost_port +from pytestsalt.fixtures.config import apply_master_config, apply_minion_config +from pytestsalt.fixtures.daemons import SaltMaster, SaltMinion, start_daemon + +log = logging.getLogger(__name__) + +SESSION_ROOT_DIR = 'session-mm-root' +SESSION_SECONDARY_ROOT_DIR = 'session-secondary-mm-root' + + +@pytest.fixture(scope='session') +def session_mm_root_dir(tempdir): + ''' + Return the session scoped salt root dir + ''' + return tempdir.mkdir(SESSION_ROOT_DIR) + + +@pytest.fixture(scope='session') +def session_mm_conf_dir(session_mm_root_dir): + ''' + Return the session scoped salt root dir + ''' + return session_mm_root_dir.join('conf').ensure(dir=True) + + +# ----- Master Fixtures ---------------------------------------------------------------------------------------------> +@pytest.fixture(scope='session') +def session_mm_master_id(): + ''' + Returns the session scoped master id + ''' + return 'mm-master' + + +@pytest.fixture(scope='session') +def session_mm_master_publish_port(): + ''' + Returns an unused localhost port for the master publish interface + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_master_return_port(): + ''' + Returns an unused localhost port for the master return interface + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_master_engine_port(): + ''' + Returns an unused localhost port for the pytest session salt master engine + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_master_tcp_master_pub_port(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_master_tcp_master_pull_port(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_master_tcp_master_publish_pull(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_master_tcp_master_workers(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_master_log_prefix(session_mm_master_id): + return 'salt-master/{}'.format(session_mm_master_id) + + +@pytest.fixture(scope='session') +def session_mm_master_config_file(session_mm_conf_dir): + ''' + Returns the path to the salt master configuration file + ''' + return session_mm_conf_dir.join('master').realpath().strpath + + +@pytest.fixture(scope='session') +def session_mm_master_default_options(session_master_default_options): + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_master')) as rfh: + config_file_opts = yaml.deserialize(rfh.read()) + opts = session_master_default_options.copy() + if config_file_opts: + opts.update(config_file_opts) + return opts + + +@pytest.fixture(scope='session') +def session_mm_master_config_overrides(session_master_config_overrides, + session_mm_root_dir): + overrides = session_master_config_overrides.copy() + pytest_stop_sending_events_file = session_mm_root_dir.join('pytest_mm_stop_sending_events_file').strpath + with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: + wfh.write('') + overrides['pytest_stop_sending_events_file'] = pytest_stop_sending_events_file + return overrides + + +@pytest.fixture(scope='session') +def session_mm_master_config(session_mm_root_dir, + session_mm_master_default_options, + session_mm_master_config_file, + session_mm_master_publish_port, + session_mm_master_return_port, + session_mm_master_engine_port, + session_mm_master_config_overrides, + session_mm_master_id, + session_base_env_state_tree_root_dir, + session_prod_env_state_tree_root_dir, + session_base_env_pillar_tree_root_dir, + session_prod_env_pillar_tree_root_dir, + running_username, + log_server_port, + log_server_level, + engines_dir, + log_handlers_dir, + session_mm_master_log_prefix, + session_mm_master_tcp_master_pub_port, + session_mm_master_tcp_master_pull_port, + session_mm_master_tcp_master_publish_pull, + session_mm_master_tcp_master_workers): + ''' + This fixture will return the salt master configuration options after being + overridden with any options passed from ``session_master_config_overrides`` + ''' + return apply_master_config(session_mm_master_default_options, + session_mm_root_dir, + session_mm_master_config_file, + session_mm_master_publish_port, + session_mm_master_return_port, + session_mm_master_engine_port, + session_mm_master_config_overrides, + session_mm_master_id, + [session_base_env_state_tree_root_dir.strpath], + [session_prod_env_state_tree_root_dir.strpath], + [session_base_env_pillar_tree_root_dir.strpath], + [session_prod_env_pillar_tree_root_dir.strpath], + running_username, + log_server_port, + log_server_level, + engines_dir, + log_handlers_dir, + session_mm_master_log_prefix, + session_mm_master_tcp_master_pub_port, + session_mm_master_tcp_master_pull_port, + session_mm_master_tcp_master_publish_pull, + session_mm_master_tcp_master_workers) + + +@pytest.fixture(scope='session') +def session_mm_salt_master(request, + session_mm_conf_dir, + session_mm_master_id, + session_mm_master_config, + log_server, # pylint: disable=unused-argument + session_mm_master_log_prefix, + cli_master_script_name, + _cli_bin_dir, + _salt_fail_hard, + ): + ''' + Returns a running salt-master + ''' + return start_daemon(request, + daemon_name='salt-master', + daemon_id=session_mm_master_id, + daemon_log_prefix=session_mm_master_log_prefix, + daemon_cli_script_name=cli_master_script_name, + daemon_config=session_mm_master_config, + daemon_config_dir=session_mm_conf_dir, + daemon_class=SaltMaster, + bin_dir_path=_cli_bin_dir, + fail_hard=_salt_fail_hard, + event_listener_config_dir=session_mm_conf_dir, + start_timeout=60) +# <---- Master Fixtures ---------------------------------------------------------------------------------------------- + + +@pytest.fixture(scope='session') +def session_mm_secondary_root_dir(tempdir): + ''' + Return the session scoped salt secondary root dir + ''' + return tempdir.mkdir(SESSION_SECONDARY_ROOT_DIR) + + +@pytest.fixture(scope='session') +def session_mm_secondary_conf_dir(session_mm_secondary_root_dir): + ''' + Return the session scoped salt root dir + ''' + return session_mm_secondary_root_dir.join('conf').ensure(dir=True) + + +# ----- Sub Master Fixtures -----------------------------------------------------------------------------------------> + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_id(): + ''' + Returns the session scoped master id + ''' + return 'mm-sub-master' + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_publish_port(): + ''' + Returns an unused localhost port for the master publish interface + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_return_port(): + ''' + Returns an unused localhost port for the master return interface + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_engine_port(): + ''' + Returns an unused localhost port for the pytest session salt master engine + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_tcp_master_pub_port(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_tcp_master_pull_port(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_tcp_master_publish_pull(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_tcp_master_workers(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_log_prefix(session_mm_secondary_master_id): + return 'salt-master/{}'.format(session_mm_secondary_master_id) + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_config_file(session_mm_secondary_conf_dir): + ''' + Returns the path to the salt master configuration file + ''' + return session_mm_secondary_conf_dir.join('master').realpath().strpath + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_default_options(session_master_default_options): + opts = session_master_default_options.copy() + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_sub_master')) as rfh: + opts.update(yaml.deserialize(rfh.read())) + return opts + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_config_overrides(session_master_config_overrides, + session_mm_secondary_root_dir): + overrides = session_master_config_overrides.copy() + pytest_stop_sending_events_file = session_mm_secondary_root_dir.join('pytest_mm_stop_sending_events_file').strpath + with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: + wfh.write('') + overrides['pytest_stop_sending_events_file'] = pytest_stop_sending_events_file + return overrides + + +@pytest.fixture(scope='session') +def session_mm_secondary_master_config(session_mm_secondary_root_dir, + session_mm_secondary_master_default_options, + session_mm_secondary_master_config_file, + session_mm_secondary_master_publish_port, + session_mm_secondary_master_return_port, + session_mm_secondary_master_engine_port, + session_mm_secondary_master_config_overrides, + session_mm_secondary_master_id, + session_base_env_state_tree_root_dir, + session_prod_env_state_tree_root_dir, + session_base_env_pillar_tree_root_dir, + session_prod_env_pillar_tree_root_dir, + running_username, + log_server_port, + log_server_level, + engines_dir, + log_handlers_dir, + session_mm_secondary_master_log_prefix, + session_mm_secondary_master_tcp_master_pub_port, + session_mm_secondary_master_tcp_master_pull_port, + session_mm_secondary_master_tcp_master_publish_pull, + session_mm_secondary_master_tcp_master_workers): + ''' + This fixture will return the salt master configuration options after being + overridden with any options passed from ``session_master_config_overrides`` + ''' + return apply_master_config(session_mm_secondary_master_default_options, + session_mm_secondary_root_dir, + session_mm_secondary_master_config_file, + session_mm_secondary_master_publish_port, + session_mm_secondary_master_return_port, + session_mm_secondary_master_engine_port, + session_mm_secondary_master_config_overrides, + session_mm_secondary_master_id, + [session_base_env_state_tree_root_dir.strpath], + [session_prod_env_state_tree_root_dir.strpath], + [session_base_env_pillar_tree_root_dir.strpath], + [session_prod_env_pillar_tree_root_dir.strpath], + running_username, + log_server_port, + log_server_level, + engines_dir, + log_handlers_dir, + session_mm_secondary_master_log_prefix, + session_mm_secondary_master_tcp_master_pub_port, + session_mm_secondary_master_tcp_master_pull_port, + session_mm_secondary_master_tcp_master_publish_pull, + session_mm_secondary_master_tcp_master_workers) + + +@pytest.fixture(scope='session') +def session_mm_secondary_salt_master(request, + session_mm_secondary_conf_dir, + session_mm_secondary_master_id, + session_mm_secondary_master_config, + log_server, # pylint: disable=unused-argument + session_mm_secondary_master_log_prefix, + cli_master_script_name, + _cli_bin_dir, + _salt_fail_hard, + session_mm_master_config, + session_mm_salt_master + ): + ''' + Returns a running salt-master + ''' + # The secondary salt master depends on the primarily salt master fixture + # because we need to clone the keys + for keyfile in ('master.pem', 'master.pub'): + shutil.copyfile( + os.path.join(session_mm_master_config['pki_dir'], keyfile), + os.path.join(session_mm_secondary_master_config['pki_dir'], keyfile) + ) + return start_daemon(request, + daemon_name='salt-master', + daemon_id=session_mm_secondary_master_id, + daemon_log_prefix=session_mm_secondary_master_log_prefix, + daemon_cli_script_name=cli_master_script_name, + daemon_config=session_mm_secondary_master_config, + daemon_config_dir=session_mm_secondary_conf_dir, + daemon_class=SaltMaster, + bin_dir_path=_cli_bin_dir, + fail_hard=_salt_fail_hard, + event_listener_config_dir=session_mm_secondary_conf_dir, + start_timeout=60) +# <---- Sub Master Fixtures ------------------------------------------------------------------------------------------ + +# ----- Sub Minion Fixtures ---------------------------------------------------------------------------------------------> +@pytest.fixture(scope='session') +def session_mm_secondary_minion_id(): + ''' + Returns the session scoped minion id + ''' + return 'mm-sub-minion' + + +@pytest.fixture(scope='session') +def session_mm_secondary_minion_tcp_pub_port(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_secondary_minion_tcp_pull_port(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_secondary_minion_log_prefix(session_mm_secondary_minion_id): + return 'salt-minion/{}'.format(session_mm_secondary_minion_id) + + +@pytest.fixture(scope='session') +def session_mm_secondary_minion_config_file(session_mm_secondary_conf_dir): + ''' + Returns the path to the salt minion configuration file + ''' + return session_mm_secondary_conf_dir.join('minion').realpath().strpath + + +@pytest.fixture(scope='session') +def session_mm_secondary_minion_default_options(session_secondary_minion_default_options): + opts = session_secondary_minion_default_options.copy() + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_sub_minion')) as rfh: + opts.update(yaml.deserialize(rfh.read())) + return opts + + +@pytest.fixture(scope='session') +def session_mm_secondary_minion_config_overrides(session_secondary_minion_config_overrides, + session_mm_master_return_port, + session_mm_secondary_master_return_port): + if session_secondary_minion_config_overrides: + opts = session_secondary_minion_config_overrides.copy() + else: + opts = {} + opts['master_port'] = None + opts['master'] = [ + 'localhost:{}'.format(session_mm_master_return_port), + 'localhost:{}'.format(session_mm_secondary_master_return_port) + ] + return opts + + +@pytest.fixture(scope='session') +def session_mm_secondary_minion_config(session_mm_secondary_root_dir, + session_mm_secondary_minion_config_file, + session_mm_secondary_master_return_port, + session_mm_secondary_minion_default_options, + session_mm_secondary_minion_config_overrides, + session_mm_secondary_minion_id, + running_username, + log_server_port, + log_server_level, + log_handlers_dir, + session_mm_secondary_minion_log_prefix, + session_mm_secondary_minion_tcp_pub_port, + session_mm_secondary_minion_tcp_pull_port): + ''' + This fixture will return the session salt minion configuration options after being + overrided with any options passed from ``session_secondary_minion_config_overrides`` + ''' + return apply_minion_config(session_mm_secondary_minion_default_options, + session_mm_secondary_root_dir, + session_mm_secondary_minion_config_file, + session_mm_secondary_master_return_port, + session_mm_secondary_minion_config_overrides, + session_mm_secondary_minion_id, + running_username, + log_server_port, + log_server_level, + log_handlers_dir, + session_mm_secondary_minion_log_prefix, + session_mm_secondary_minion_tcp_pub_port, + session_mm_secondary_minion_tcp_pull_port) + + +@pytest.fixture(scope='session') +def session_mm_secondary_salt_minion(request, + session_mm_salt_master, + session_mm_secondary_salt_master, + session_mm_secondary_minion_id, + session_mm_secondary_minion_config, + session_mm_secondary_minion_log_prefix, + cli_minion_script_name, + log_server, + _cli_bin_dir, + _salt_fail_hard, + session_mm_secondary_conf_dir): + ''' + Returns a running salt-minion + ''' + return start_daemon(request, + daemon_name='salt-minion', + daemon_id=session_mm_secondary_minion_id, + daemon_log_prefix=session_mm_secondary_minion_log_prefix, + daemon_cli_script_name=cli_minion_script_name, + daemon_config=session_mm_secondary_minion_config, + daemon_config_dir=session_mm_secondary_conf_dir, + daemon_class=SaltMinion, + bin_dir_path=_cli_bin_dir, + fail_hard=_salt_fail_hard, + event_listener_config_dir=session_mm_secondary_conf_dir, + start_timeout=60) +# <---- Minion Fixtures ---------------------------------------------------------------------------------------------- + +# ----- Minion Fixtures -----------------------------------------------------------------------------------------> +@pytest.fixture(scope='session') +def session_mm_minion_id(): + ''' + Returns the session scoped minion id + ''' + return 'mm-minion' + + +@pytest.fixture(scope='session') +def session_mm_minion_tcp_pub_port(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_minion_tcp_pull_port(): + ''' + Returns an unused localhost port + ''' + return get_unused_localhost_port() + + +@pytest.fixture(scope='session') +def session_mm_minion_log_prefix(session_mm_minion_id): + return 'salt-minion/{}'.format(session_mm_minion_id) + + +@pytest.fixture(scope='session') +def session_mm_minion_config_file(session_mm_conf_dir): + ''' + Returns the path to the salt minion configuration file + ''' + return session_mm_conf_dir.join('minion').realpath().strpath + + +@pytest.fixture(scope='session') +def session_mm_minion_default_options(session_minion_default_options): + opts = session_minion_default_options.copy() + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_sub_minion')) as rfh: + opts.update(yaml.deserialize(rfh.read())) + return opts + + +@pytest.fixture(scope='session') +def session_mm_minion_config_overrides(session_minion_config_overrides, + session_mm_master_return_port, + session_mm_secondary_master_return_port): + if session_minion_config_overrides: + opts = session_minion_config_overrides.copy() + else: + opts = {} + opts['master_port'] = None + opts['master'] = [ + 'localhost:{}'.format(session_mm_master_return_port), + 'localhost:{}'.format(session_mm_secondary_master_return_port) + ] + return opts + + +@pytest.fixture(scope='session') +def session_mm_minion_config(session_mm_root_dir, + session_mm_minion_config_file, + session_mm_master_return_port, + session_mm_minion_default_options, + session_mm_minion_config_overrides, + session_mm_minion_id, + running_username, + log_server_port, + log_server_level, + log_handlers_dir, + session_mm_minion_log_prefix, + session_mm_minion_tcp_pub_port, + session_mm_minion_tcp_pull_port): + ''' + This fixture will return the session salt minion configuration options after being + overrided with any options passed from ``session_minion_config_overrides`` + ''' + return apply_minion_config(session_mm_minion_default_options, + session_mm_root_dir, + session_mm_minion_config_file, + session_mm_master_return_port, + session_mm_minion_config_overrides, + session_mm_minion_id, + running_username, + log_server_port, + log_server_level, + log_handlers_dir, + session_mm_minion_log_prefix, + session_mm_minion_tcp_pub_port, + session_mm_minion_tcp_pull_port) + + +@pytest.fixture(scope='session') +def session_mm_salt_minion(request, + session_mm_salt_master, + session_mm_secondary_salt_master, + session_mm_minion_id, + session_mm_minion_config, + session_mm_minion_log_prefix, + cli_minion_script_name, + log_server, + _cli_bin_dir, + _salt_fail_hard, + session_mm_conf_dir): + ''' + Returns a running salt-minion + ''' + return start_daemon(request, + daemon_name='salt-minion', + daemon_id=session_mm_minion_id, + daemon_log_prefix=session_mm_minion_log_prefix, + daemon_cli_script_name=cli_minion_script_name, + daemon_config=session_mm_minion_config, + daemon_config_dir=session_mm_conf_dir, + daemon_class=SaltMinion, + bin_dir_path=_cli_bin_dir, + fail_hard=_salt_fail_hard, + event_listener_config_dir=session_mm_conf_dir, + start_timeout=60) +# <---- Sub Minion Fixtures ------------------------------------------------------------------------------------------ + + +@pytest.fixture(scope='session') +def default_session_daemons(request, + log_server, + session_mm_salt_master, + session_mm_secondary_salt_master, + session_mm_salt_minion, + session_mm_secondary_salt_minion, + ): + + request.session.stats_processes.update(OrderedDict(( + ('Salt MM Master', psutil.Process(session_mm_salt_master.pid)), + ('Salt MM Minion', psutil.Process(session_mm_salt_minion.pid)), + ('Salt MM Sub Master', psutil.Process(session_mm_secondary_salt_master.pid)), + ('Salt MM Sub Minion', psutil.Process(session_mm_secondary_salt_minion.pid)), + )).items()) + + # Run tests + yield + + # Stop daemons now(they would be stopped at the end of the test run session + for daemon in (session_mm_secondary_salt_minion, + session_mm_secondary_salt_master, + session_mm_salt_minion, + session_mm_salt_master + ): + try: + daemon.terminate() + except Exception as exc: # pylint: disable=broad-except + log.warning('Failed to terminate daemon: %s', daemon.__class__.__name__) + + +@pytest.fixture(scope='session', autouse=True) +def mm_bridge_pytest_and_runtests(reap_stray_processes, + session_mm_conf_dir, + session_mm_secondary_conf_dir, + session_base_env_pillar_tree_root_dir, + session_base_env_state_tree_root_dir, + session_prod_env_state_tree_root_dir, + session_mm_master_config, + session_mm_minion_config, + session_mm_secondary_master_config, + session_mm_secondary_minion_config, + default_session_daemons): + + # Make sure unittest2 classes know their paths + RUNTIME_VARS.TMP_MM_CONF_DIR = session_mm_conf_dir.realpath().strpath + RUNTIME_VARS.TMP_MM_SUB_CONF_DIR = session_mm_secondary_conf_dir.realpath().strpath + RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR = session_mm_secondary_conf_dir.realpath().strpath + RUNTIME_VARS.TMP_PILLAR_TREE = session_base_env_pillar_tree_root_dir.realpath().strpath + RUNTIME_VARS.TMP_STATE_TREE = session_base_env_state_tree_root_dir.realpath().strpath + RUNTIME_VARS.TMP_PRODENV_STATE_TREE = session_prod_env_state_tree_root_dir.realpath().strpath + + # Make sure unittest2 uses the pytest generated configuration + RUNTIME_VARS.RUNTIME_CONFIGS['mm_master'] = freeze(session_mm_master_config) + RUNTIME_VARS.RUNTIME_CONFIGS['mm_minion'] = freeze(session_mm_minion_config) + RUNTIME_VARS.RUNTIME_CONFIGS['mm_sub_master'] = freeze(session_mm_secondary_master_config) + RUNTIME_VARS.RUNTIME_CONFIGS['mm_sub_minion'] = freeze(session_mm_secondary_minion_config) diff --git a/tests/multimaster/minion/test_event.py b/tests/multimaster/minion/test_event.py index 385c14d938da..5eb6a081b483 100644 --- a/tests/multimaster/minion/test_event.py +++ b/tests/multimaster/minion/test_event.py @@ -33,14 +33,14 @@ def test_minion_hangs_on_master_failure_50814(self): res = self.run_function( 'iptables.append', ('filter', 'INPUT', disconnect_master_rule), - master_tgt=1, + master_tgt='mm-sub-master', ) # Workaround slow beacons.list_available response if not res: res = self.run_function( 'iptables.append', ('filter', 'INPUT', disconnect_master_rule), - master_tgt=1, + master_tgt='mm-sub-master', ) self.assertTrue(res) try: @@ -48,14 +48,14 @@ def test_minion_hangs_on_master_failure_50814(self): res = self.run_function( 'event.send', ('myco/foo/bar',), - master_tgt=1, + master_tgt='mm-sub-master', ) self.assertTrue(res) # Send one more event. Minion was hanging on this. This is fixed by #53417 res = self.run_function( 'event.send', ('myco/foo/bar',), - master_tgt=1, + master_tgt='mm-sub-master', timeout=60, ) self.assertTrue(res, 'Minion is not responding to the second master after the first ' @@ -65,12 +65,13 @@ def test_minion_hangs_on_master_failure_50814(self): # Since minion could be not responsive now use `salt-call --local` for this. res = self.run_call( "iptables.delete filter INPUT rule='{0}'".format(disconnect_master_rule), - local=True) + local=True, + timeout=30) self.assertEqual(res, ['local:']) # Ensure the master is back. res = self.run_function( 'event.send', ('myco/foo/bar',), - master_tgt=0, + master_tgt='mm-master', ) self.assertTrue(res) diff --git a/tests/packdump.py b/tests/packdump.py index 92ed79de29bc..5a230eed946f 100644 --- a/tests/packdump.py +++ b/tests/packdump.py @@ -9,8 +9,8 @@ import sys import pprint -# Import third party libs -import msgpack +# Import Salt libs +import salt.utils.msgpack def dump(path): @@ -21,7 +21,7 @@ def dump(path): print('Not a file') return with open(path, 'rb') as fp_: - data = msgpack.loads(fp_.read()) + data = salt.utils.msgpack.loads(fp_.read()) pprint.pprint(data) diff --git a/tests/runtests.py b/tests/runtests.py index 2e11af588280..32c6c7ed1fc0 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -12,7 +12,6 @@ import time import warnings import collections - TESTS_DIR = os.path.dirname(os.path.normpath(os.path.abspath(__file__))) if os.name == 'nt': TESTS_DIR = TESTS_DIR.replace('\\', '\\\\') @@ -43,6 +42,8 @@ pass # Import salt libs +from salt.ext import six + try: from tests.support.paths import TMP, SYS_TMP_DIR, INTEGRATION_TEST_DIR from tests.support.paths import CODE_DIR as SALT_ROOT @@ -56,7 +57,7 @@ print('Current sys.path:') import pprint pprint.pprint(sys.path) - raise exc + six.reraise(*sys.exc_info()) from tests.integration import TestDaemon, TestDaemonStartFailed # pylint: disable=W0403 from tests.multimaster import MultimasterTestDaemon @@ -907,11 +908,11 @@ def run_multimaster_tests(self): try: print_header( - ' * Setting up Salt daemons to execute tests', + ' * Setting up multimaster Salt daemons to execute tests', top=False, width=getattr(self.options, 'output_columns', PNUM) ) except TypeError: - print_header(' * Setting up Salt daemons to execute tests', top=False) + print_header(' * Setting up multimaster Salt daemons to execute tests', top=False) status = [] diff --git a/tests/support/case.py b/tests/support/case.py index c1fe2630f7fc..44119e583385 100644 --- a/tests/support/case.py +++ b/tests/support/case.py @@ -18,9 +18,7 @@ import re import sys import time -import stat import errno -import signal import textwrap import logging import tempfile @@ -29,93 +27,32 @@ # Import salt testing libs from tests.support.unit import TestCase -from tests.support.helpers import ( - RedirectStdStreams, requires_sshd_server, win32_kill_process_tree -) +from tests.support.helpers import RedirectStdStreams, requires_sshd_server +from tests.support.processes import terminate_process from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import ( - AdaptedConfigurationTestCaseMixin, - SaltClientTestCaseMixin, - SaltMultimasterClientTestCaseMixin, - ) -from tests.support.paths import ScriptPathMixin, INTEGRATION_TEST_DIR, CODE_DIR, PYEXEC, SCRIPT_DIR +from tests.support.mixins import (AdaptedConfigurationTestCaseMixin, + SaltClientTestCaseMixin, + SaltMultimasterClientTestCaseMixin) +from tests.support.paths import INTEGRATION_TEST_DIR, CODE_DIR, PYEXEC, SCRIPT_DIR +from tests.support.cli_scripts import ScriptPathMixin # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import cStringIO, range # pylint: disable=import-error +from salt.ext.six.moves import cStringIO # pylint: disable=import-error STATE_FUNCTION_RUNNING_RE = re.compile( r'''The function (?:"|')(?P.*)(?:"|') is running as PID ''' r'(?P[\d]+) and was started at (?P.*) with jid (?P[\d]+)' ) -SCRIPT_TEMPLATES = { - 'salt': [ - 'from salt.scripts import salt_main\n', - 'if __name__ == \'__main__\':' - ' salt_main()' - ], - 'salt-api': [ - 'import salt.cli\n', - 'def main():', - ' sapi = salt.cli.SaltAPI()', - ' sapi.run()\n', - 'if __name__ == \'__main__\':', - ' main()' - ], - 'common': [ - 'from salt.scripts import salt_{0}\n', - 'if __name__ == \'__main__\':', - ' salt_{0}()' - ] -} log = logging.getLogger(__name__) -class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin): +class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin): ''' Execute a test for a shell command ''' - def get_script_path(self, script_name): - ''' - Return the path to a testing runtime script - ''' - if not os.path.isdir(RUNTIME_VARS.TMP_SCRIPT_DIR): - os.makedirs(RUNTIME_VARS.TMP_SCRIPT_DIR) - - script_path = os.path.join(RUNTIME_VARS.TMP_SCRIPT_DIR, script_name) - if not os.path.isfile(script_path): - log.debug('Generating {0}'.format(script_path)) - - # Late import - import salt.utils.files - - with salt.utils.files.fopen(script_path, 'w') as sfh: - script_template = SCRIPT_TEMPLATES.get(script_name, None) - if script_template is None: - script_template = SCRIPT_TEMPLATES.get('common', None) - if script_template is None: - raise RuntimeError( - '{0} does not know how to handle the {1} script'.format( - self.__class__.__name__, - script_name - ) - ) - contents = ( - '#!{0}\n'.format(sys.executable) + - '\n'.join(script_template).format(script_name.replace('salt-', '')) - ) - sfh.write(contents) - log.debug( - 'Wrote the following contents to temp script %s:\n%s', - script_path, contents - ) - st = os.stat(script_path) - os.chmod(script_path, st.st_mode | stat.S_IEXEC) - - return script_path - def run_salt(self, arg_str, with_retcode=False, catch_stderr=False, timeout=15): r''' Run the ``salt`` CLI tool with the provided arguments @@ -233,11 +170,15 @@ def run_cp(self, arg_str, with_retcode=False, catch_stderr=False): arg_str = '--config-dir {0} {1}'.format(self.config_dir, arg_str) return self.run_script('salt-cp', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr) - def run_call(self, arg_str, with_retcode=False, catch_stderr=False, local=False): + def run_call(self, arg_str, with_retcode=False, catch_stderr=False, local=False, timeout=15): arg_str = '{0} --config-dir {1} {2}'.format('--local' if local else '', self.config_dir, arg_str) - return self.run_script('salt-call', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr) + return self.run_script('salt-call', + arg_str, + with_retcode=with_retcode, + catch_stderr=catch_stderr, + timeout=timeout) def run_cloud(self, arg_str, catch_stderr=False, timeout=None): ''' @@ -374,33 +315,7 @@ def format_return(retcode, stdout, stderr=None, timed_out=False): if process.returncode is not None: break else: - # We've reached the timeout - if term_sent is False: - # Kill the process group since sending the term signal - # would only terminate the shell, not the command - # executed in the shell - if salt.utils.platform.is_windows(): - _, alive = win32_kill_process_tree(process.pid) - if alive: - log.error("Child processes still alive: %s", alive) - else: - os.killpg(os.getpgid(process.pid), signal.SIGINT) - term_sent = True - continue - - try: - # As a last resort, kill the process group - if salt.utils.platform.is_windows(): - _, alive = win32_kill_process_tree(process.pid) - if alive: - log.error("Child processes still alive: %s", alive) - else: - os.killpg(os.getpgid(process.pid), signal.SIGINT) - except OSError as exc: - if exc.errno != errno.ESRCH: - # If errno is not "no such process", raise - raise - + terminate_process(process.pid, kill_children=True) return format_return( process.returncode, *process.communicate(), @@ -848,15 +763,20 @@ def run_function(self, function, arg=(), minion_tgt='minion', timeout=300, maste timeout=timeout, kwarg=kwargs) + if RUNTIME_VARS.PYTEST_SESSION: + fail_or_skip_func = self.fail + else: + fail_or_skip_func = self.skipTest + if minion_tgt not in orig: - self.skipTest( + fail_or_skip_func( 'WARNING(SHOULD NOT HAPPEN #1935): Failed to get a reply ' 'from the minion \'{0}\'. Command output: {1}'.format( minion_tgt, orig ) ) elif orig[minion_tgt] is None and function not in known_to_return_none: - self.skipTest( + fail_or_skip_func( 'WARNING(SHOULD NOT HAPPEN #1935): Failed to get \'{0}\' from ' 'the minion \'{1}\'. Command output: {2}'.format( function, minion_tgt, orig @@ -868,16 +788,6 @@ def run_function(self, function, arg=(), minion_tgt='minion', timeout=300, maste return orig[minion_tgt] - def run_function_all_masters(self, function, arg=(), minion_tgt='minion', timeout=300, **kwargs): - ''' - Run a single salt function from all the masters in multimaster environment - and condition the return down to match the behavior of the raw function call - ''' - ret = [] - for master in range(len(self.clients)): - ret.append(self.run_function(function, arg, minion_tgt, timeout, master_tgt=master, **kwargs)) - return ret - def run_state(self, function, **kwargs): ''' Run the state.single command and return the state return structure @@ -925,7 +835,7 @@ class MultimasterModuleCase(ModuleCase, SaltMultimasterClientTestCaseMixin): Execute a module function ''' - def run_function(self, function, arg=(), minion_tgt='minion', timeout=300, master_tgt=0, **kwargs): + def run_function(self, function, arg=(), minion_tgt='mm-minion', timeout=300, master_tgt='mm-master', **kwargs): ''' Run a single salt function and condition the return down to match the behavior of the raw function call @@ -938,27 +848,38 @@ def run_function(self, function, arg=(), minion_tgt='minion', timeout=300, maste 'ssh.recv_known_host_entries', 'time.sleep' ) - if minion_tgt == 'sub_minion': + if minion_tgt == 'mm-sub-minion': known_to_return_none += ('mine.update',) if 'f_arg' in kwargs: kwargs['arg'] = kwargs.pop('f_arg') if 'f_timeout' in kwargs: kwargs['timeout'] = kwargs.pop('f_timeout') - orig = self.clients[master_tgt].cmd(minion_tgt, - function, - arg, - timeout=timeout, - kwarg=kwargs) + if master_tgt is None: + client = self.clients['mm-master'] + elif isinstance(master_tgt, int): + client = self.clients[list(self.clients)[master_tgt]] + else: + client = self.clients[master_tgt] + orig = client.cmd(minion_tgt, + function, + arg, + timeout=timeout, + kwarg=kwargs) + + if RUNTIME_VARS.PYTEST_SESSION: + fail_or_skip_func = self.fail + else: + fail_or_skip_func = self.skipTest if minion_tgt not in orig: - self.skipTest( + fail_or_skip_func( 'WARNING(SHOULD NOT HAPPEN #1935): Failed to get a reply ' 'from the minion \'{0}\'. Command output: {1}'.format( minion_tgt, orig ) ) elif orig[minion_tgt] is None and function not in known_to_return_none: - self.skipTest( + fail_or_skip_func( 'WARNING(SHOULD NOT HAPPEN #1935): Failed to get \'{0}\' from ' 'the minion \'{1}\'. Command output: {2}'.format( function, minion_tgt, orig @@ -970,14 +891,20 @@ def run_function(self, function, arg=(), minion_tgt='minion', timeout=300, maste return orig[minion_tgt] - def run_function_all_masters(self, function, arg=(), minion_tgt='minion', timeout=300, **kwargs): + def run_function_all_masters(self, function, arg=(), minion_tgt='mm-minion', timeout=300, **kwargs): ''' Run a single salt function from all the masters in multimaster environment and condition the return down to match the behavior of the raw function call ''' ret = [] - for master in range(len(self.clients)): - ret.append(self.run_function(function, arg, minion_tgt, timeout, master_tgt=master, **kwargs)) + for master_id in self.clients: + ret.append( + self.run_function(function, + arg=arg, + minion_tgt=minion_tgt, + timeout=timeout, + master_tgt=master_id, + **kwargs)) return ret @@ -993,8 +920,12 @@ def run_function(self, function, arg=(), timeout=90): behavior of the raw function call ''' orig = self.client.cmd('minion', function, arg, timeout=timeout) + if RUNTIME_VARS.PYTEST_SESSION: + fail_or_skip_func = self.fail + else: + fail_or_skip_func = self.skipTest if 'minion' not in orig: - self.skipTest( + fail_or_skip_func( 'WARNING(SHOULD NOT HAPPEN #1935): Failed to get a reply ' 'from the minion. Command output: {0}'.format(orig) ) diff --git a/tests/support/cherrypy_testclasses.py b/tests/support/cherrypy_testclasses.py index 3686a3597e6f..37d72ad5d6ca 100644 --- a/tests/support/cherrypy_testclasses.py +++ b/tests/support/cherrypy_testclasses.py @@ -11,7 +11,7 @@ import salt.config from tests.support.mock import patch -from tests.support.paths import TMP_CONF_DIR +from tests.support.runtests import RUNTIME_VARS if HAS_CHERRYPY: from tests.support.cptestcase import BaseCherryPyTestCase @@ -39,7 +39,7 @@ def __get_opts__(self): @classmethod def setUpClass(cls): - master_conf = os.path.join(TMP_CONF_DIR, 'master') + master_conf = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master') cls.config = salt.config.client_config(master_conf) cls.base_opts = {} cls.base_opts.update(cls.config) @@ -54,7 +54,7 @@ def setUp(self): self.app = app self.addCleanup(delattr, self, 'app') - master_conf = os.path.join(TMP_CONF_DIR, 'master') + master_conf = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master') client_config = salt.config.client_config(master_conf) base_opts = {} base_opts.update(client_config) diff --git a/tests/support/cli_scripts.py b/tests/support/cli_scripts.py new file mode 100644 index 000000000000..774be16c2435 --- /dev/null +++ b/tests/support/cli_scripts.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +''' + tests.support.cli_scripts + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Code to generate Salt CLI scripts for test runs +''' + +# Import Python Libs +from __future__ import absolute_import, unicode_literals +import os +import sys +import logging + +# Import Pytest Salt libs +from pytestsalt.utils import cli_scripts + +log = logging.getLogger(__name__) + + +def get_script_path(bin_dir, script_name): + ''' + Return the path to a testing runtime script, generating one if it does not yet exist + ''' + # Late import + from tests.support.runtests import RUNTIME_VARS + + if not os.path.isdir(bin_dir): + os.makedirs(bin_dir) + + cli_script_name = 'cli_{}.py'.format(script_name.replace('-', '_')) + script_path = os.path.join(bin_dir, cli_script_name) + + if not os.path.isfile(script_path): + cli_scripts.generate_script( + bin_dir=bin_dir, + script_name=script_name, + executable=sys.executable, + code_dir=RUNTIME_VARS.CODE_DIR, + inject_sitecustomize='COVERAGE_PROCESS_START' in os.environ + ) + log.info('Returning script path %r', script_path) + return script_path + + +class ScriptPathMixin(object): + + def get_script_path(self, script_name): + ''' + Return the path to a testing runtime script + ''' + # Late import + from tests.support.runtests import RUNTIME_VARS + return get_script_path(RUNTIME_VARS.TMP_SCRIPT_DIR, script_name) diff --git a/tests/support/events.py b/tests/support/events.py new file mode 100644 index 000000000000..f6e09be8f08e --- /dev/null +++ b/tests/support/events.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +''' + tests.support.events + ~~~~~~~~~~~~~~~~~~~~ +''' + +# Import Python libs +from __future__ import absolute_import, unicode_literals +import os +import time +import multiprocessing +from contextlib import contextmanager + +# Import Salt libs +import salt.utils.event +from salt.utils.process import clean_proc + + +@contextmanager +def eventpublisher_process(sock_dir): + proc = salt.utils.event.EventPublisher({'sock_dir': sock_dir}) + proc.start() + try: + if os.environ.get('TRAVIS_PYTHON_VERSION', None) is not None: + # Travis is slow + time.sleep(10) + else: + time.sleep(2) + yield + finally: + clean_proc(proc) + + +class EventSender(multiprocessing.Process): + def __init__(self, data, tag, wait, sock_dir): + super(EventSender, self).__init__() + self.data = data + self.tag = tag + self.wait = wait + self.sock_dir = sock_dir + + def run(self): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=False) + time.sleep(self.wait) + me.fire_event(self.data, self.tag) + # Wait a few seconds before tearing down the zmq context + if os.environ.get('TRAVIS_PYTHON_VERSION', None) is not None: + # Travis is slow + time.sleep(10) + else: + time.sleep(2) + + +@contextmanager +def eventsender_process(data, tag, sock_dir, wait=0): + proc = EventSender(data, tag, wait, sock_dir=sock_dir) + proc.start() + try: + yield + finally: + clean_proc(proc) diff --git a/tests/support/gitfs.py b/tests/support/gitfs.py index 8a7ee9d9f8f1..e2b8107b1ed5 100644 --- a/tests/support/gitfs.py +++ b/tests/support/gitfs.py @@ -33,7 +33,7 @@ from tests.support.case import ModuleCase from tests.support.unit import SkipTest from tests.support.mixins import AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin, SaltReturnAssertsMixin -from tests.support.helpers import get_unused_localhost_port, requires_system_grains +from tests.support.helpers import get_unused_localhost_port, requires_system_grains, patched_environ from tests.support.runtests import RUNTIME_VARS from tests.support.mock import patch from pytestsalt.utils import SaltDaemonScriptBase as _SaltDaemonScriptBase, terminate_process @@ -129,13 +129,12 @@ def start_daemon(daemon_cli_script_name, daemon_class.__name__, attempts ) - return process + break else: terminate_process(process.pid, kill_children=True, slow_stop=slow_stop) time.sleep(1) continue - else: # pylint: disable=useless-else-on-loop - # Wrong, we have a return, its not useless + else: if process is not None: terminate_process(process.pid, kill_children=True, slow_stop=slow_stop) raise AssertionError( @@ -144,6 +143,7 @@ def start_daemon(daemon_cli_script_name, attempts-1 ) ) + return process class SaltDaemonScriptBase(_SaltDaemonScriptBase): @@ -318,7 +318,7 @@ class SaltClientMixin(ModuleCase): @classmethod @requires_system_grains - def setUpClass(cls, grains=None): + def setUpClass(cls, grains=None): # pylint: disable=arguments-differ # Cent OS 6 has too old a version of git to handle the make_repo code, as # it lacks the -c option for git itself. make_repo = getattr(cls, 'make_repo', None) @@ -444,6 +444,8 @@ def tearDownClass(cls): except KeyError: log.warning('Failed to delete test account. Salt return:\n%s', pprint.pformat(ret)) + cls.prep_states_ran = False + cls.known_hosts_setup = False shutil.rmtree(cls.sshd_config_dir, ignore_errors=True) ssh_dir = os.path.expanduser('~/.ssh') for filename in (cls.id_rsa_nopass, @@ -556,6 +558,7 @@ def tearDownClass(cls): log.info('[%s] %s stopped', cls.uwsgi_proc.log_prefix, cls.uwsgi_proc.__class__.__name__) cls.uwsgi_proc = None shutil.rmtree(cls.root_dir, ignore_errors=True) + cls.prep_states_ran = False super(WebserverMixin, cls).tearDownClass() @@ -893,15 +896,8 @@ def get_pillar(self, ext_pillar_conf): passphraselsess key is used to auth without needing to modify the root user's ssh config file. ''' - - def cleanup_environ(environ): - os.environ.clear() - os.environ.update(environ) - - self.addCleanup(cleanup_environ, os.environ.copy()) - - os.environ['GIT_SSH'] = self.git_ssh - return super(GitPillarSSHTestBase, self).get_pillar(ext_pillar_conf) + with patched_environ(GIT_SSH=self.git_ssh): + return super(GitPillarSSHTestBase, self).get_pillar(ext_pillar_conf) class GitPillarHTTPTestBase(GitPillarTestBase, WebserverMixin): diff --git a/tests/support/helpers.py b/tests/support/helpers.py index 267e4682b2ae..6fc0e79bc060 100644 --- a/tests/support/helpers.py +++ b/tests/support/helpers.py @@ -15,13 +15,13 @@ from __future__ import absolute_import, print_function, unicode_literals import base64 import errno +import fnmatch import functools import inspect import logging import os import random import shutil -import signal import socket import string import subprocess @@ -35,29 +35,20 @@ import types # Import 3rd-party libs -import psutil # pylint: disable=3rd-party-module-not-gated from salt.ext import six from salt.ext.six.moves import range, builtins # pylint: disable=import-error,redefined-builtin -try: - from pytestsalt.utils import get_unused_localhost_port # pylint: disable=unused-import -except ImportError: - from tests.integration import get_unused_localhost_port +from pytestsalt.utils import get_unused_localhost_port # Import Salt Tests Support libs -from tests.support.unit import skip, _id +from tests.support.unit import skip, _id, SkipTest from tests.support.mock import patch -from tests.support.paths import FILES, TMP +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.utils.files import salt.utils.platform import salt.utils.stringutils -if salt.utils.platform.is_windows(): - import salt.utils.win_functions -else: - import pwd - log = logging.getLogger(__name__) HAS_SYMLINKS = None @@ -74,7 +65,7 @@ def no_symlinks(): try: output = subprocess.Popen( ['git', 'config', '--get', 'core.symlinks'], - cwd=TMP, + cwd=RUNTIME_VARS.TMP, stdout=subprocess.PIPE).communicate()[0] except OSError as exc: if exc.errno != errno.ENOENT: @@ -101,6 +92,11 @@ class MyTestCase(TestCase): def test_create_user(self): pass ''' + # Late import + from tests.support.runtests import RUNTIME_VARS + if RUNTIME_VARS.PYTEST_SESSION: + setattr(caller, '__destructive_test__', True) + if inspect.isclass(caller): # We're decorating a class old_setup = getattr(caller, 'setUp', None) @@ -135,6 +131,11 @@ class MyTestCase(TestCase): def test_create_user(self): pass ''' + # Late import + from tests.support.runtests import RUNTIME_VARS + if RUNTIME_VARS.PYTEST_SESSION: + setattr(caller, '__expensive_test__', True) + if inspect.isclass(caller): # We're decorating a class old_setup = getattr(caller, 'setUp', None) @@ -198,10 +199,30 @@ def test_sometimes_works(self): def wrap(cls): for attempt in range(0, attempts): try: + if attempt > 0: + # Run through setUp again + # We only run it after the first iteration(>0) because the regular + # test runner will have already ran setUp the first time + setup = getattr(cls, 'setUp', None) + if callable(setup): + setup() return caller(cls) + except SkipTest as exc: + cls.skipTest(exc.args[0]) except Exception as exc: + exc_info = sys.exc_info() + if isinstance(exc, SkipTest): + six.reraise(*exc_info) + if not isinstance(exc, AssertionError) and log.isEnabledFor(logging.DEBUG): + log.exception(exc, exc_info=exc_info) if attempt >= attempts -1: - raise exc + # We won't try to run tearDown once the attempts are exhausted + # because the regular test runner will do that for us + six.reraise(*exc_info) + # Run through tearDown again + teardown = getattr(cls, 'tearDown', None) + if callable(teardown): + teardown() backoff_time = attempt ** 2 log.info( 'Found Exception. Waiting %s seconds to retry.', @@ -309,14 +330,14 @@ def flush(self): pass -class TestsLoggingHandler(object): +class TstSuiteLoggingHandler(object): ''' Simple logging handler which can be used to test if certain logging messages get emitted or not: .. code-block:: python - with TestsLoggingHandler() as handler: + with TstSuiteLoggingHandler() as handler: # (...) Do what ever you wish here handler.messages # here are the emitted log messages @@ -392,27 +413,6 @@ def release(self): return self.handler.release() -def relative_import(import_name, relative_from='../'): - ''' - Update sys.path to include `relative_from` before importing `import_name` - ''' - try: - return __import__(import_name) - except ImportError: - previous_frame = inspect.getframeinfo(inspect.currentframe().f_back) - sys.path.insert( - 0, os.path.realpath( - os.path.join( - os.path.abspath( - os.path.dirname(previous_frame.filename) - ), - relative_from - ) - ) - ) - return __import__(import_name) - - class ForceImportErrorOn(object): ''' This class is meant to be used in mock'ed test cases which require an @@ -997,7 +997,7 @@ class WithTempfile(object): def __init__(self, **kwargs): self.create = kwargs.pop('create', True) if 'dir' not in kwargs: - kwargs['dir'] = TMP + kwargs['dir'] = RUNTIME_VARS.TMP if 'prefix' not in kwargs: kwargs['prefix'] = '__salt.test.' self.kwargs = kwargs @@ -1028,7 +1028,7 @@ class WithTempdir(object): def __init__(self, **kwargs): self.create = kwargs.pop('create', True) if 'dir' not in kwargs: - kwargs['dir'] = TMP + kwargs['dir'] = RUNTIME_VARS.TMP self.kwargs = kwargs def __call__(self, func): @@ -1058,21 +1058,10 @@ def requires_system_grains(func): @functools.wraps(func) def decorator(*args, **kwargs): if not hasattr(requires_system_grains, '__grains__'): - import salt.config - root_dir = tempfile.mkdtemp(dir=TMP) - defaults = salt.config.DEFAULT_MINION_OPTS.copy() - defaults.pop('conf_file') - defaults.update({ - 'root_dir': root_dir, - 'cachedir': 'cachedir', - 'sock_dir': 'sock', - 'pki_dir': 'pki', - 'log_file': 'logs/minion', - 'pidfile': 'pids/minion.pid' - }) - opts = salt.config.minion_config(None, defaults=defaults) + # Late import + from tests.support.sminion import build_minion_opts + opts = build_minion_opts(minion_id='runtests-internal-sminion') requires_system_grains.__grains__ = salt.loader.grains(opts) - shutil.rmtree(root_dir, ignore_errors=True) kwargs['grains'] = requires_system_grains.__grains__ return func(*args, **kwargs) return decorator @@ -1084,75 +1073,58 @@ def requires_salt_modules(*names): .. versionadded:: 0.5.2 ''' - def decorator(caller): + def _check_required_salt_modules(*required_salt_modules): + # Late import + from tests.support.sminion import create_sminion + required_salt_modules = set(required_salt_modules) + sminion = create_sminion(minion_id='runtests-internal-sminion') + available_modules = list(sminion.functions) + not_available_modules = set() + try: + cached_not_available_modules = sminion.__not_availiable_modules__ + except AttributeError: + cached_not_available_modules = sminion.__not_availiable_modules__ = set() + + if cached_not_available_modules: + for not_available_module in cached_not_available_modules: + if not_available_module in required_salt_modules: + not_available_modules.add(not_available_module) + required_salt_modules.remove(not_available_module) + + for required_module_name in required_salt_modules: + search_name = required_module_name + if '.' not in search_name: + search_name += '.*' + if not fnmatch.filter(available_modules, search_name): + not_available_modules.add(required_module_name) + cached_not_available_modules.add(required_module_name) + + if not_available_modules: + if len(not_available_modules) == 1: + raise SkipTest('Salt module \'{}\' is not available'.format(*not_available_modules)) + raise SkipTest('Salt modules not available: {}'.format(', '.join(not_available_modules))) + def decorator(caller): if inspect.isclass(caller): # We're decorating a class old_setup = getattr(caller, 'setUp', None) def setUp(self, *args, **kwargs): + _check_required_salt_modules(*names) if old_setup is not None: old_setup(self, *args, **kwargs) - if not hasattr(self, 'run_function'): - raise RuntimeError( - '{0} does not have the \'run_function\' method which ' - 'is necessary to collect the loaded modules'.format( - self.__class__.__name__ - ) - ) - - if not hasattr(requires_salt_modules, '__available_modules__'): - requires_salt_modules.__available_modules__ = set() - - _names = [] - for name in names: - if name not in requires_salt_modules.__available_modules__: - _names.append(name) - - if _names: - not_found_modules = self.run_function('runtests_helpers.modules_available', _names) - for name in _names: - if name not in not_found_modules: - requires_salt_modules.__available_modules__.add(name) - if not_found_modules: - if len(not_found_modules) == 1: - self.skipTest('Salt module {0!r} is not available'.format(not_found_modules[0])) - self.skipTest('Salt modules not available: {0!r}'.format(not_found_modules)) caller.setUp = setUp return caller # We're simply decorating functions @functools.wraps(caller) def wrapper(cls): - - if not hasattr(cls, 'run_function'): - raise RuntimeError( - '{0} does not have the \'run_function\' method which is ' - 'necessary to collect the loaded modules'.format( - cls.__class__.__name__ - ) - ) - - if not hasattr(requires_salt_modules, '__available_modules__'): - requires_salt_modules.__available_modules__ = set() - - _names = [] - for name in names: - if name not in requires_salt_modules.__available_modules__: - _names.append(name) - - if _names: - not_found_modules = cls.run_function('runtests_helpers.modules_available', _names) - for name in _names: - if name not in not_found_modules: - requires_salt_modules.__available_modules__.add(name) - if not_found_modules: - if len(not_found_modules) == 1: - cls.skipTest('Salt module {0!r} is not available'.format(not_found_modules[0])) - cls.skipTest('Salt modules not available: {0!r}'.format(not_found_modules)) + _check_required_salt_modules(*names) return caller(cls) + return wrapper + return decorator @@ -1190,6 +1162,11 @@ def skip_if_binaries_missing(*binaries, **kwargs): def skip_if_not_root(func): + # Late import + from tests.support.runtests import RUNTIME_VARS + if RUNTIME_VARS.PYTEST_SESSION: + setattr(func, '__skip_if_not_root__', True) + if not sys.platform.startswith('win'): if os.getuid() != 0: func.__unittest_skip__ = True @@ -1203,172 +1180,6 @@ def skip_if_not_root(func): return func -if sys.platform.startswith('win'): - SIGTERM = signal.CTRL_BREAK_EVENT # pylint: disable=no-member -else: - SIGTERM = signal.SIGTERM - - -def collect_child_processes(pid): - ''' - Try to collect any started child processes of the provided pid - ''' - # Let's get the child processes of the started subprocess - try: - parent = psutil.Process(pid) - if hasattr(parent, 'children'): - children = parent.children(recursive=True) - else: - children = [] - except psutil.NoSuchProcess: - children = [] - return children[::-1] # return a reversed list of the children - - -def _terminate_process_list(process_list, kill=False, slow_stop=False): - for process in process_list[:][::-1]: # Iterate over a reversed copy of the list - if not psutil.pid_exists(process.pid): - process_list.remove(process) - continue - try: - if not kill and process.status() == psutil.STATUS_ZOMBIE: - # Zombie processes will exit once child processes also exit - continue - try: - cmdline = process.cmdline() - except psutil.AccessDenied: - # OSX is more restrictive about the above information - cmdline = None - except OSError: - cmdline = None - if not cmdline: - try: - cmdline = process.as_dict() - except Exception: - cmdline = 'UNKNOWN PROCESS' - if kill: - log.info('Killing process(%s): %s', process.pid, cmdline) - process.kill() - else: - log.info('Terminating process(%s): %s', process.pid, cmdline) - try: - if slow_stop: - # Allow coverage data to be written down to disk - process.send_signal(SIGTERM) - try: - process.wait(2) - except psutil.TimeoutExpired: - if psutil.pid_exists(process.pid): - continue - else: - process.terminate() - except OSError as exc: - if exc.errno not in (errno.ESRCH, errno.EACCES): - raise - if not psutil.pid_exists(process.pid): - process_list.remove(process) - except psutil.NoSuchProcess: - process_list.remove(process) - - -def terminate_process_list(process_list, kill=False, slow_stop=False): - - def on_process_terminated(proc): - log.info('Process %s terminated with exit code: %s', getattr(proc, '_cmdline', proc), proc.returncode) - - # Try to terminate processes with the provided kill and slow_stop parameters - log.info('Terminating process list. 1st step. kill: %s, slow stop: %s', kill, slow_stop) - - # Cache the cmdline since that will be inaccessible once the process is terminated - for proc in process_list: - try: - cmdline = proc.cmdline() - except (psutil.NoSuchProcess, psutil.AccessDenied): - # OSX is more restrictive about the above information - cmdline = None - if not cmdline: - try: - cmdline = proc - except (psutil.NoSuchProcess, psutil.AccessDenied): - cmdline = ''.format(proc) - proc._cmdline = cmdline - _terminate_process_list(process_list, kill=kill, slow_stop=slow_stop) - psutil.wait_procs(process_list, timeout=15, callback=on_process_terminated) - - if process_list: - # If there's still processes to be terminated, retry and kill them if slow_stop is False - log.info('Terminating process list. 2nd step. kill: %s, slow stop: %s', slow_stop is False, slow_stop) - _terminate_process_list(process_list, kill=slow_stop is False, slow_stop=slow_stop) - psutil.wait_procs(process_list, timeout=10, callback=on_process_terminated) - - if process_list: - # If there's still processes to be terminated, just kill them, no slow stopping now - log.info('Terminating process list. 3rd step. kill: True, slow stop: False') - _terminate_process_list(process_list, kill=True, slow_stop=False) - psutil.wait_procs(process_list, timeout=5, callback=on_process_terminated) - - if process_list: - # In there's still processes to be terminated, log a warning about it - log.warning('Some processes failed to properly terminate: %s', process_list) - - -def terminate_process(pid=None, process=None, children=None, kill_children=False, slow_stop=False): - ''' - Try to terminate/kill the started processe - ''' - children = children or [] - process_list = [] - - def on_process_terminated(proc): - if proc.returncode: - log.info('Process %s terminated with exit code: %s', getattr(proc, '_cmdline', proc), proc.returncode) - else: - log.info('Process %s terminated', getattr(proc, '_cmdline', proc)) - - if pid and not process: - try: - process = psutil.Process(pid) - process_list.append(process) - except psutil.NoSuchProcess: - # Process is already gone - process = None - - if kill_children: - if process: - if not children: - children = collect_child_processes(process.pid) - else: - # Let's collect children again since there might be new ones - children.extend(collect_child_processes(pid)) - if children: - process_list.extend(children) - - if process_list: - if process: - log.info('Stopping process %s and respective children: %s', process, children) - else: - log.info('Terminating process list: %s', process_list) - terminate_process_list(process_list, kill=slow_stop is False, slow_stop=slow_stop) - if process and psutil.pid_exists(process.pid): - log.warning('Process left behind which we were unable to kill: %s', process) - - -def terminate_process_pid(pid, only_children=False): - children = [] - process = None - - # Let's begin the shutdown routines - try: - process = psutil.Process(pid) - children = collect_child_processes(pid) - except psutil.NoSuchProcess: - log.info('No process with the PID %s was found running', pid) - - if only_children: - return terminate_process(children=children, kill_children=True, slow_stop=True) - return terminate_process(pid=pid, process=process, children=children, kill_children=True, slow_stop=True) - - def repeat(caller=None, condition=True, times=5): ''' Repeat a test X amount of times until the first failure. @@ -1528,7 +1339,7 @@ def __init__(self, raise ValueError('port must be an integer') if root is None: - root = os.path.join(FILES, 'file', 'base') + root = RUNTIME_VARS.BASE_FILES try: self.root = os.path.realpath(root) except AttributeError: @@ -1547,8 +1358,12 @@ def target(self): ''' self.ioloop = tornado.ioloop.IOLoop() self.ioloop.make_current() - self.application = tornado.web.Application( - [(r'/(.*)', self.handler, {'path': self.root})]) + if self.handler == tornado.web.StaticFileHandler: + self.application = tornado.web.Application( + [(r'/(.*)', self.handler, {'path': self.root})]) + else: + self.application = tornado.web.Application( + [(r'/(.*)', self.handler)]) self.application.listen(self.port) self.ioloop.start() @@ -1611,37 +1426,23 @@ def stop(self): self.server_thread.join() -def win32_kill_process_tree(pid, sig=signal.SIGTERM, include_parent=True, - timeout=None, on_terminate=None): - ''' - Kill a process tree (including grandchildren) with signal "sig" and return - a (gone, still_alive) tuple. "on_terminate", if specified, is a callabck - function which is called as soon as a child terminates. +class MirrorPostHandler(tornado.web.RequestHandler): ''' - if pid == os.getpid(): - raise RuntimeError("I refuse to kill myself") - try: - parent = psutil.Process(pid) - except psutil.NoSuchProcess: - log.debug("PID not found alive: %d", pid) - return ([], []) - children = parent.children(recursive=True) - if include_parent: - children.append(parent) - for p in children: - p.send_signal(sig) - gone, alive = psutil.wait_procs(children, timeout=timeout, - callback=on_terminate) - return (gone, alive) - - -def this_user(): - ''' - Get the user associated with the current process. + Mirror a POST body back to the client ''' - if salt.utils.platform.is_windows(): - return salt.utils.win_functions.get_current_user(with_domain=False) - return pwd.getpwuid(os.getuid())[0] + def post(self, *args): # pylint: disable=arguments-differ + ''' + Handle the post + ''' + body = self.request.body + log.debug('Incoming body: %s Incoming args: %s', body, args) + self.write(body) + + def data_received(self): # pylint: disable=arguments-differ + ''' + Streaming not used for testing + ''' + raise NotImplementedError() def dedent(text, linesep=os.linesep): @@ -1656,3 +1457,41 @@ def dedent(text, linesep=os.linesep): if not isinstance(text, six.text_type): return salt.utils.stringutils.to_bytes(clean_text) return clean_text + + +class PatchedEnviron(object): + + def __init__(self, **kwargs): + self.cleanup_keys = kwargs.pop('__cleanup__', ()) + self.kwargs = kwargs + self.original_environ = None + + def __enter__(self): + self.original_environ = os.environ.copy() + for key in self.cleanup_keys: + os.environ.pop(key, None) + + # Make sure there are no unicode characters in the self.kwargs if we're + # on Python 2. These are being added to `os.environ` and causing + # problems + if sys.version_info < (3,): + kwargs = self.kwargs.copy() + clean_kwargs = {} + for k in self.kwargs: + key = k + if isinstance(key, six.text_type): + key = key.encode('utf-8') + if isinstance(self.kwargs[k], six.text_type): + kwargs[k] = kwargs[k].encode('utf-8') + clean_kwargs[key] = kwargs[k] + self.kwargs = clean_kwargs + + os.environ.update(**self.kwargs) + return self + + def __exit__(self, *args): + os.environ.clear() + os.environ.update(self.original_environ) + + +patched_environ = PatchedEnviron diff --git a/tests/support/jinja_filters.py b/tests/support/jinja_filters.py index d596d242a9cc..0091063afe8a 100644 --- a/tests/support/jinja_filters.py +++ b/tests/support/jinja_filters.py @@ -31,7 +31,7 @@ def test_data_compare_lists(self): ''' test jinja filter data.compare_list ''' - _expected = {'ret': {'old': 'b'}} + _expected = {'ret': {'old': ['b']}} ret = self.run_function('state.sls', ['jinja_filters.data_compare_lists']) self.assertIn('module_|-test_|-test.echo_|-run', ret) diff --git a/tests/support/mixins.py b/tests/support/mixins.py index 45cc6b37ed33..fc92a6877921 100644 --- a/tests/support/mixins.py +++ b/tests/support/mixins.py @@ -14,6 +14,7 @@ from __future__ import absolute_import, print_function import os import sys +import copy import time import types import atexit @@ -23,9 +24,10 @@ import functools import subprocess import multiprocessing +from collections import OrderedDict # Import Salt Testing Libs -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch +from tests.support.mock import patch from tests.support.runtests import RUNTIME_VARS from tests.support.paths import CODE_DIR @@ -70,7 +72,20 @@ def test_version_includes_binary_name(self): self._call_binary_expected_version_ = salt.version.__version__ out = '\n'.join(self.run_script(self._call_binary_, '--version')) - self.assertIn(self._call_binary_, out) + # Assert that the binary name is in the output + try: + self.assertIn(self._call_binary_, out) + except AssertionError: + # We might have generated the CLI scripts in which case we replace '-' with '_' + alternate_binary_name = self._call_binary_.replace('-', '_') + errmsg = 'Neither \'{}\' or \'{}\' were found as part of the binary name in:\n\'{}\''.format( + self._call_binary_, + alternate_binary_name, + out + ) + self.assertIn(alternate_binary_name, out, msg=errmsg) + + # Assert that the version is in the output self.assertIn(self._call_binary_expected_version_, out) @@ -80,8 +95,8 @@ class AdaptedConfigurationTestCaseMixin(object): @staticmethod def get_temp_config(config_for, **config_overrides): - rootdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - conf_dir = os.path.join(rootdir, 'conf') + rootdir = config_overrides.get('root_dir', tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)) + conf_dir = config_overrides.pop('conf_dir', os.path.join(rootdir, 'conf')) for key in ('cachedir', 'pki_dir', 'sock_dir'): if key not in config_overrides: config_overrides[key] = key @@ -116,7 +131,6 @@ def get_temp_config(config_for, **config_overrides): root_dir=rdict['root_dir'], ) - rdict['config_dir'] = conf_dir rdict['conf_file'] = os.path.join(conf_dir, config_for) with salt.utils.files.fopen(rdict['conf_file'], 'w') as wfh: salt.utils.yaml.safe_dump(rdict, wfh, default_flow_style=False) @@ -310,9 +324,15 @@ def clients(self): # Late import import salt.client if 'runtime_clients' not in RUNTIME_VARS.RUNTIME_CONFIGS: - mopts = self.get_config(self._salt_client_config_file_name_, from_scratch=True) - RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] = salt.client.get_local_client(mopts=mopts) - return RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] + RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] = OrderedDict() + + runtime_clients = RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] + for master_id in ('mm-master', 'mm-sub-master'): + if master_id in runtime_clients: + continue + mopts = self.get_config(master_id.replace('-', '_'), from_scratch=True) + runtime_clients[master_id] = salt.client.get_local_client(mopts=mopts) + return runtime_clients class ShellCaseCommonTestsMixin(CheckShellBinaryNameAndVersionMixin): @@ -410,9 +430,6 @@ def __setup_loader_modules_mocks__(setup_func): @functools.wraps(setup_func) def wrapper(self): - if NO_MOCK: - self.skipTest(NO_MOCK_REASON) - loader_modules_configs = self.setup_loader_modules() if not isinstance(loader_modules_configs, dict): raise RuntimeError( @@ -710,23 +727,18 @@ def assertSaltStateChangesNotEqual(self, ret, comparison, keys=()): self.assertNotEqual(saltret, comparison) -def _fetch_events(q): +def _fetch_events(q, opts): ''' Collect events and store them ''' def _clean_queue(): - print('Cleaning queue!') + log.info('Cleaning queue!') while not q.empty(): queue_item = q.get() queue_item.task_done() atexit.register(_clean_queue) - a_config = AdaptedConfigurationTestCaseMixin() - event = salt.utils.event.get_event( - 'minion', - sock_dir=a_config.get_config('minion')['sock_dir'], - opts=a_config.get_config('minion'), - ) + event = salt.utils.event.get_event('minion', sock_dir=opts['sock_dir'], opts=opts) # Wait for event bus to be connected while not event.connect_pull(30): @@ -750,11 +762,14 @@ class SaltMinionEventAssertsMixin(object): Asserts to verify that a given event was seen ''' - def __new__(cls, *args, **kwargs): - # We have to cross-call to re-gen a config + @classmethod + def setUpClass(cls): + opts = copy.deepcopy(RUNTIME_VARS.RUNTIME_CONFIGS['minion']) cls.q = multiprocessing.Queue() - cls.fetch_proc = salt.utils.process.SignalHandlingMultiprocessingProcess( - target=_fetch_events, args=(cls.q,) + cls.fetch_proc = salt.utils.process.SignalHandlingProcess( + target=_fetch_events, + args=(cls.q, opts), + name='Process-{}-Queue'.format(cls.__name__) ) cls.fetch_proc.start() # Wait for the event bus to be connected @@ -762,10 +777,12 @@ def __new__(cls, *args, **kwargs): if msg != 'CONNECTED': # Just in case something very bad happens raise RuntimeError('Unexpected message in test\'s event queue') - return object.__new__(cls) - def __exit__(self, *args, **kwargs): - self.fetch_proc.join() + @classmethod + def tearDownClass(cls): + cls.fetch_proc.join() + del cls.q + del cls.fetch_proc def assertMinionEventFired(self, tag): #TODO diff --git a/tests/support/mock.py b/tests/support/mock.py index 38b68bd5c4c4..805a60377cab 100644 --- a/tests/support/mock.py +++ b/tests/support/mock.py @@ -25,75 +25,26 @@ from salt.ext import six import salt.utils.stringutils -try: - from mock import ( - Mock, - MagicMock, - patch, - sentinel, - DEFAULT, - # ANY and call will be imported further down - create_autospec, - FILTER_DIR, - NonCallableMock, - NonCallableMagicMock, - PropertyMock, - __version__ - ) - NO_MOCK = False - NO_MOCK_REASON = '' - mock_version = [] - for __part in __version__.split('.'): - try: - mock_version.append(int(__part)) - except ValueError: - # Non-integer value (ex. '1a') - mock_version.append(__part) - mock_version = tuple(mock_version) -except ImportError as exc: - NO_MOCK = True - NO_MOCK_REASON = 'mock python module is unavailable' - mock_version = (0, 0, 0) - - # Let's not fail on imports by providing fake objects and classes - - class MagicMock(object): - - # __name__ can't be assigned a unicode - __name__ = str('{0}.fakemock').format(__name__) # future lint: disable=blacklisted-function - - def __init__(self, *args, **kwargs): - pass - - def dict(self, *args, **kwargs): - return self - - def multiple(self, *args, **kwargs): - return self - - def __call__(self, *args, **kwargs): - return self - - Mock = MagicMock - patch = MagicMock() - sentinel = object() - DEFAULT = object() - create_autospec = MagicMock() - FILTER_DIR = True - NonCallableMock = MagicMock() - NonCallableMagicMock = MagicMock() - mock_open = object() - PropertyMock = object() - call = tuple - ANY = object() - - -if NO_MOCK is False: - try: - from mock import call, ANY - except ImportError: - NO_MOCK = True - NO_MOCK_REASON = 'you need to upgrade your mock version to >= 0.8.0' +# By these days, we should blowup if mock is not available +import mock # pylint: disable=blacklisted-external-import +__mock_version = tuple([int(part) for part in mock.__version__.split('.') if part.isdigit()]) # pylint: disable=no-member +if sys.version_info < (3, 6) and __mock_version < (2,): + # We need mock >= 2.0.0 before Py3.6 + raise ImportError('Please install mock>=2.0.0') + +from mock import (Mock, # pylint: disable=no-name-in-module,no-member + MagicMock, + patch, + sentinel, + DEFAULT, + create_autospec, + FILTER_DIR, + NonCallableMock, + NonCallableMagicMock, + PropertyMock, + __version__, + ANY, + call) class MockFH(object): diff --git a/tests/support/parser/__init__.py b/tests/support/parser/__init__.py index a188f8945f1a..9313a1aa81e0 100644 --- a/tests/support/parser/__init__.py +++ b/tests/support/parser/__init__.py @@ -30,7 +30,7 @@ from collections import namedtuple import tests.support.paths -from tests.support import helpers +from tests.support import processes from tests.support.unit import TestLoader, TextTestRunner from tests.support.xmlunit import HAS_XMLRUNNER, XMLTestRunner @@ -72,7 +72,7 @@ def __global_logging_exception_handler(exc_type, exc_value, exc_traceback, # Log the exception try: msg = ( - 'An un-handled exception was caught by salt-testing\'s global exception handler:\n{}: {}\n{}'.format( + 'An un-handled exception was caught by salt\'s testing global exception handler:\n{}: {}\n{}'.format( exc_type.__name__, exc_value, ''.join(_format_exception(exc_type, exc_value, exc_traceback)).strip() @@ -402,7 +402,7 @@ def _test_mods(self): try: return self.__test_mods except AttributeError: - self.__test_mods = set(tests.support.paths.test_mods()) + self.__test_mods = set(tests.support.paths.list_test_mods()) return self.__test_mods def _map_files(self, files): @@ -598,12 +598,12 @@ def _validate_options(self): self.validate_options() - if self.support_destructive_tests_selection: + if self.support_destructive_tests_selection and not os.environ.get('DESTRUCTIVE_TESTS', None): # Set the required environment variable in order to know if # destructive tests should be executed or not. os.environ['DESTRUCTIVE_TESTS'] = str(self.options.run_destructive) - if self.support_expensive_tests_selection: + if self.support_expensive_tests_selection and not os.environ.get('EXPENSIVE_TESTS', None): # Set the required environment variable in order to know if # expensive tests should be executed or not. os.environ['EXPENSIVE_TESTS'] = str(self.options.run_expensive) @@ -860,18 +860,18 @@ def finalize(self, exit_code=0): Run the finalization procedures. Show report, clean-up file-system, etc ''' # Collect any child processes still laying around - children = helpers.collect_child_processes(os.getpid()) + children = processes.collect_child_processes(os.getpid()) if self.options.no_report is False: self.print_overall_testsuite_report() self.post_execution_cleanup() # Brute force approach to terminate this process and its children if children: log.info('Terminating test suite child processes: %s', children) - helpers.terminate_process(children=children, kill_children=True) - children = helpers.collect_child_processes(os.getpid()) + processes.terminate_process(children=children, kill_children=True) + children = processes.collect_child_processes(os.getpid()) if children: log.info('Second run at terminating test suite child processes: %s', children) - helpers.terminate_process(children=children, kill_children=True) + processes.terminate_process(children=children, kill_children=True) exit_msg = 'Test suite execution finalized with exit code: {}'.format(exit_code) log.info(exit_msg) self.exit(status=exit_code, msg=exit_msg + '\n') diff --git a/tests/support/paths.py b/tests/support/paths.py index e676885692bf..b8dadeddebf1 100644 --- a/tests/support/paths.py +++ b/tests/support/paths.py @@ -16,10 +16,8 @@ import os import re import sys -import stat import logging import tempfile -import textwrap import salt.utils.path @@ -55,6 +53,7 @@ os.environ.get('TMPDIR', tempfile.gettempdir()) if not sys.platform.startswith('darwin') else '/tmp' )) TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir') +TMP_ROOT_DIR = os.path.join(TMP, 'rootdir') FILES = os.path.join(INTEGRATION_TEST_DIR, 'files') BASE_FILES = os.path.join(INTEGRATION_TEST_DIR, 'files', 'file', 'base') PROD_FILES = os.path.join(INTEGRATION_TEST_DIR, 'files', 'file', 'prod') @@ -77,55 +76,8 @@ ENGINES_DIR = os.path.join(FILES, 'engines') LOG_HANDLERS_DIR = os.path.join(FILES, 'log_handlers') -SCRIPT_TEMPLATES = { - 'salt': [ - 'from salt.scripts import salt_main\n', - 'if __name__ == \'__main__\':\n' - ' salt_main()' - ], - 'salt-api': [ - 'import salt.cli\n', - 'def main():\n', - ' sapi = salt.cli.SaltAPI()', - ' sapi.start()\n', - 'if __name__ == \'__main__\':', - ' main()' - ], - 'common': [ - 'from salt.scripts import salt_{0}\n', - 'import salt.utils.platform\n\n', - 'if __name__ == \'__main__\':\n', - ' if salt.utils.platform.is_windows():\n', - ' import os.path\n', - ' import py_compile\n', - ' cfile = os.path.splitext(__file__)[0] + ".pyc"\n', - ' if not os.path.exists(cfile):\n', - ' py_compile.compile(__file__, cfile)\n', - ' salt_{0}()' - ], - 'coverage': textwrap.dedent( - ''' - SITECUSTOMIZE_DIR = os.path.join(CODE_DIR, 'tests', 'support', 'coverage') - COVERAGE_FILE = os.path.join(CODE_DIR, '.coverage') - COVERAGE_PROCESS_START = os.path.join(CODE_DIR, '.coveragerc') - PYTHONPATH = os.environ.get('PYTHONPATH') or None - if PYTHONPATH is None: - PYTHONPATH_ENV_VAR = SITECUSTOMIZE_DIR - else: - PYTHON_PATH_ENTRIES = PYTHONPATH.split(os.pathsep) - if SITECUSTOMIZE_DIR in PYTHON_PATH_ENTRIES: - PYTHON_PATH_ENTRIES.remove(SITECUSTOMIZE_DIR) - PYTHON_PATH_ENTRIES.insert(0, SITECUSTOMIZE_DIR) - PYTHONPATH_ENV_VAR = os.pathsep.join(PYTHON_PATH_ENTRIES) - os.environ['PYTHONPATH'] = PYTHONPATH_ENV_VAR - os.environ['COVERAGE_FILE'] = COVERAGE_FILE - os.environ['COVERAGE_PROCESS_START'] = COVERAGE_PROCESS_START - ''' - ) -} - -def test_mods(): +def list_test_mods(): ''' A generator which returns all of the test files ''' @@ -141,62 +93,3 @@ def test_mods(): mod_name += '.' + parent_mod mod_name += '.' + filename[:-3] yield mod_name - - -class ScriptPathMixin(object): - - def get_script_path(self, script_name): - ''' - Return the path to a testing runtime script - ''' - if not os.path.isdir(TMP_SCRIPT_DIR): - os.makedirs(TMP_SCRIPT_DIR) - - script_path = os.path.join(TMP_SCRIPT_DIR, - 'cli_{0}.py'.format(script_name.replace('-', '_'))) - - if not os.path.isfile(script_path): - log.info('Generating %s', script_path) - - # Late import - import salt.utils.files - - with salt.utils.files.fopen(script_path, 'w') as sfh: - script_template = SCRIPT_TEMPLATES.get(script_name, None) - if script_template is None: - script_template = SCRIPT_TEMPLATES.get('common', None) - if script_template is None: - raise RuntimeError( - '{0} does not know how to handle the {1} script'.format( - self.__class__.__name__, - script_name - ) - ) - - shebang = sys.executable - if len(shebang) > 128: - # Too long for a shebang, let's use /usr/bin/env and hope - # the right python is picked up - shebang = '/usr/bin/env python' - - if 'COVERAGE_PROCESS_START' in os.environ: - coverage_snippet = SCRIPT_TEMPLATES['coverage'] - else: - coverage_snippet = '' - - sfh.write( - '#!{0}\n\n'.format(shebang) + - 'from __future__ import absolute_import\n' - 'import os\n' - 'import sys\n' + - 'CODE_DIR = r"{0}"\n'.format(CODE_DIR) + - 'if CODE_DIR not in sys.path:\n' + - ' sys.path.insert(0, CODE_DIR)\n' + - coverage_snippet + '\n' + - '\n'.join(script_template).format(script_name.replace('salt-', '')) - ) - fst = os.stat(script_path) - os.chmod(script_path, fst.st_mode | stat.S_IEXEC) - - log.info('Returning script path %r', script_path) - return script_path diff --git a/tests/support/processes.py b/tests/support/processes.py index 7da2cb304e0f..6fe5e850ef76 100644 --- a/tests/support/processes.py +++ b/tests/support/processes.py @@ -15,7 +15,6 @@ import logging # Import pytest-salt libs -from pytestsalt.utils import SaltRunEventListener as PytestSaltRunEventListener from pytestsalt.utils import collect_child_processes, terminate_process, terminate_process_list # pylint: disable=unused-import from pytestsalt.fixtures.daemons import Salt as PytestSalt from pytestsalt.fixtures.daemons import SaltKey as PytestSaltKey @@ -27,18 +26,11 @@ from pytestsalt.fixtures.daemons import SaltProxy as PytestSaltProxy # Import tests support libs -from tests.support.paths import ScriptPathMixin +from tests.support.cli_scripts import ScriptPathMixin log = logging.getLogger(__name__) -class SaltRunEventListener(ScriptPathMixin, PytestSaltRunEventListener): - ''' - Override this class's __init__ because there's no request argument since we're still - not running under pytest - ''' - - class GetSaltRunFixtureMixin(ScriptPathMixin): ''' Override this classes `get_salt_run_fixture` because we're still not running under pytest @@ -47,14 +39,6 @@ class GetSaltRunFixtureMixin(ScriptPathMixin): def get_salt_run_fixture(self): pass - def get_salt_run_event_listener(self): - return SaltRunEventListener(None, - self.config, - self.config_dir, - self.bin_dir_path, - self.log_prefix, - cli_script_name='run') - class Salt(ScriptPathMixin, PytestSalt): ''' @@ -124,11 +108,15 @@ def start_daemon(daemon_name=None, start_timeout=10, slow_stop=False, environ=None, - cwd=None): + cwd=None, + event_listener_config_dir=None): ''' Returns a running salt daemon ''' + # Old config name daemon_config['pytest_port'] = daemon_config['runtests_conn_check_port'] + # New config name + daemon_config['pytest_engine_port'] = daemon_config['runtests_conn_check_port'] request = None if fail_hard: fail_method = RuntimeError @@ -139,15 +127,27 @@ def start_daemon(daemon_name=None, process = None while attempts <= 3: # pylint: disable=too-many-nested-blocks attempts += 1 - process = daemon_class(request, - daemon_config, - daemon_config_dir, - bin_dir_path, - daemon_log_prefix, - cli_script_name=daemon_cli_script_name, - slow_stop=slow_stop, - environ=environ, - cwd=cwd) + try: + process = daemon_class(request=request, + config=daemon_config, + config_dir=daemon_config_dir, + bin_dir_path=bin_dir_path, + log_prefix=daemon_log_prefix, + cli_script_name=daemon_cli_script_name, + slow_stop=slow_stop, + environ=environ, + cwd=cwd, + event_listener_config_dir=event_listener_config_dir) + except TypeError: + process = daemon_class(request=request, + config=daemon_config, + config_dir=daemon_config_dir, + bin_dir_path=bin_dir_path, + log_prefix=daemon_log_prefix, + cli_script_name=daemon_cli_script_name, + slow_stop=slow_stop, + environ=environ, + cwd=cwd) process.start() if process.is_alive(): try: @@ -176,18 +176,11 @@ def start_daemon(daemon_name=None, attempts ) - def stop_daemon(): - log.info('[%s] Stopping pytest %s(%s)', daemon_log_prefix, daemon_name, daemon_id) - terminate_process(process.pid, kill_children=True, slow_stop=slow_stop) - log.info('[%s] pytest %s(%s) stopped', daemon_log_prefix, daemon_name, daemon_id) - - # request.addfinalizer(stop_daemon) - return process + break else: terminate_process(process.pid, kill_children=True, slow_stop=slow_stop) continue - else: # pylint: disable=useless-else-on-loop - # Wrong, we have a return, its not useless + else: if process is not None: terminate_process(process.pid, kill_children=True, slow_stop=slow_stop) raise fail_method( @@ -197,3 +190,4 @@ def stop_daemon(): attempts-1 ) ) + return process diff --git a/tests/support/runtests.py b/tests/support/runtests.py index 074db7aa091f..4ee5810838a1 100644 --- a/tests/support/runtests.py +++ b/tests/support/runtests.py @@ -51,64 +51,32 @@ import os import shutil import logging -import multiprocessing -import salt.utils.json +# Import Salt libs +import salt.utils.path +import salt.utils.platform + +try: + import pwd +except ImportError: + import salt.utils.win_functions # Import tests support libs import tests.support.paths as paths -import tests.support.helpers # Import 3rd-party libs from salt.ext import six -try: - import coverage # pylint: disable=import-error - HAS_COVERAGE = True -except ImportError: - HAS_COVERAGE = False -try: - import multiprocessing.util - # Force forked multiprocessing processes to be measured as well - - def multiprocessing_stop(coverage_object): - ''' - Save the multiprocessing process coverage object - ''' - coverage_object.stop() - coverage_object.save() - - def multiprocessing_start(obj): - coverage_options = salt.utils.json.loads(os.environ.get('SALT_RUNTESTS_COVERAGE_OPTIONS', '{}')) - if not coverage_options: - return - - if coverage_options.get('data_suffix', False) is False: - return - - coverage_object = coverage.coverage(**coverage_options) - coverage_object.start() - - multiprocessing.util.Finalize( - None, - multiprocessing_stop, - args=(coverage_object,), - exitpriority=1000 - ) - - if HAS_COVERAGE: - multiprocessing.util.register_after_fork( - multiprocessing_start, - multiprocessing_start - ) -except ImportError: - pass - - -RUNNING_TESTS_USER = tests.support.helpers.this_user() +log = logging.getLogger(__name__) -log = logging.getLogger(__name__) +def this_user(): + ''' + Get the user associated with the current process. + ''' + if salt.utils.platform.is_windows(): + return salt.utils.win_functions.get_current_user(with_domain=False) + return pwd.getpwuid(os.getuid())[0] class RootsDict(dict): @@ -132,20 +100,20 @@ def recursive_copytree(source, destination, overwrite=False): src_path = os.path.join(root, item) dst_path = os.path.join(destination, src_path.replace(source, '').lstrip(os.sep)) if not os.path.exists(dst_path): - log.debug('Creating directory: {0}'.format(dst_path)) + log.debug('Creating directory: %s', dst_path) os.makedirs(dst_path) for item in files: src_path = os.path.join(root, item) dst_path = os.path.join(destination, src_path.replace(source, '').lstrip(os.sep)) if os.path.exists(dst_path) and not overwrite: if os.stat(src_path).st_mtime > os.stat(dst_path).st_mtime: - log.debug('Copying {0} to {1}'.format(src_path, dst_path)) + log.debug('Copying %s to %s', src_path, dst_path) shutil.copy2(src_path, dst_path) else: if not os.path.isdir(os.path.dirname(dst_path)): - log.debug('Creating directory: {0}'.format(os.path.dirname(dst_path))) + log.debug('Creating directory: %s', os.path.dirname(dst_path)) os.makedirs(os.path.dirname(dst_path)) - log.debug('Copying {0} to {1}'.format(src_path, dst_path)) + log.debug('Copying %s to %s', src_path, dst_path) shutil.copy2(src_path, dst_path) @@ -204,6 +172,7 @@ def __setattr__(self, name, value): PILLAR_DIR=paths.PILLAR_DIR, ENGINES_DIR=paths.ENGINES_DIR, LOG_HANDLERS_DIR=paths.LOG_HANDLERS_DIR, + TMP_ROOT_DIR=paths.TMP_ROOT_DIR, TMP_CONF_DIR=paths.TMP_CONF_DIR, TMP_CONF_MASTER_INCLUDES=os.path.join(paths.TMP_CONF_DIR, 'master.d'), TMP_CONF_MINION_INCLUDES=os.path.join(paths.TMP_CONF_DIR, 'minion.d'), @@ -220,7 +189,14 @@ def __setattr__(self, name, value): TMP_STATE_TREE=paths.TMP_STATE_TREE, TMP_PILLAR_TREE=paths.TMP_PILLAR_TREE, TMP_PRODENV_STATE_TREE=paths.TMP_PRODENV_STATE_TREE, - RUNNING_TESTS_USER=RUNNING_TESTS_USER, - RUNTIME_CONFIGS={} + SHELL_TRUE_PATH=salt.utils.path.which('true') if not salt.utils.platform.is_windows() else 'cmd /c exit 0 > nul', + SHELL_FALSE_PATH=salt.utils.path.which('false') if not salt.utils.platform.is_windows() else 'cmd /c exit 1 > nul', + RUNNING_TESTS_USER=this_user(), + RUNTIME_CONFIGS={}, + CODE_DIR=paths.CODE_DIR, + BASE_FILES=paths.BASE_FILES, + PROD_FILES=paths.PROD_FILES, + TESTS_DIR=paths.TESTS_DIR, + PYTEST_SESSION=False ) # <---- Tests Runtime Variables -------------------------------------------------------------------------------------- diff --git a/tests/support/sminion.py b/tests/support/sminion.py new file mode 100644 index 000000000000..86730f8a293f --- /dev/null +++ b/tests/support/sminion.py @@ -0,0 +1,219 @@ +# -*- coding: utf-8 -*- +''' +tests.support.sminion +~~~~~~~~~~~~~~~~~~~~~ + +SMinion's support functions +''' +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals +import os +import sys +import shutil +import hashlib +import logging + +# Import salt libs +import salt.minion +import salt.utils.path +import salt.utils.stringutils + +# Import testing libs +from tests.support.runtests import RUNTIME_VARS + +log = logging.getLogger(__name__) + + +def build_minion_opts(minion_id=None, + root_dir=None, + initial_conf_file=None, + minion_opts_overrides=None, + skip_cached_opts=False, + cache_opts=True, + minion_role=None): + if minion_id is None: + minion_id = 'pytest-internal-sminion' + if skip_cached_opts is False: + try: + opts_cache = build_minion_opts.__cached_opts__ + except AttributeError: + opts_cache = build_minion_opts.__cached_opts__ = {} + cached_opts = opts_cache.get(minion_id) + if cached_opts: + return cached_opts + + log.info('Generating testing minion %r configuration...', minion_id) + if root_dir is None: + hashed_minion_id = hashlib.sha1() + hashed_minion_id.update(salt.utils.stringutils.to_bytes(minion_id)) + root_dir = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, hashed_minion_id.hexdigest()[:6]) + + if initial_conf_file is not None: + minion_opts = salt.config._read_conf_file(initial_conf_file) # pylint: disable=protected-access + else: + minion_opts = {} + + conf_dir = os.path.join(root_dir, 'conf') + conf_file = os.path.join(conf_dir, 'minion') + + minion_opts['id'] = minion_id + minion_opts['conf_file'] = conf_file + minion_opts['root_dir'] = root_dir + minion_opts['cachedir'] = 'cache' + minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER + minion_opts['pki_dir'] = 'pki' + minion_opts['hosts.file'] = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'hosts') + minion_opts['aliases.file'] = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'aliases') + minion_opts['file_client'] = 'local' + minion_opts['server_id_use_crc'] = 'adler32' + minion_opts['pillar_roots'] = { + 'base': [ + RUNTIME_VARS.TMP_PILLAR_TREE, + ] + } + minion_opts['file_roots'] = { + 'base': [ + # Let's support runtime created files that can be used like: + # salt://my-temp-file.txt + RUNTIME_VARS.TMP_STATE_TREE + ], + # Alternate root to test __env__ choices + 'prod': [ + os.path.join(RUNTIME_VARS.FILES, 'file', 'prod'), + RUNTIME_VARS.TMP_PRODENV_STATE_TREE + ] + } + if initial_conf_file and initial_conf_file.startswith(RUNTIME_VARS.FILES): + # We assume we were passed a minion configuration file defined fo testing and, as such + # we define the file and pillar roots to include the testing states/pillar trees + minion_opts['pillar_roots']['base'].append( + os.path.join(RUNTIME_VARS.FILES, 'pillar', 'base'), + ) + minion_opts['file_roots']['base'].append( + os.path.join(RUNTIME_VARS.FILES, 'file', 'base'), + ) + minion_opts['file_roots']['prod'].append( + os.path.join(RUNTIME_VARS.FILES, 'file', 'prod'), + ) + + # We need to copy the extension modules into the new master root_dir or + # it will be prefixed by it + extension_modules_path = os.path.join(root_dir, 'extension_modules') + if not os.path.exists(extension_modules_path): + shutil.copytree( + os.path.join( + RUNTIME_VARS.FILES, 'extension_modules' + ), + extension_modules_path + ) + minion_opts['extension_modules'] = extension_modules_path + + # Custom grains + if 'grains' not in minion_opts: + minion_opts['grains'] = {} + if minion_role is not None: + minion_opts['grains']['role'] = minion_role + + # Under windows we can't seem to properly create a virtualenv off of another + # virtualenv, we can on linux but we will still point to the virtualenv binary + # outside the virtualenv running the test suite, if that's the case. + try: + real_prefix = sys.real_prefix + # The above attribute exists, this is a virtualenv + if salt.utils.platform.is_windows(): + virtualenv_binary = os.path.join(real_prefix, 'Scripts', 'virtualenv.exe') + else: + # We need to remove the virtualenv from PATH or we'll get the virtualenv binary + # from within the virtualenv, we don't want that + path = os.environ.get('PATH') + if path is not None: + path_items = path.split(os.pathsep) + for item in path_items[:]: + if item.startswith(sys.base_prefix): + path_items.remove(item) + os.environ['PATH'] = os.pathsep.join(path_items) + virtualenv_binary = salt.utils.path.which('virtualenv') + if path is not None: + # Restore previous environ PATH + os.environ['PATH'] = path + if not virtualenv_binary.startswith(real_prefix): + virtualenv_binary = None + if virtualenv_binary and not os.path.exists(virtualenv_binary): + # It doesn't exist?! + virtualenv_binary = None + except AttributeError: + # We're not running inside a virtualenv + virtualenv_binary = None + if virtualenv_binary: + minion_opts['venv_bin'] = virtualenv_binary + + # Override minion_opts with minion_opts_overrides + if minion_opts_overrides: + minion_opts.update(minion_opts_overrides) + + if not os.path.exists(conf_dir): + os.makedirs(conf_dir) + + with salt.utils.files.fopen(conf_file, 'w') as fp_: + salt.utils.yaml.safe_dump(minion_opts, fp_, default_flow_style=False) + + log.info('Generating testing minion %r configuration completed.', minion_id) + minion_opts = salt.config.minion_config(conf_file, minion_id=minion_id, cache_minion_id=True) + salt.utils.verify.verify_env( + [ + os.path.join(minion_opts['pki_dir'], 'accepted'), + os.path.join(minion_opts['pki_dir'], 'rejected'), + os.path.join(minion_opts['pki_dir'], 'pending'), + os.path.dirname(minion_opts['log_file']), + minion_opts['extension_modules'], + minion_opts['cachedir'], + minion_opts['sock_dir'], + RUNTIME_VARS.TMP_STATE_TREE, + RUNTIME_VARS.TMP_PILLAR_TREE, + RUNTIME_VARS.TMP_PRODENV_STATE_TREE, + RUNTIME_VARS.TMP, + ], + RUNTIME_VARS.RUNNING_TESTS_USER, + root_dir=root_dir + ) + if cache_opts: + try: + opts_cache = build_minion_opts.__cached_opts__ + except AttributeError: + opts_cache = build_minion_opts.__cached_opts__ = {} + opts_cache[minion_id] = minion_opts + return minion_opts + + +def create_sminion(minion_id=None, + root_dir=None, + initial_conf_file=None, + sminion_cls=salt.minion.SMinion, + minion_opts_overrides=None, + skip_cached_minion=False, + cache_sminion=True): + if minion_id is None: + minion_id = 'pytest-internal-sminion' + if skip_cached_minion is False: + try: + minions_cache = create_sminion.__cached_minions__ + except AttributeError: + create_sminion.__cached_minions__ = {} + cached_minion = create_sminion.__cached_minions__.get(minion_id) + if cached_minion: + return cached_minion + minion_opts = build_minion_opts(minion_id=minion_id, + root_dir=root_dir, + initial_conf_file=initial_conf_file, + minion_opts_overrides=minion_opts_overrides, + skip_cached_opts=skip_cached_minion, + cache_opts=cache_sminion) + log.info('Instantiating a testing %s(%s)', sminion_cls.__name__, minion_id) + sminion = sminion_cls(minion_opts) + if cache_sminion: + try: + minions_cache = create_sminion.__cached_minions__ + except AttributeError: + minions_cache = create_sminion.__cached_minions__ = {} + minions_cache[minion_id] = sminion + return sminion diff --git a/tests/support/unit.py b/tests/support/unit.py index 04833a0ce639..25af3ad89268 100644 --- a/tests/support/unit.py +++ b/tests/support/unit.py @@ -26,6 +26,18 @@ import os import sys import logging +from unittest import ( + TestLoader as _TestLoader, + TextTestRunner as _TextTestRunner, + TestCase as _TestCase, + expectedFailure, + TestSuite as _TestSuite, + skip, + skipIf as _skipIf, + TestResult, + TextTestResult as _TextTestResult +) +from unittest.case import _id, SkipTest from salt.ext import six try: import psutil @@ -51,67 +63,6 @@ eu mauris sit amet convallis. Morbi vulputate vel odio non laoreet. Nullam in suscipit tellus. Sed quis posuere urna.''' -# support python < 2.7 via unittest2 -if sys.version_info < (2, 7): - try: - # pylint: disable=import-error - from unittest2 import ( - TestLoader as __TestLoader, - TextTestRunner as __TextTestRunner, - TestCase as __TestCase, - expectedFailure, - TestSuite as __TestSuite, - skip, - skipIf, - TestResult as _TestResult, - TextTestResult as __TextTestResult - ) - from unittest2.case import _id, SkipTest - # pylint: enable=import-error - - class NewStyleClassMixin(object): - ''' - Simple new style class to make pylint shut up! - - And also to avoid errors like: - - 'Cannot create a consistent method resolution order (MRO) for bases' - ''' - - class _TestLoader(__TestLoader, NewStyleClassMixin): - pass - - class _TextTestRunner(__TextTestRunner, NewStyleClassMixin): - pass - - class _TestCase(__TestCase, NewStyleClassMixin): - pass - - class _TestSuite(__TestSuite, NewStyleClassMixin): - pass - - class TestResult(_TestResult, NewStyleClassMixin): - pass - - class _TextTestResult(__TextTestResult, NewStyleClassMixin): - pass - - except ImportError: - raise SystemExit('You need to install unittest2 to run the salt tests') -else: - from unittest import ( - TestLoader as _TestLoader, - TextTestRunner as _TextTestRunner, - TestCase as _TestCase, - expectedFailure, - TestSuite as _TestSuite, - skip, - skipIf, - TestResult, - TextTestResult as _TextTestResult - ) - from unittest.case import _id, SkipTest - class TestSuite(_TestSuite): @@ -429,6 +380,14 @@ class TextTestRunner(_TextTestRunner): resultclass = TextTestResult +def skipIf(skip, reason): + from tests.support.runtests import RUNTIME_VARS + if RUNTIME_VARS.PYTEST_SESSION: + import pytest + return pytest.mark.skipif(skip, reason=reason) + return _skipIf(skip, reason) + + __all__ = [ 'TestLoader', 'TextTestRunner', diff --git a/tests/support/zfs.py b/tests/support/zfs.py new file mode 100644 index 000000000000..5b260ff82faf --- /dev/null +++ b/tests/support/zfs.py @@ -0,0 +1,870 @@ +# -*- coding: utf-8 -*- +''' + tests.support.zfs + ~~~~~~~~~~~~~~~~~ + + ZFS related unit test data structures +''' + +# Import Python libs +from __future__ import absolute_import, unicode_literals, print_function + +# Import salt libs +import salt.utils.zfs + +# Import Salt tests libs +from tests.support.mock import MagicMock, patch + + +class ZFSMockData(object): + + def __init__(self): + # property_map mocks + self.pmap_exec_zpool = { + 'retcode': 2, + 'stdout': '', + 'stderr': "\n".join([ + 'missing property argument', + 'usage:', + ' get [-Hp] [-o "all" | field[,...]] <"all" | property[,...]> ...', + '', + 'the following properties are supported:', + '', + ' PROPERTY EDIT VALUES', + '', + ' allocated NO ', + ' capacity NO ', + ' dedupratio NO <1.00x or higher if deduped>', + ' expandsize NO ', + ' fragmentation NO ', + ' free NO ', + ' freeing NO ', + ' guid NO ', + ' health NO ', + ' leaked NO ', + ' size NO ', + ' altroot YES ', + ' autoexpand YES on | off', + ' autoreplace YES on | off', + ' bootfs YES ', + ' bootsize YES ', + ' cachefile YES | none', + ' comment YES ', + ' dedupditto YES ', + ' delegation YES on | off', + ' failmode YES wait | continue | panic', + ' listsnapshots YES on | off', + ' readonly YES on | off', + ' version YES ', + ' feature@... YES disabled | enabled | active', + '', + 'The feature@ properties must be appended with a feature name.', + 'See zpool-features(5). ', + ]), + } + self.pmap_zpool = { + 'comment': { + 'edit': True, + 'type': 'str', + 'values': '' + }, + 'freeing': { + 'edit': False, + 'type': 'size', + 'values': '' + }, + 'listsnapshots': { + 'edit': True, + 'type': 'bool', + 'values': 'on | off' + }, + 'leaked': { + 'edit': False, + 'type': 'size', + 'values': '' + }, + 'version': { + 'edit': True, + 'type': 'numeric', + 'values': '' + }, + 'write': { + 'edit': False, + 'type': 'size', + 'values': '' + }, + 'replace': { + 'edit': True, + 'type': 'bool', + 'values': 'on | off' + }, + 'delegation': { + 'edit': True, + 'type': 'bool', + 'values': 'on | off' + }, + 'dedupditto': { + 'edit': True, + 'type': 'str', + 'values': '' + }, + 'autoexpand': { + 'edit': True, + 'type': 'bool', + 'values': 'on | off' + }, + 'alloc': { + 'edit': False, + 'type': 'size', + 'values': '' + }, + 'allocated': { + 'edit': False, + 'type': 'size', + 'values': '' + }, + 'guid': { + 'edit': False, + 'type': 'numeric', + 'values': '' + }, + 'size': { + 'edit': False, + 'type': 'size', + 'values': '' + }, + 'cap': { + 'edit': False, + 'type': 'numeric', + 'values': '' + }, + 'capacity': { + 'edit': False, + 'type': 'size', + 'values': '' + }, + "capacity-alloc": { + "edit": False, + "type": "size", + "values": "" + }, + "capacity-free": { + "edit": False, + "type": "size", + "values": "" + }, + 'cachefile': { + 'edit': True, + 'type': 'str', + 'values': ' | none' + }, + "cksum": { + "edit": False, + "type": "numeric", + "values": "" + }, + 'bootfs': { + 'edit': True, + 'type': 'str', + 'values': '' + }, + 'autoreplace': { + 'edit': True, + 'type': 'bool', + 'values': 'on | off' + }, + "bandwith-read": { + "edit": False, + "type": "size", + "values": "" + }, + "bandwith-write": { + "edit": False, + "type": "size", + "values": "" + }, + "operations-read": { + "edit": False, + "type": "size", + "values": "" + }, + "operations-write": { + "edit": False, + "type": "size", + "values": "" + }, + "read": { + "edit": False, + "type": "size", + "values": "" + }, + 'readonly': { + 'edit': True, + 'type': 'bool', + 'values': 'on | off' + }, + 'dedupratio': { + 'edit': False, + 'type': 'str', + 'values': '<1.00x or higher if deduped>' + }, + 'health': { + 'edit': False, + 'type': 'str', + 'values': '' + }, + 'feature@': { + 'edit': True, + 'type': 'str', + 'values': 'disabled | enabled | active' + }, + 'expandsize': { + 'edit': False, + 'type': 'size', + 'values': '' + }, + 'listsnaps': { + 'edit': True, + 'type': 'bool', + 'values': 'on | off' + }, + 'bootsize': { + 'edit': True, + 'type': 'size', + 'values': '' + }, + 'free': { + 'edit': False, + 'type': 'size', + 'values': '' + }, + 'failmode': { + 'edit': True, + 'type': 'str', + 'values': 'wait | continue | panic' + }, + 'altroot': { + 'edit': True, + 'type': 'str', + 'values': '' + }, + 'expand': { + 'edit': True, + 'type': 'bool', + 'values': 'on | off' + }, + 'frag': { + 'edit': False, + 'type': 'str', + 'values': '' + }, + 'fragmentation': { + 'edit': False, + 'type': 'str', + 'values': '' + } + } + self.pmap_exec_zfs = { + 'retcode': 2, + 'stdout': '', + 'stderr': "\n".join([ + 'missing property argument', + 'usage:', + ' get [-crHp] [-d max] [-o "all" | field[,...]]', + ' [-t type[,...]] [-s source[,...]]', + ' <"all" | property[,...]> [filesystem|volume|snapshot|bookmark] ...', + '', + 'The following properties are supported:', + '', + ' PROPERTY EDIT INHERIT VALUES', + '', + ' available NO NO ', + ' clones NO NO [,...]', + ' compressratio NO NO <1.00x or higher if compressed>', + ' creation NO NO ', + ' defer_destroy NO NO yes | no', + ' filesystem_count NO NO ', + ' logicalreferenced NO NO ', + ' logicalused NO NO ', + ' mounted NO NO yes | no', + ' origin NO NO ', + ' receive_resume_token NO NO ', + ' refcompressratio NO NO <1.00x or higher if compressed>', + ' referenced NO NO ', + ' snapshot_count NO NO ', + ' type NO NO filesystem | volume | snapshot | bookmark', + ' used NO NO ', + ' usedbychildren NO NO ', + ' usedbydataset NO NO ', + ' usedbyrefreservation NO NO ', + ' usedbysnapshots NO NO ', + ' userrefs NO NO ', + ' written NO NO ', + ' aclinherit YES YES discard | noallow | restricted | passthrough | passthrough-x', + ' aclmode YES YES discard | groupmask | passthrough | restricted', + ' atime YES YES on | off', + ' canmount YES NO on | off | noauto', + ' casesensitivity NO YES sensitive | insensitive | mixed', + ' checksum YES YES on | off | fletcher2 | fletcher4 | sha256 | sha512 | skein | edonr', + ' compression YES YES on | off | lzjb | gzip | gzip-[1-9] | zle | lz4', + ' copies YES YES 1 | 2 | 3', + ' dedup YES YES on | off | verify | sha256[,verify], sha512[,verify], skein[,verify], edonr,verify', + ' devices YES YES on | off', + ' exec YES YES on | off', + ' filesystem_limit YES NO | none', + ' logbias YES YES latency | throughput', + ' mlslabel YES YES ', + ' mountpoint YES YES | legacy | none', + ' nbmand YES YES on | off', + ' normalization NO YES none | formC | formD | formKC | formKD', + ' primarycache YES YES all | none | metadata', + ' quota YES NO | none', + ' readonly YES YES on | off', + ' recordsize YES YES 512 to 1M, power of 2', + ' redundant_metadata YES YES all | most', + ' refquota YES NO | none', + ' refreservation YES NO | none', + ' reservation YES NO | none', + ' secondarycache YES YES all | none | metadata', + ' setuid YES YES on | off', + ' sharenfs YES YES on | off | share(1M) options', + ' sharesmb YES YES on | off | sharemgr(1M) options', + ' snapdir YES YES hidden | visible', + ' snapshot_limit YES NO | none', + ' sync YES YES standard | always | disabled', + ' utf8only NO YES on | off', + ' version YES NO 1 | 2 | 3 | 4 | 5 | current', + ' volblocksize NO YES 512 to 128k, power of 2', + ' volsize YES NO ', + ' vscan YES YES on | off', + ' xattr YES YES on | off', + ' zoned YES YES on | off', + ' userused@... NO NO ', + ' groupused@... NO NO ', + ' userquota@... YES NO | none', + ' groupquota@... YES NO | none', + ' written@ NO NO ', + '', + 'Sizes are specified in bytes with standard units such as K, M, G, etc.', + '', + 'User-defined properties can be specified by using a name containing a colon (:).', + '', + 'The {user|group}{used|quota}@ properties must be appended with', + 'a user or group specifier of one of these forms:', + ' POSIX name (eg: "matt")', + ' POSIX id (eg: "126829")', + ' SMB name@domain (eg: "matt@sun")', + ' SMB SID (eg: "S-1-234-567-89")', + ]), + } + self.pmap_zfs = { + "origin": { + "edit": False, + "inherit": False, + "values": "", + "type": "str" + }, + "setuid": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool" + }, + "referenced": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "vscan": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool" + }, + "logicalused": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "userrefs": { + "edit": False, + "inherit": False, + "values": "", + "type": "numeric" + }, + "primarycache": { + "edit": True, + "inherit": True, + "values": "all | none | metadata", + "type": "str" + }, + "logbias": { + "edit": True, + "inherit": True, + "values": "latency | throughput", + "type": "str" + }, + "creation": { + "edit": False, + "inherit": False, + "values": "", + "type": "str" + }, + "sync": { + "edit": True, + "inherit": True, + "values": "standard | always | disabled", + "type": "str" + }, + "dedup": { + "edit": True, + "inherit": True, + "values": "on | off | verify | sha256[,verify], sha512[,verify], skein[,verify], edonr,verify", + "type": "bool" + }, + "sharenfs": { + "edit": True, + "inherit": True, + "values": "on | off | share(1m) options", + "type": "bool" + }, + "receive_resume_token": { + "edit": False, + "inherit": False, + "values": "", + "type": "str" + }, + "usedbyrefreservation": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "sharesmb": { + "edit": True, + "inherit": True, + "values": "on | off | sharemgr(1m) options", + "type": "bool" + }, + "rdonly": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool" + }, + "reservation": { + "edit": True, + "inherit": False, + "values": " | none", + "type": "size" + }, + "reserv": { + "edit": True, + "inherit": False, + "values": " | none", + "type": "size" + }, + "mountpoint": { + "edit": True, + "inherit": True, + "values": " | legacy | none", + "type": "str" + }, + "casesensitivity": { + "edit": False, + "inherit": True, + "values": "sensitive | insensitive | mixed", + "type": "str" + }, + "utf8only": { + "edit": False, + "inherit": True, + "values": "on | off", + "type": "bool" + }, + "usedbysnapshots": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "readonly": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool" + }, + "written@": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "avail": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "recsize": { + "edit": True, + "inherit": True, + "values": "512 to 1m, power of 2", + "type": "str" + }, + "atime": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool" + }, + "compression": { + "edit": True, + "inherit": True, + "values": "on | off | lzjb | gzip | gzip-[1-9] | zle | lz4", + "type": "bool" + }, + "snapdir": { + "edit": True, + "inherit": True, + "values": "hidden | visible", + "type": "str" + }, + "aclmode": { + "edit": True, + "inherit": True, + "values": "discard | groupmask | passthrough | restricted", + "type": "str" + }, + "zoned": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool" + }, + "copies": { + "edit": True, + "inherit": True, + "values": "1 | 2 | 3", + "type": "numeric" + }, + "snapshot_limit": { + "edit": True, + "inherit": False, + "values": " | none", + "type": "numeric" + }, + "aclinherit": { + "edit": True, + "inherit": True, + "values": "discard | noallow | restricted | passthrough | passthrough-x", + "type": "str" + }, + "compressratio": { + "edit": False, + "inherit": False, + "values": "<1.00x or higher if compressed>", + "type": "str" + }, + "xattr": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool" + }, + "written": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "version": { + "edit": True, + "inherit": False, + "values": "1 | 2 | 3 | 4 | 5 | current", + "type": "numeric" + }, + "recordsize": { + "edit": True, + "inherit": True, + "values": "512 to 1m, power of 2", + "type": "str" + }, + "refquota": { + "edit": True, + "inherit": False, + "values": " | none", + "type": "size" + }, + "filesystem_limit": { + "edit": True, + "inherit": False, + "values": " | none", + "type": "numeric" + }, + "lrefer.": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "type": { + "edit": False, + "inherit": False, + "values": "filesystem | volume | snapshot | bookmark", + "type": "str" + }, + "secondarycache": { + "edit": True, + "inherit": True, + "values": "all | none | metadata", + "type": "str" + }, + "refer": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "available": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "used": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "exec": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool" + }, + "compress": { + "edit": True, + "inherit": True, + "values": "on | off | lzjb | gzip | gzip-[1-9] | zle | lz4", + "type": "bool" + }, + "volblock": { + "edit": False, + "inherit": True, + "values": "512 to 128k, power of 2", + "type": "str" + }, + "refcompressratio": { + "edit": False, + "inherit": False, + "values": "<1.00x or higher if compressed>", + "type": "str" + }, + "quota": { + "edit": True, + "inherit": False, + "values": " | none", + "type": "size" + }, + "groupquota@": { + "edit": True, + "inherit": False, + "values": " | none", + "type": "size" + }, + "userquota@": { + "edit": True, + "inherit": False, + "values": " | none", + "type": "size" + }, + "snapshot_count": { + "edit": False, + "inherit": False, + "values": "", + "type": "numeric" + }, + "volsize": { + "edit": True, + "inherit": False, + "values": "", + "type": "size" + }, + "clones": { + "edit": False, + "inherit": False, + "values": "[,...]", + "type": "str" + }, + "canmount": { + "edit": True, + "inherit": False, + "values": "on | off | noauto", + "type": "bool" + }, + "mounted": { + "edit": False, + "inherit": False, + "values": "yes | no", + "type": "bool_alt" + }, + "groupused@": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "normalization": { + "edit": False, + "inherit": True, + "values": "none | formc | formd | formkc | formkd", + "type": "str" + }, + "usedbychildren": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "usedbydataset": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "mlslabel": { + "edit": True, + "inherit": True, + "values": "", + "type": "str" + }, + "refreserv": { + "edit": True, + "inherit": False, + "values": " | none", + "type": "size" + }, + "defer_destroy": { + "edit": False, + "inherit": False, + "values": "yes | no", + "type": "bool_alt" + }, + "volblocksize": { + "edit": False, + "inherit": True, + "values": "512 to 128k, power of 2", + "type": "str" + }, + "lused.": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "redundant_metadata": { + "edit": True, + "inherit": True, + "values": "all | most", + "type": "str" + }, + "filesystem_count": { + "edit": False, + "inherit": False, + "values": "", + "type": "numeric" + }, + "devices": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool" + }, + "refreservation": { + "edit": True, + "inherit": False, + "values": " | none", + "type": "size" + }, + "userused@": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "logicalreferenced": { + "edit": False, + "inherit": False, + "values": "", + "type": "size" + }, + "checksum": { + "edit": True, + "inherit": True, + "values": "on | off | fletcher2 | fletcher4 | sha256 | sha512 | skein | edonr", + "type": "bool" + }, + "nbmand": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool" + } + } + + def _from_auto(self, name, value, source='auto'): + ''' + some more complex patching for zfs.from_auto + ''' + with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)), \ + patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + return salt.utils.zfs.from_auto(name, value, source) + + def _from_auto_dict(self, values, source='auto'): + ''' + some more complex patching for zfs.from_auto_dict + ''' + with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)), \ + patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + return salt.utils.zfs.from_auto_dict(values, source) + + def _to_auto(self, name, value, source='auto', convert_to_human=True): + ''' + some more complex patching for zfs.to_auto + ''' + with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)), \ + patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + return salt.utils.zfs.to_auto(name, value, source, convert_to_human) + + def _to_auto_dict(self, values, source='auto', convert_to_human=True): + ''' + some more complex patching for zfs.to_auto_dict + ''' + with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)), \ + patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + return salt.utils.zfs.to_auto_dict(values, source, convert_to_human) + + def get_patched_utils(self): + return { + 'zfs.is_supported': MagicMock(return_value=True), + 'zfs.has_feature_flags': MagicMock(return_value=True), + 'zfs.property_data_zpool': MagicMock(return_value=self.pmap_zpool), + 'zfs.property_data_zfs': MagicMock(return_value=self.pmap_zfs), + # NOTE: we make zpool_command and zfs_command a NOOP + # these are extensively tested in tests.unit.utils.test_zfs + 'zfs.zpool_command': MagicMock(return_value='/bin/false'), + 'zfs.zfs_command': MagicMock(return_value='/bin/false'), + # NOTE: from_auto_dict is a special snowflake + # internally it calls multiple calls from + # salt.utils.zfs but we cannot patch those using + # the common methode, __utils__ is not available + # so they are direct calls, we do some voodoo here. + 'zfs.from_auto_dict': self._from_auto_dict, + 'zfs.from_auto': self._from_auto, + 'zfs.to_auto_dict': self._to_auto_dict, + 'zfs.to_auto': self._to_auto, + } diff --git a/tests/tox-helper.py b/tests/tox-helper.py deleted file mode 100644 index 98e60fe0f3ca..000000000000 --- a/tests/tox-helper.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This script exists so that path handling when running tox works for both Linux and Windows - -# Import Python Libs -from __future__ import absolute_import, unicode_literals -import os -import shutil -import argparse -import tempfile - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument( - '--rootdir', - default=os.path.abspath(os.path.dirname(os.path.dirname(__file__))) - ) - subparsers = parser.add_subparsers(help='sub-command help', dest='subparser') - - subparsers.add_parser('create-dirs') - subparsers.add_parser('move-artifacts') - - options = parser.parse_args() - if options.subparser == 'create-dirs': - for dirname in ('logs', 'coverage', 'xml-unittests-output'): - path = os.path.join(options.rootdir, 'artifacts', dirname) - if not os.path.exists(path): - os.makedirs(path) - - if options.subparser == 'move-artifacts': - tmp_artifacts_dir = os.path.join(tempfile.gettempdir(), 'artifacts') - if not os.path.exists(tmp_artifacts_dir): - os.makedirs(tmp_artifacts_dir) - - for dirname in ('logs', 'coverage', 'xml-unittests-output'): - src = os.path.join(options.rootdir, 'artifacts', dirname) - dst = os.path.join(tmp_artifacts_dir, dirname) - shutil.copytree(src, dst) - - -if __name__ == '__main__': - main() diff --git a/tests/unit/auth/test_ldap.py b/tests/unit/auth/test_ldap.py index fb48ce8a56d1..5433ba3372a9 100644 --- a/tests/unit/auth/test_ldap.py +++ b/tests/unit/auth/test_ldap.py @@ -7,7 +7,7 @@ import salt.auth.ldap # Import Salt Testing Libs -from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON +from tests.support.mock import patch from tests.support.unit import skipIf, TestCase salt.auth.ldap.__opts__ = {} @@ -28,7 +28,6 @@ def search_s(*args, **kwargs): ] -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not salt.auth.ldap.HAS_LDAP, 'Install python-ldap for this test') class LDAPAuthTestCase(TestCase): ''' diff --git a/tests/unit/beacons/test_adb.py b/tests/unit/beacons/test_adb.py index e0d9f738c122..4094d7078acf 100644 --- a/tests/unit/beacons/test_adb.py +++ b/tests/unit/beacons/test_adb.py @@ -4,15 +4,14 @@ from __future__ import absolute_import # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, Mock +from tests.support.unit import TestCase +from tests.support.mock import patch, Mock from tests.support.mixins import LoaderModuleMockMixin # Salt libs import salt.beacons.adb as adb -@skipIf(NO_MOCK, NO_MOCK_REASON) class ADBBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.adb diff --git a/tests/unit/beacons/test_avahi_announce.py b/tests/unit/beacons/test_avahi_announce.py index baf5d0cb3e02..9cdc92b8ea62 100644 --- a/tests/unit/beacons/test_avahi_announce.py +++ b/tests/unit/beacons/test_avahi_announce.py @@ -4,15 +4,13 @@ from __future__ import absolute_import # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase from tests.support.mixins import LoaderModuleMockMixin # Salt libs import salt.beacons.avahi_announce as avahi_announce -@skipIf(NO_MOCK, NO_MOCK_REASON) class AvahiAnnounceBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.avahi_announce diff --git a/tests/unit/beacons/test_bonjour_announce.py b/tests/unit/beacons/test_bonjour_announce.py index f10aa2a42ef5..c611784f5b48 100644 --- a/tests/unit/beacons/test_bonjour_announce.py +++ b/tests/unit/beacons/test_bonjour_announce.py @@ -4,15 +4,13 @@ from __future__ import absolute_import # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase from tests.support.mixins import LoaderModuleMockMixin # Salt libs import salt.beacons.bonjour_announce as bonjour_announce -@skipIf(NO_MOCK, NO_MOCK_REASON) class BonjourAnnounceBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.avahi_announce diff --git a/tests/unit/beacons/test_btmp.py b/tests/unit/beacons/test_btmp.py index 0060a035328f..e3bb50477d47 100644 --- a/tests/unit/beacons/test_btmp.py +++ b/tests/unit/beacons/test_btmp.py @@ -7,7 +7,7 @@ # Salt testing libs from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, mock_open +from tests.support.mock import patch, MagicMock, mock_open from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -27,7 +27,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BTMPBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.[s] diff --git a/tests/unit/beacons/test_diskusage.py b/tests/unit/beacons/test_diskusage.py index ed2ecc65226c..41c5deb3d145 100644 --- a/tests/unit/beacons/test_diskusage.py +++ b/tests/unit/beacons/test_diskusage.py @@ -5,8 +5,8 @@ from collections import namedtuple # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, Mock +from tests.support.unit import TestCase +from tests.support.mock import patch, MagicMock, Mock from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -48,7 +48,6 @@ 50) -@skipIf(NO_MOCK, NO_MOCK_REASON) class DiskUsageBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.adb diff --git a/tests/unit/beacons/test_glxinfo.py b/tests/unit/beacons/test_glxinfo.py index 3457b067439b..5f356e5bdf16 100644 --- a/tests/unit/beacons/test_glxinfo.py +++ b/tests/unit/beacons/test_glxinfo.py @@ -7,14 +7,13 @@ import salt.beacons.glxinfo as glxinfo # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, Mock +from tests.support.unit import TestCase +from tests.support.mock import patch, Mock # Import test suite libs from tests.support.mixins import LoaderModuleMockMixin -@skipIf(NO_MOCK, NO_MOCK_REASON) class GLXInfoBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.glxinfo diff --git a/tests/unit/beacons/test_haproxy.py b/tests/unit/beacons/test_haproxy.py index 34a5a46eecb2..fb608c92f296 100644 --- a/tests/unit/beacons/test_haproxy.py +++ b/tests/unit/beacons/test_haproxy.py @@ -4,15 +4,14 @@ from __future__ import absolute_import # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import patch, MagicMock from tests.support.mixins import LoaderModuleMockMixin # Salt libs import salt.beacons.haproxy as haproxy -@skipIf(NO_MOCK, NO_MOCK_REASON) class HAProxyBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.haproxy diff --git a/tests/unit/beacons/test_inotify.py b/tests/unit/beacons/test_inotify.py index 5c0dc8c89868..41b6f8475764 100644 --- a/tests/unit/beacons/test_inotify.py +++ b/tests/unit/beacons/test_inotify.py @@ -39,10 +39,33 @@ def setUp(self): def tearDown(self): shutil.rmtree(self.tmpdir, ignore_errors=True) + def test_non_list_config(self): + config = {} + + ret = inotify.validate(config) + + self.assertEqual(ret, (False, 'Configuration for inotify beacon must' + ' be a list.')) + def test_empty_config(self): config = [{}] - ret = inotify.beacon(config) - self.assertEqual(ret, []) + ret = inotify.validate(config) + _expected = (False, 'Configuration for inotify beacon must include files.') + self.assertEqual(ret, _expected) + + def test_files_none_config(self): + config = [{'files': None}] + ret = inotify.validate(config) + _expected = (False, 'Configuration for inotify beacon invalid, ' + 'files must be a dict.') + self.assertEqual(ret, _expected) + + def test_files_list_config(self): + config = [{'files': [{u'/importantfile': {u'mask': [u'modify']}}]}] + ret = inotify.validate(config) + _expected = (False, 'Configuration for inotify beacon invalid, ' + 'files must be a dict.') + self.assertEqual(ret, _expected) def test_file_open(self): path = os.path.realpath(__file__) diff --git a/tests/unit/beacons/test_journald.py b/tests/unit/beacons/test_journald.py index ee3c6d1c3d8e..94bb411be1e3 100644 --- a/tests/unit/beacons/test_journald.py +++ b/tests/unit/beacons/test_journald.py @@ -6,8 +6,8 @@ from uuid import UUID # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, Mock +from tests.support.unit import TestCase +from tests.support.mock import Mock from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -74,7 +74,6 @@ def get_previous(self, *args, **kwargs): SYSTEMD_MOCK = SystemdJournaldMock() -@skipIf(NO_MOCK, NO_MOCK_REASON) class JournaldBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.journald diff --git a/tests/unit/beacons/test_load.py b/tests/unit/beacons/test_load.py index 672c301cf6e8..2d8df48758b3 100644 --- a/tests/unit/beacons/test_load.py +++ b/tests/unit/beacons/test_load.py @@ -5,7 +5,7 @@ # Salt testing libs from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.mock import patch, MagicMock from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -16,7 +16,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) class LoadBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.load diff --git a/tests/unit/beacons/test_log.py b/tests/unit/beacons/test_log.py index 8551c4867344..fadf3721b9da 100644 --- a/tests/unit/beacons/test_log.py +++ b/tests/unit/beacons/test_log.py @@ -4,8 +4,8 @@ from __future__ import absolute_import # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, mock_open +from tests.support.unit import TestCase +from tests.support.mock import patch, mock_open from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -20,7 +20,6 @@ 'for user username by (uid=0)\n' -@skipIf(NO_MOCK, NO_MOCK_REASON) class LogBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.log diff --git a/tests/unit/beacons/test_memusage.py b/tests/unit/beacons/test_memusage.py index e3219073d7a9..faa81dcb6719 100644 --- a/tests/unit/beacons/test_memusage.py +++ b/tests/unit/beacons/test_memusage.py @@ -5,8 +5,8 @@ from collections import namedtuple # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import patch, MagicMock from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -18,7 +18,6 @@ 1156378624, 4750528512, 898908160) -@skipIf(NO_MOCK, NO_MOCK_REASON) class MemUsageBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.memusage diff --git a/tests/unit/beacons/test_network_info.py b/tests/unit/beacons/test_network_info.py index 0b76c50ecd7b..a8726455e14c 100644 --- a/tests/unit/beacons/test_network_info.py +++ b/tests/unit/beacons/test_network_info.py @@ -5,8 +5,8 @@ from collections import namedtuple # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import patch, MagicMock from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -25,7 +25,6 @@ 0, 0, 0)} -@skipIf(NO_MOCK, NO_MOCK_REASON) class NetworkInfoBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.network_info diff --git a/tests/unit/beacons/test_network_settings.py b/tests/unit/beacons/test_network_settings.py index 8380dfecdd7d..77f09b0e7825 100644 --- a/tests/unit/beacons/test_network_settings.py +++ b/tests/unit/beacons/test_network_settings.py @@ -5,7 +5,6 @@ # Salt testing libs from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON from tests.support.mixins import LoaderModuleMockMixin try: from pyroute2 import IPDB @@ -20,7 +19,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) class NetworkSettingsBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.network_settings diff --git a/tests/unit/beacons/test_ps.py b/tests/unit/beacons/test_ps.py index 2ae5a5735bc2..5e7dc11e4182 100644 --- a/tests/unit/beacons/test_ps.py +++ b/tests/unit/beacons/test_ps.py @@ -4,8 +4,8 @@ from __future__ import absolute_import # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch +from tests.support.unit import TestCase +from tests.support.mock import patch from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -24,7 +24,6 @@ def name(self): return self._name -@skipIf(NO_MOCK, NO_MOCK_REASON) class PSBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.[s] diff --git a/tests/unit/beacons/test_salt_proxy.py b/tests/unit/beacons/test_salt_proxy.py index 0cac46ec9780..320481bfa4fe 100644 --- a/tests/unit/beacons/test_salt_proxy.py +++ b/tests/unit/beacons/test_salt_proxy.py @@ -5,8 +5,8 @@ from collections import namedtuple # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import patch, MagicMock from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -17,7 +17,6 @@ FakeProcess = namedtuple('Process', 'cmdline pid') -@skipIf(NO_MOCK, NO_MOCK_REASON) class SaltProxyBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.[s] diff --git a/tests/unit/beacons/test_sensehat.py b/tests/unit/beacons/test_sensehat.py index de2d1696d842..9f3e1e141529 100644 --- a/tests/unit/beacons/test_sensehat.py +++ b/tests/unit/beacons/test_sensehat.py @@ -4,15 +4,14 @@ from __future__ import absolute_import # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import MagicMock from tests.support.mixins import LoaderModuleMockMixin # Salt libs import salt.beacons.sensehat as sensehat -@skipIf(NO_MOCK, NO_MOCK_REASON) class SensehatBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.[s] diff --git a/tests/unit/beacons/test_service.py b/tests/unit/beacons/test_service.py index 3f9ac318dc3d..d6fd9a8b4baa 100644 --- a/tests/unit/beacons/test_service.py +++ b/tests/unit/beacons/test_service.py @@ -5,8 +5,8 @@ from collections import namedtuple # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import patch, MagicMock from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -17,7 +17,6 @@ FakeProcess = namedtuple('Process', 'cmdline pid') -@skipIf(NO_MOCK, NO_MOCK_REASON) class ServiceBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.service diff --git a/tests/unit/beacons/test_smartos_imgadm.py b/tests/unit/beacons/test_smartos_imgadm.py index 8dcf8d11fd9d..e80f724f4c94 100644 --- a/tests/unit/beacons/test_smartos_imgadm.py +++ b/tests/unit/beacons/test_smartos_imgadm.py @@ -4,8 +4,8 @@ from __future__ import absolute_import, print_function, unicode_literals # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import patch, MagicMock from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -45,7 +45,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class SmartOSImgAdmBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.imgadm diff --git a/tests/unit/beacons/test_smartos_vmadm.py b/tests/unit/beacons/test_smartos_vmadm.py index 61f14dd76bff..5f79f3d46870 100644 --- a/tests/unit/beacons/test_smartos_vmadm.py +++ b/tests/unit/beacons/test_smartos_vmadm.py @@ -4,8 +4,8 @@ from __future__ import absolute_import # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import patch, MagicMock from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -52,7 +52,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class SmartOSImgAdmBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.vmadm diff --git a/tests/unit/beacons/test_status.py b/tests/unit/beacons/test_status.py index 32fa0af1b370..175716ffcade 100644 --- a/tests/unit/beacons/test_status.py +++ b/tests/unit/beacons/test_status.py @@ -30,7 +30,7 @@ class StatusBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' def setup_loader_modules(self): - opts = salt.config.DEFAULT_MINION_OPTS + opts = salt.config.DEFAULT_MINION_OPTS.copy() opts['grains'] = salt.loader.grains(opts) module_globals = { '__opts__': opts, diff --git a/tests/unit/beacons/test_telegram_bot_msg.py b/tests/unit/beacons/test_telegram_bot_msg.py index 436a4190022e..2ccadf580b6b 100644 --- a/tests/unit/beacons/test_telegram_bot_msg.py +++ b/tests/unit/beacons/test_telegram_bot_msg.py @@ -10,7 +10,7 @@ # Salt testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch # Third-party libs try: @@ -24,7 +24,6 @@ @skipIf(not HAS_TELEGRAM, 'telegram is not available') -@skipIf(NO_MOCK, NO_MOCK_REASON) class TelegramBotMsgBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.telegram_bot diff --git a/tests/unit/beacons/test_twilio_txt_msg.py b/tests/unit/beacons/test_twilio_txt_msg.py index 5223e06d28d8..64e0b548f9d4 100644 --- a/tests/unit/beacons/test_twilio_txt_msg.py +++ b/tests/unit/beacons/test_twilio_txt_msg.py @@ -9,7 +9,7 @@ # Salt testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch # Import 3rd Party libs try: @@ -104,7 +104,6 @@ def __init__(self): @skipIf(not HAS_TWILIO, 'twilio.rest is not available') -@skipIf(NO_MOCK, NO_MOCK_REASON) class TwilioMsgTxtBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.twilio_txt_msg diff --git a/tests/unit/beacons/test_watchdog.py b/tests/unit/beacons/test_watchdog.py index b96a81ccf7bc..ffaa2d6d2930 100644 --- a/tests/unit/beacons/test_watchdog.py +++ b/tests/unit/beacons/test_watchdog.py @@ -9,6 +9,7 @@ # Salt libs import salt.utils.files +import salt.utils.platform from salt.beacons import watchdog from salt.ext.six.moves import range @@ -40,6 +41,7 @@ def create(path, content=None): @skipIf(not watchdog.HAS_WATCHDOG, 'watchdog is not available') +@skipIf(salt.utils.platform.is_darwin(), 'Tests were being skipped pre macos under nox. Keep it like that for now.') class IWatchdogBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.watchdog diff --git a/tests/unit/beacons/test_wtmp.py b/tests/unit/beacons/test_wtmp.py index 125930ceec6d..81d40ee8ba55 100644 --- a/tests/unit/beacons/test_wtmp.py +++ b/tests/unit/beacons/test_wtmp.py @@ -7,7 +7,7 @@ # Salt testing libs from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, mock_open +from tests.support.mock import patch, MagicMock, mock_open from tests.support.mixins import LoaderModuleMockMixin # Salt libs @@ -27,7 +27,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) class WTMPBeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.beacons.[s] diff --git a/tests/unit/cache/test_cache.py b/tests/unit/cache/test_cache.py index da943983bd78..81698d5f6b5c 100644 --- a/tests/unit/cache/test_cache.py +++ b/tests/unit/cache/test_cache.py @@ -8,12 +8,8 @@ # Import Salt Testing libs # import integration -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, - patch, -) +from tests.support.unit import TestCase +from tests.support.mock import patch # Import Salt libs import salt.payload @@ -41,7 +37,6 @@ def test_factory_memcache(self): self.assertIsInstance(ret, salt.cache.MemCache) -@skipIf(NO_MOCK, NO_MOCK_REASON) class MemCacheTest(TestCase): ''' Validate Cache class methods diff --git a/tests/unit/cache/test_localfs.py b/tests/unit/cache/test_localfs.py index b80cfd2031a2..579dfd468be7 100644 --- a/tests/unit/cache/test_localfs.py +++ b/tests/unit/cache/test_localfs.py @@ -10,13 +10,11 @@ import tempfile # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -27,7 +25,6 @@ from salt.exceptions import SaltCacheError -@skipIf(NO_MOCK, NO_MOCK_REASON) class LocalFSTest(TestCase, LoaderModuleMockMixin): ''' Validate the functions in the localfs cache @@ -93,7 +90,7 @@ def test_store_success(self): Tests that the store function writes the data to the serializer for storage. ''' # Create a temporary cache dir - tmp_dir = tempfile.mkdtemp(dir=TMP) + tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # Use the helper function to create the cache file using localfs.store() self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self)) @@ -127,7 +124,7 @@ def test_fetch_success(self): Tests that the fetch function is able to read the cache file and return its data. ''' # Create a temporary cache dir - tmp_dir = tempfile.mkdtemp(dir=TMP) + tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # Create a new serializer object to use in function patches serializer = salt.payload.Serial(self) @@ -164,7 +161,7 @@ def test_updated_success(self): Test that the updated function returns the modification time of the cache file ''' # Create a temporary cache dir - tmp_dir = tempfile.mkdtemp(dir=TMP) + tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # Use the helper function to create the cache file using localfs.store() self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self)) @@ -197,7 +194,7 @@ def test_flush_success(self): ''' with patch('os.path.isfile', MagicMock(return_value=True)): # Create a temporary cache dir - tmp_dir = tempfile.mkdtemp(dir=TMP) + tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # Use the helper function to create the cache file using localfs.store() self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self)) @@ -239,7 +236,7 @@ def test_list_success(self): Tests the return of the ls function containing bank entries. ''' # Create a temporary cache dir - tmp_dir = tempfile.mkdtemp(dir=TMP) + tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # Use the helper function to create the cache file using localfs.store() self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self)) @@ -256,7 +253,7 @@ def test_contains(self): is provided. ''' # Create a temporary cache dir - tmp_dir = tempfile.mkdtemp(dir=TMP) + tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # Use the helper function to create the cache file using localfs.store() self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self)) diff --git a/tests/unit/cli/test_batch.py b/tests/unit/cli/test_batch.py index fdb2da7d6597..acabbe51f562 100644 --- a/tests/unit/cli/test_batch.py +++ b/tests/unit/cli/test_batch.py @@ -10,11 +10,10 @@ from salt.cli.batch import Batch # Import Salt Testing Libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch -@skipIf(NO_MOCK, NO_MOCK_REASON) class BatchTestCase(TestCase): ''' Unit Tests for the salt.cli.batch module diff --git a/tests/unit/cli/test_daemons.py b/tests/unit/cli/test_daemons.py index b6b0cd4ecb79..e17b97961184 100644 --- a/tests/unit/cli/test_daemons.py +++ b/tests/unit/cli/test_daemons.py @@ -9,8 +9,8 @@ import multiprocessing # Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, MagicMock, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.mock import patch, MagicMock from tests.support.mixins import SaltClientTestCaseMixin # Import Salt libs @@ -223,7 +223,6 @@ def _create_syndic(): child_pipe.close() -@skipIf(NO_MOCK, NO_MOCK_REASON) class DaemonsStarterTestCase(TestCase, SaltClientTestCaseMixin): ''' Unit test for the daemons starter classes. diff --git a/tests/unit/client/ssh/__init__.py b/tests/unit/client/ssh/__init__.py new file mode 100644 index 000000000000..40a96afc6ff0 --- /dev/null +++ b/tests/unit/client/ssh/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/tests/unit/client/ssh/wrapper/__init__.py b/tests/unit/client/ssh/wrapper/__init__.py new file mode 100644 index 000000000000..40a96afc6ff0 --- /dev/null +++ b/tests/unit/client/ssh/wrapper/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/tests/unit/client/ssh/wrapper/test_state.py b/tests/unit/client/ssh/wrapper/test_state.py new file mode 100644 index 000000000000..a57b68e15055 --- /dev/null +++ b/tests/unit/client/ssh/wrapper/test_state.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: :email:`saltybob = {0} required'.format(GITPYTHON_MINVER)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class GitPythonTest(GitFSTestBase, GitFSTestFuncs, TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - opts = copy.deepcopy(OPTS) + opts = { + 'sock_dir': self.tmp_sock_dir, + 'gitfs_remotes': ['file://' + self.tmp_repo_dir], + 'gitfs_root': '', + 'fileserver_backend': ['gitfs'], + 'gitfs_base': 'master', + 'fileserver_events': True, + 'transport': 'zeromq', + 'gitfs_mountpoint': '', + 'gitfs_saltenv': [], + 'gitfs_env_whitelist': [], + 'gitfs_env_blacklist': [], + 'gitfs_saltenv_whitelist': [], + 'gitfs_saltenv_blacklist': [], + 'gitfs_user': '', + 'gitfs_password': '', + 'gitfs_insecure_auth': False, + 'gitfs_privkey': '', + 'gitfs_pubkey': '', + 'gitfs_passphrase': '', + 'gitfs_refspecs': [ + '+refs/heads/*:refs/remotes/origin/*', + '+refs/tags/*:refs/tags/*' + ], + 'gitfs_ssl_verify': True, + 'gitfs_disable_saltenv_mapping': False, + 'gitfs_ref_types': ['branch', 'tag', 'sha'], + 'gitfs_update_interval': 60, + '__role': 'master', + } opts['cachedir'] = self.tmp_cachedir opts['sock_dir'] = self.tmp_sock_dir opts['gitfs_provider'] = 'gitpython' @@ -503,11 +521,39 @@ def setup_loader_modules(self): @skipIf(not HAS_GITPYTHON, 'GitPython >= {0} required for temp repo setup'.format(GITPYTHON_MINVER)) @skipIf(not HAS_PYGIT2, 'pygit2 >= {0} and libgit2 >= {1} required'.format(PYGIT2_MINVER, LIBGIT2_MINVER)) @skipIf(salt.utils.platform.is_windows(), 'Skip Pygit2 on windows, due to pygit2 access error on windows') -@skipIf(NO_MOCK, NO_MOCK_REASON) class Pygit2Test(GitFSTestBase, GitFSTestFuncs, TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - opts = copy.deepcopy(OPTS) + opts = { + 'sock_dir': self.tmp_sock_dir, + 'gitfs_remotes': ['file://' + self.tmp_repo_dir], + 'gitfs_root': '', + 'fileserver_backend': ['gitfs'], + 'gitfs_base': 'master', + 'fileserver_events': True, + 'transport': 'zeromq', + 'gitfs_mountpoint': '', + 'gitfs_saltenv': [], + 'gitfs_env_whitelist': [], + 'gitfs_env_blacklist': [], + 'gitfs_saltenv_whitelist': [], + 'gitfs_saltenv_blacklist': [], + 'gitfs_user': '', + 'gitfs_password': '', + 'gitfs_insecure_auth': False, + 'gitfs_privkey': '', + 'gitfs_pubkey': '', + 'gitfs_passphrase': '', + 'gitfs_refspecs': [ + '+refs/heads/*:refs/remotes/origin/*', + '+refs/tags/*:refs/tags/*' + ], + 'gitfs_ssl_verify': True, + 'gitfs_disable_saltenv_mapping': False, + 'gitfs_ref_types': ['branch', 'tag', 'sha'], + 'gitfs_update_interval': 60, + '__role': 'master', + } opts['cachedir'] = self.tmp_cachedir opts['sock_dir'] = self.tmp_sock_dir opts['gitfs_provider'] = 'pygit2' diff --git a/tests/unit/fileserver/test_hgfs.py b/tests/unit/fileserver/test_hgfs.py new file mode 100644 index 000000000000..b1fc50952b7c --- /dev/null +++ b/tests/unit/fileserver/test_hgfs.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase +from tests.support.mock import patch + +# Import Salt libs +import salt.fileserver.hgfs as hgfs + + +class HgfsFileTest(TestCase, LoaderModuleMockMixin): + def setup_loader_modules(self): + return { + hgfs: {} + } + + def test_env_is_exposed(self): + ''' + test _env_is_exposed method when + base is in whitelist + ''' + with patch.dict(hgfs.__opts__, + {'hgfs_saltenv_whitelist': 'base', + 'hgfs_saltenv_blacklist': ''}): + assert hgfs._env_is_exposed('base') + + def test_env_is_exposed_blacklist(self): + ''' + test _env_is_exposed method when + base is in blacklist + ''' + with patch.dict(hgfs.__opts__, + {'hgfs_saltenv_whitelist': '', + 'hgfs_saltenv_blacklist': 'base'}): + assert not hgfs._env_is_exposed('base') diff --git a/tests/unit/fileserver/test_roots.py b/tests/unit/fileserver/test_roots.py index 2c3e6f2d8c0b..e0d939a0861c 100644 --- a/tests/unit/fileserver/test_roots.py +++ b/tests/unit/fileserver/test_roots.py @@ -10,11 +10,10 @@ import tempfile # Import Salt Testing libs -from tests.integration import AdaptedConfigurationTestCaseMixin -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import BASE_FILES, TMP, TMP_STATE_TREE -from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON +from tests.support.mixins import AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin +from tests.support.unit import TestCase +from tests.support.mock import patch +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.fileserver.roots as roots @@ -32,14 +31,11 @@ UNICODE_DIRNAME = UNICODE_ENVNAME = 'соль' -@skipIf(NO_MOCK, NO_MOCK_REASON) class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin): def setup_loader_modules(self): - self.tmp_cachedir = tempfile.mkdtemp(dir=TMP) self.opts = self.get_temp_config('master') - self.opts['cachedir'] = self.tmp_cachedir - empty_dir = os.path.join(TMP_STATE_TREE, 'empty_dir') + empty_dir = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'empty_dir') if not os.path.isdir(empty_dir): os.makedirs(empty_dir) return {roots: {'__opts__': self.opts}} @@ -50,7 +46,7 @@ def setUpClass(cls): Create special file_roots for symlink test on Windows ''' if salt.utils.platform.is_windows(): - root_dir = tempfile.mkdtemp(dir=TMP) + root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) source_sym = os.path.join(root_dir, 'source_sym') with salt.utils.files.fopen(source_sym, 'w') as fp_: fp_.write('hello world!\n') @@ -63,8 +59,8 @@ def setUpClass(cls): cls.test_symlink_list_file_roots = {'base': [root_dir]} else: cls.test_symlink_list_file_roots = None - cls.tmp_dir = tempfile.mkdtemp(dir=TMP) - full_path_to_file = os.path.join(BASE_FILES, 'testfile') + cls.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + full_path_to_file = os.path.join(RUNTIME_VARS.BASE_FILES, 'testfile') with salt.utils.files.fopen(full_path_to_file, 'rb') as s_fp: with salt.utils.files.fopen(os.path.join(cls.tmp_dir, 'testfile'), 'wb') as d_fp: for line in s_fp: @@ -94,7 +90,7 @@ def test_find_file(self): ret = roots.find_file('testfile') self.assertEqual('testfile', ret['rel']) - full_path_to_file = os.path.join(BASE_FILES, 'testfile') + full_path_to_file = os.path.join(RUNTIME_VARS.BASE_FILES, 'testfile') self.assertEqual(full_path_to_file, ret['path']) def test_serve_file(self): @@ -108,7 +104,7 @@ def test_serve_file(self): ret = roots.serve_file(load, fnd) with salt.utils.files.fopen( - os.path.join(BASE_FILES, 'testfile'), 'rb') as fp_: + os.path.join(RUNTIME_VARS.BASE_FILES, 'testfile'), 'rb') as fp_: data = fp_.read() self.assertDictEqual( @@ -138,7 +134,7 @@ def test_file_hash(self): # Hashes are different in Windows. May be how git translates line # endings with salt.utils.files.fopen( - os.path.join(BASE_FILES, 'testfile'), 'rb') as fp_: + os.path.join(RUNTIME_VARS.BASE_FILES, 'testfile'), 'rb') as fp_: hsum = salt.utils.hashutils.sha256_digest(fp_.read()) self.assertDictEqual( @@ -181,7 +177,7 @@ def test_symlink_list(self): self.opts['file_roots'] = orig_file_roots def test_dynamic_file_roots(self): - dyn_root_dir = tempfile.mkdtemp(dir=TMP) + dyn_root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) top_sls = os.path.join(dyn_root_dir, 'top.sls') with salt.utils.files.fopen(top_sls, 'w') as fp_: fp_.write("{{saltenv}}:\n '*':\n - dynamo\n") diff --git a/tests/unit/fileserver/test_svnfs.py b/tests/unit/fileserver/test_svnfs.py new file mode 100644 index 000000000000..b9b39edc07fc --- /dev/null +++ b/tests/unit/fileserver/test_svnfs.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase +from tests.support.mock import patch + +# Import Salt libs +import salt.fileserver.svnfs as svnfs + + +class SvnfsFileTest(TestCase, LoaderModuleMockMixin): + def setup_loader_modules(self): + return { + svnfs: {} + } + + def test_env_is_exposed(self): + ''' + test _env_is_exposed method when + base is in whitelist + ''' + with patch.dict(svnfs.__opts__, + {'svnfs_saltenv_whitelist': 'base', + 'svnfs_saltenv_blacklist': ''}): + assert svnfs._env_is_exposed('base') + + def test_env_is_exposed_blacklist(self): + ''' + test _env_is_exposed method when + base is in blacklist + ''' + with patch.dict(svnfs.__opts__, + {'svnfs_saltenv_whitelist': '', + 'svnfs_saltenv_blacklist': 'base'}): + assert not svnfs._env_is_exposed('base') diff --git a/tests/unit/grains/test_core.py b/tests/unit/grains/test_core.py index 8c45ee194abd..86f7717d322d 100644 --- a/tests/unit/grains/test_core.py +++ b/tests/unit/grains/test_core.py @@ -9,6 +9,7 @@ import os import socket import textwrap +import platform # Import Salt Testing Libs try: @@ -23,8 +24,6 @@ MagicMock, patch, mock_open, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -33,6 +32,8 @@ import salt.utils.network import salt.utils.platform import salt.utils.path +import salt.modules.cmdmod +import salt.modules.smbios import salt.grains.core as core # Import 3rd-party libs @@ -55,7 +56,6 @@ SOLARIS_DIR = os.path.join(os.path.dirname(__file__), 'solaris') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not pytest, False) class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): ''' @@ -821,7 +821,7 @@ def test_bsd_memdata(self): @skipIf(salt.utils.platform.is_windows(), 'System is Windows') def test_docker_virtual(self): ''' - Test if OS grains are parsed correctly in Ubuntu Xenial Xerus + Test if virtual grains are parsed correctly in Docker. ''' with patch.object(os.path, 'isdir', MagicMock(return_value=False)): with patch.object(os.path, @@ -835,27 +835,80 @@ def test_docker_virtual(self): 'Testing Docker cgroup substring \'%s\'', cgroup_substr) with patch('salt.utils.files.fopen', mock_open(read_data=cgroup_data)): with patch.dict(core.__salt__, {'cmd.run_all': MagicMock()}): + grains = core._virtual({'kernel': 'Linux'}) self.assertEqual( - core._virtual({'kernel': 'Linux'}).get('virtual_subtype'), + grains.get('virtual_subtype'), 'Docker' ) + self.assertEqual( + grains.get('virtual'), + 'container', + ) + + @skipIf(salt.utils.platform.is_windows(), 'System is Windows') + def test_lxc_virtual(self): + ''' + Test if virtual grains are parsed correctly in LXC. + ''' + with patch.object(os.path, 'isdir', MagicMock(return_value=False)): + with patch.object(os.path, + 'isfile', + MagicMock(side_effect=lambda x: True if x == '/proc/1/cgroup' else False)): + cgroup_data = '10:memory:/lxc/a_long_sha256sum' + with patch('salt.utils.files.fopen', mock_open(read_data=cgroup_data)): + with patch.dict(core.__salt__, {'cmd.run_all': MagicMock()}): + grains = core._virtual({'kernel': 'Linux'}) + self.assertEqual( + grains.get('virtual_subtype'), + 'LXC' + ) + self.assertEqual( + grains.get('virtual'), + 'container', + ) @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') def test_xen_virtual(self): ''' Test if OS grains are parsed correctly in Ubuntu Xenial Xerus ''' - with patch.object(os.path, 'isfile', MagicMock(return_value=False)): - with patch.dict(core.__salt__, {'cmd.run': MagicMock(return_value='')}), \ - patch.object(os.path, - 'isfile', - MagicMock(side_effect=lambda x: True if x == '/sys/bus/xen/drivers/xenconsole' else False)): + with patch.multiple(os.path, isdir=MagicMock(side_effect=lambda x: x == '/sys/bus/xen'), + isfile=MagicMock(side_effect=lambda x: + x == '/sys/bus/xen/drivers/xenconsole')): + with patch.dict(core.__salt__, {'cmd.run': MagicMock(return_value='')}): log.debug('Testing Xen') self.assertEqual( core._virtual({'kernel': 'Linux'}).get('virtual_subtype'), 'Xen PV DomU' ) + def test_if_virtual_subtype_exists_virtual_should_fallback_to_virtual(self): + def mockstat(path): + if path == '/': + return 'fnord' + elif path == '/proc/1/root/.': + return 'roscivs' + return None + with patch.dict( + core.__salt__, + { + 'cmd.run': MagicMock(return_value=''), + 'cmd.run_all': MagicMock(return_value={'retcode': 0, 'stdout': ''}), + } + ): + with patch.multiple( + os.path, + isfile=MagicMock(return_value=False), + isdir=MagicMock(side_effect=lambda x: x == '/proc'), + ): + with patch.multiple( + os, + stat=MagicMock(side_effect=mockstat), + ): + grains = core._virtual({'kernel': 'Linux'}) + assert grains.get('virtual_subtype') is not None + assert grains.get('virtual') == 'virtual' + def _check_ipaddress(self, value, ip_v): ''' check if ip address in a list is valid @@ -999,6 +1052,38 @@ def test_fqdns_return(self): self.assertEqual(len(fqdns['fqdns']), len(ret['fqdns'])) self.assertEqual(set(fqdns['fqdns']), set(ret['fqdns'])) + @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @patch.object(salt.utils, 'is_windows', MagicMock(return_value=False)) + @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4'])) + @patch('salt.utils.network.ip_addrs6', MagicMock(return_value=[])) + def test_fqdns_socket_error(self): + ''' + test the behavior on non-critical socket errors of the dns grain + ''' + def _gen_gethostbyaddr(errno): + def _gethostbyaddr(_): + herror = socket.herror() + herror.errno = errno + raise herror + return _gethostbyaddr + + for errno in (0, core.HOST_NOT_FOUND, core.NO_DATA): + mock_log = MagicMock() + with patch.object(socket, 'gethostbyaddr', + side_effect=_gen_gethostbyaddr(errno)): + with patch('salt.grains.core.log', mock_log): + self.assertEqual(core.fqdns(), {'fqdns': []}) + mock_log.debug.assert_called_once() + mock_log.error.assert_not_called() + + mock_log = MagicMock() + with patch.object(socket, 'gethostbyaddr', + side_effect=_gen_gethostbyaddr(-1)): + with patch('salt.grains.core.log', mock_log): + self.assertEqual(core.fqdns(), {'fqdns': []}) + mock_log.debug.assert_not_called() + mock_log.error.assert_called_once() + def test_core_virtual(self): ''' test virtual grain with cmd virt-what @@ -1241,3 +1326,109 @@ def test_locale_info_no_tz_tzname(self): is_proxy.assert_called_once_with() is_windows.assert_not_called() self.assertEqual(ret['locale_info']['timezone'], 'unknown') + + def test_cwd_exists(self): + cwd_grain = core.cwd() + + self.assertIsInstance(cwd_grain, dict) + self.assertTrue('cwd' in cwd_grain) + self.assertEqual(cwd_grain['cwd'], os.getcwd()) + + def test_cwd_is_cwd(self): + cwd = os.getcwd() + + try: + # change directory + new_dir = os.path.split(cwd)[0] + os.chdir(new_dir) + + cwd_grain = core.cwd() + + self.assertEqual(cwd_grain['cwd'], new_dir) + finally: + # change back to original directory + os.chdir(cwd) + + def test_virtual_set_virtual_grain(self): + osdata = {} + + (osdata['kernel'], osdata['nodename'], + osdata['kernelrelease'], osdata['kernelversion'], osdata['cpuarch'], _) = platform.uname() + + with patch.dict(core.__salt__, {'cmd.run': salt.modules.cmdmod.run, + 'cmd.run_all': salt.modules.cmdmod.run_all, + 'cmd.retcode': salt.modules.cmdmod.retcode, + 'smbios.get': salt.modules.smbios.get}): + + virtual_grains = core._virtual(osdata) + + self.assertIn('virtual', virtual_grains) + + def test_virtual_has_virtual_grain(self): + osdata = {'virtual': 'something'} + + (osdata['kernel'], osdata['nodename'], + osdata['kernelrelease'], osdata['kernelversion'], osdata['cpuarch'], _) = platform.uname() + + with patch.dict(core.__salt__, {'cmd.run': salt.modules.cmdmod.run, + 'cmd.run_all': salt.modules.cmdmod.run_all, + 'cmd.retcode': salt.modules.cmdmod.retcode, + 'smbios.get': salt.modules.smbios.get}): + + virtual_grains = core._virtual(osdata) + + self.assertIn('virtual', virtual_grains) + self.assertNotEqual(virtual_grains['virtual'], 'physical') + + @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + def test_windows_virtual_set_virtual_grain(self): + osdata = {} + + (osdata['kernel'], osdata['nodename'], + osdata['kernelrelease'], osdata['kernelversion'], osdata['cpuarch'], _) = platform.uname() + + with patch.dict(core.__salt__, {'cmd.run': salt.modules.cmdmod.run, + 'cmd.run_all': salt.modules.cmdmod.run_all, + 'cmd.retcode': salt.modules.cmdmod.retcode, + 'smbios.get': salt.modules.smbios.get}): + + virtual_grains = core._windows_virtual(osdata) + + self.assertIn('virtual', virtual_grains) + + @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + def test_windows_virtual_has_virtual_grain(self): + osdata = {'virtual': 'something'} + + (osdata['kernel'], osdata['nodename'], + osdata['kernelrelease'], osdata['kernelversion'], osdata['cpuarch'], _) = platform.uname() + + with patch.dict(core.__salt__, {'cmd.run': salt.modules.cmdmod.run, + 'cmd.run_all': salt.modules.cmdmod.run_all, + 'cmd.retcode': salt.modules.cmdmod.retcode, + 'smbios.get': salt.modules.smbios.get}): + + virtual_grains = core._windows_virtual(osdata) + + self.assertIn('virtual', virtual_grains) + self.assertNotEqual(virtual_grains['virtual'], 'physical') + + @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + def test_osdata_virtual_key_win(self): + with patch.dict(core.__salt__, {'cmd.run': salt.modules.cmdmod.run, + 'cmd.run_all': salt.modules.cmdmod.run_all, + 'cmd.retcode': salt.modules.cmdmod.retcode, + 'smbios.get': salt.modules.smbios.get}): + + _windows_platform_data_ret = core.os_data() + _windows_platform_data_ret['virtual'] = 'something' + + with patch.object(core, + '_windows_platform_data', + return_value=_windows_platform_data_ret) as _windows_platform_data: + + osdata_grains = core.os_data() + _windows_platform_data.assert_called_once() + + self.assertIn('virtual', osdata_grains) + self.assertNotEqual(osdata_grains['virtual'], 'physical') diff --git a/tests/unit/grains/test_disks.py b/tests/unit/grains/test_disks.py index 635e0194418a..9b23f1e8bc85 100644 --- a/tests/unit/grains/test_disks.py +++ b/tests/unit/grains/test_disks.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.grains.disks as disks -@skipIf(NO_MOCK, NO_MOCK_REASON) class IscsiGrainsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for _windows_disks grains diff --git a/tests/unit/grains/test_fibre_channel.py b/tests/unit/grains/test_fibre_channel.py index 4481079c5e4d..a7d9058f6a26 100644 --- a/tests/unit/grains/test_fibre_channel.py +++ b/tests/unit/grains/test_fibre_channel.py @@ -6,20 +6,17 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, mock_open, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.grains.fibre_channel as fibre_channel -@skipIf(NO_MOCK, NO_MOCK_REASON) class FibreChannelGrainsTestCase(TestCase): ''' Test cases for iscsi grains diff --git a/tests/unit/grains/test_iscsi.py b/tests/unit/grains/test_iscsi.py index 93adfeb708df..168ea0746e42 100644 --- a/tests/unit/grains/test_iscsi.py +++ b/tests/unit/grains/test_iscsi.py @@ -8,20 +8,17 @@ import textwrap # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, mock_open, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.grains.iscsi as iscsi -@skipIf(NO_MOCK, NO_MOCK_REASON) class IscsiGrainsTestCase(TestCase): ''' Test cases for iscsi grains diff --git a/tests/unit/grains/test_napalm.py b/tests/unit/grains/test_napalm.py index 63f190072116..3446975a51a6 100644 --- a/tests/unit/grains/test_napalm.py +++ b/tests/unit/grains/test_napalm.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -35,7 +33,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) @patch('salt.grains.napalm.DEVICE_CACHE', TEST_DEVICE_CACHE) @patch('salt.grains.napalm.GRAINS_CACHE', TEST_CACHE) class NapalmGrainsTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/grains/test_nvme.py b/tests/unit/grains/test_nvme.py new file mode 100644 index 000000000000..c323e425506c --- /dev/null +++ b/tests/unit/grains/test_nvme.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: :email:`Simon Dodsley ` +''' +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals +import errno +import textwrap + +# Import Salt Testing Libs +from tests.support.unit import TestCase +from tests.support.mock import ( + patch, + mock_open, + MagicMock +) + +# Import Salt Libs +import salt.grains.nvme as nvme + + +class NvmeGrainsTestCase(TestCase): + ''' + Test cases for nvme grains + ''' + + def test_linux_nvme_nqn_grains(self): + _nvme_file = textwrap.dedent('''\ + nqn.2014-08.org.nvmexpress:fc_lif:uuid:2cd61a74-17f9-4c22-b350-3020020c458d + ''') + + with patch('salt.utils.files.fopen', mock_open(read_data=_nvme_file)): + nqn = nvme._linux_nqn() + + assert isinstance(nqn, list) + assert len(nqn) == 1 + assert nqn == ['nqn.2014-08.org.nvmexpress:fc_lif:uuid:2cd61a74-17f9-4c22-b350-3020020c458d'] + + @patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(errno.EPERM, + 'The cables are not the same length.'))) + @patch('salt.grains.nvme.log', MagicMock()) + def test_linux_nqn_non_root(self): + ''' + Test if linux_nqn is running on salt-master as non-root + and handling access denial properly. + :return: + ''' + assert nvme._linux_nqn() == [] + nvme.log.debug.assert_called() + assert 'Error while accessing' in nvme.log.debug.call_args[0][0] + assert 'cables are not the same' in nvme.log.debug.call_args[0][2].strerror + assert nvme.log.debug.call_args[0][2].errno == errno.EPERM + assert nvme.log.debug.call_args[0][1] == '/etc/nvme/hostnqn' + + @patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(errno.ENOENT, ''))) + @patch('salt.grains.nvme.log', MagicMock()) + def test_linux_nqn_no_nvme_initiator(self): + ''' + Test if linux_nqn is running on salt-master as root. + nvme initiator is not there accessible or is not supported. + :return: + ''' + assert nvme._linux_nqn() == [] + nvme.log.debug.assert_not_called() diff --git a/tests/unit/modules/inspectlib/test_collector.py b/tests/unit/modules/inspectlib/test_collector.py index 28e298a0dd4b..677d0357e7f0 100644 --- a/tests/unit/modules/inspectlib/test_collector.py +++ b/tests/unit/modules/inspectlib/test_collector.py @@ -26,15 +26,12 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import salt libs from salt.modules.inspectlib.collector import Inspector -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(no_symlinks(), "Git missing 'core.symlinks=true' config") class InspectorCollectorTestCase(TestCase): ''' diff --git a/tests/unit/modules/test_acme.py b/tests/unit/modules/test_acme.py new file mode 100644 index 000000000000..f5ade73a2bbd --- /dev/null +++ b/tests/unit/modules/test_acme.py @@ -0,0 +1,253 @@ +# -*- coding: utf-8 -*- +''' +:codeauthor: Herbert Buurman +''' + +# Import future libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Python libs +import textwrap +import datetime +import os + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch + +# Import Salt Module +import salt.modules.acme as acme +import salt.utils.dictupdate +from salt.exceptions import SaltInvocationError + + +class AcmeTestCase(TestCase, LoaderModuleMockMixin): + ''' + Test cases for salt.modules.acme + ''' + + def setup_loader_modules(self): + return {acme: {}} + + def test_certs(self): + ''' + Test listing certs + ''' + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'file.readdir': MagicMock(return_value=['.', '..', 'README', 'test_expired', 'test_valid']) + }), \ + patch('os.path.isdir', side_effect=[False, True, True]): + self.assertEqual(acme.certs(), ['test_expired', 'test_valid']) + + def test_has(self): + ''' + Test checking if certificate (does not) exist. + ''' + with patch.dict(acme.__salt__, {'file.file_exists': MagicMock(return_value=True)}): # pylint: disable=no-member + self.assertTrue(acme.has('test_expired')) + with patch.dict(acme.__salt__, {'file.file_exists': MagicMock(return_value=False)}): # pylint: disable=no-member + self.assertFalse(acme.has('test_invalid')) + + def test_needs_renewal(self): + ''' + Test if expired certs do indeed need renewal. + ''' + expired = datetime.date.today() - datetime.timedelta(days=3) - datetime.date(1970, 1, 1) + valid = datetime.date.today() + datetime.timedelta(days=3) - datetime.date(1970, 1, 1) + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'tls.cert_info': MagicMock(return_value={'not_after': expired.total_seconds()}) + }): + self.assertTrue(acme.needs_renewal('test_expired')) + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'tls.cert_info': MagicMock(return_value={'not_after': valid.total_seconds()}) + }): + self.assertFalse(acme.needs_renewal('test_valid')) + # Test with integer window parameter + self.assertTrue(acme.needs_renewal('test_valid', window=5)) + # Test with string-like window parameter + self.assertTrue(acme.needs_renewal('test_valid', window='5')) + # Test with invalid window parameter + self.assertRaises(SaltInvocationError, acme.needs_renewal, 'test_valid', window='foo') + + def test_expires(self): + ''' + Test if expires function functions properly. + ''' + test_value = datetime.datetime.today() - datetime.timedelta(days=3) + test_stamp = test_value - datetime.datetime(1970, 1, 1) + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'tls.cert_info': MagicMock(return_value={'not_after': test_stamp.total_seconds()}) + }): + self.assertEqual( + acme.expires('test_expired'), + datetime.datetime.fromtimestamp(test_stamp.total_seconds()).isoformat() + ) + + def test_info(self): + ''' + Test certificate information retrieval. + ''' + certinfo_tls_result = { + "not_after": 1559471377, + "signature_algorithm": "sha256WithRSAEncryption", + "extensions": {}, + "fingerprint": ("FB:A4:5F:71:D6:5D:6C:B6:1D:2C:FD:91:09:2C:1C:52:" + "3C:EC:B6:4D:1A:95:65:37:04:D0:E2:5E:C7:64:0C:9C"), + "serial_number": 6461481982668892235, + "issuer": {}, + "not_before": 1559557777, + "subject": {}, + } + certinfo_x509_result = { + "Not After": "2019-06-02 10:29:37", + "Subject Hash": "54:3B:6C:A4", + "Serial Number": "59:AB:CB:A0:FB:90:E8:4B", + "SHA1 Finger Print": "F1:8D:F3:26:1B:D3:88:32:CD:B6:FA:3B:85:58:DA:C7:6F:62:BE:7E", + "SHA-256 Finger Print": ("FB:A4:5F:71:D6:5D:6C:B6:1D:2C:FD:91:09:2C:1C:52:" + "3C:EC:B6:4D:1A:95:65:37:04:D0:E2:5E:C7:64:0C:9C"), + "MD5 Finger Print": "95:B5:96:9B:42:A5:9E:20:78:FD:99:09:4B:21:1E:97", + "Version": 3, + "Key Size": 2048, + "Public Key": ("-----BEGIN PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsVO2vwQPKU92PSBnuGid\n" + "k8t6KWVE2jEBM10u7CgqQmD/JCnYflEHAo1nOsD7wxdhBrxhf5Qs+pEX1HOsh8VA\n" + "HDTim0iE8nQVJ0Iuen2SrwaWMhwKmZTSJRYMgd46oCMi2RdlCvcgF2Hw6RTwF7FT\n" + "hnksc4HBT91XddnP32N558tOT3YejafQNvClz5WcR+E0JzqGrV/+wfe3o+j/q5eK\n" + "UowttWazeSMvuROtqj/fEk0rop4D14pgzZqWi30tjwhJNl6fSPFWBrLEHGNyDJ+O\n" + "zfov0B2MRLJibH7GMkOCwsP2g1lVOReqcml+ju6zAKW8nHBTRg0iXB18Ifxef57Y\n" + "AQIDAQAB\n" + "-----END PUBLIC KEY-----\n"), + "Issuer": {}, + "Issuer Hash": "54:3B:6C:A4", + "Not Before": "2019-06-03 10:29:37", + "Subject": {} + } + + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'file.file_exists': MagicMock(return_value=True), + 'tls.cert_info': MagicMock(return_value=certinfo_tls_result), + }): + self.assertEqual(acme.info('test'), certinfo_tls_result) + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'file.file_exists': MagicMock(return_value=True), + 'x509.read_certificate': MagicMock(return_value=certinfo_x509_result), + }): + self.assertEqual(acme.info('test'), certinfo_x509_result) + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'file.file_exists': MagicMock(return_value=True), + 'cmd.run': MagicMock(return_value='foo'), + }): + self.assertEqual(acme.info('test'), {'text': 'foo'}) + + def test_cert(self): + ''' + Test certificate retrieval/renewal + ''' + valid_timestamp = (datetime.datetime.now() + datetime.timedelta(days=30) - + datetime.datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds() + expired_timestamp = (datetime.datetime.now() - datetime.timedelta(days=3) - + datetime.datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds() + cmd_new_cert = { + 'stdout': textwrap.dedent(''' + IMPORTANT NOTES: + - Congratulations! Your certificate and chain have been saved at: + /etc/letsencrypt/live/test/fullchain.pem + Your key file has been saved at: + /etc/letsencrypt/live/test/privkey.pem + Your cert will expire on 2019-08-07. To obtain a new or tweaked + version of this certificate in the future, simply run certbot + again. To non-interactively renew *all* of your certificates, run + "certbot renew" + - If you like Certbot, please consider supporting our work by: + + Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate + Donating to EFF: https://eff.org/donate-le + '''), + 'stderr': textwrap.dedent(''' + Saving debug log to /var/log/letsencrypt/letsencrypt.log + Plugins selected: Authenticator standalone, Installer None + Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org + Obtaining a new certificate + Resetting dropped connection: acme-v02.api.letsencrypt.org + '''), + 'retcode': 0, + } + result_new_cert = { + "comment": "Certificate test obtained", + "not_after": datetime.datetime.fromtimestamp(valid_timestamp).isoformat(), + "changes": { + "mode": "0640" + }, + "result": True + } + + cmd_no_renew = { + 'stdout': textwrap.dedent(''' + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Certificate not yet due for renewal; no action taken. + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + '''), + 'stderr': textwrap.dedent('''Saving debug log to /var/log/letsencrypt/letsencrypt.log + Plugins selected: Authenticator standalone, Installer None + Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org + Cert not yet due for renewal + Keeping the existing certificate + '''), + 'retcode': 0 + } + result_no_renew = { + "comment": "Certificate " + os.path.join('/etc/letsencrypt/live/test', 'cert.pem') + " unchanged", + "not_after": datetime.datetime.fromtimestamp(valid_timestamp).isoformat(), + "changes": {}, + "result": True + } + result_renew = { + "comment": "Certificate test renewed", + "not_after": datetime.datetime.fromtimestamp(expired_timestamp).isoformat(), + "changes": {}, + "result": True + } + + # Test fetching new certificate + with patch('salt.modules.acme.LEA', 'certbot'), \ + patch.dict(acme.__salt__, { # pylint: disable=no-member + 'cmd.run_all': MagicMock(return_value=cmd_new_cert), + 'file.file_exists': MagicMock(return_value=False), + 'tls.cert_info': MagicMock(return_value={'not_after': valid_timestamp}), + 'file.check_perms': MagicMock( + side_effect=lambda a, x, b, c, d, follow_symlinks: ( + salt.utils.dictupdate.set_dict_key_value(x, 'changes:mode', '0640'), + None + ) + ) + }): + self.assertEqual(acme.cert('test'), result_new_cert) + # Test not renewing a valid certificate + with patch('salt.modules.acme.LEA', 'certbot'), \ + patch.dict(acme.__salt__, { # pylint: disable=no-member + 'cmd.run_all': MagicMock(return_value=cmd_no_renew), + 'file.file_exists': MagicMock(return_value=True), + 'tls.cert_info': MagicMock(return_value={'not_after': valid_timestamp}), + 'file.check_perms': MagicMock( + side_effect=lambda a, x, b, c, d, follow_symlinks: ( + salt.utils.dictupdate.set_dict_key_value(x, 'result', True), + None + ) + ) + }): + self.assertEqual(acme.cert('test'), result_no_renew) + # Test renewing an expired certificate + with patch('salt.modules.acme.LEA', 'certbot'), \ + patch.dict(acme.__salt__, { # pylint: disable=no-member + 'cmd.run_all': MagicMock(return_value=cmd_new_cert), + 'file.file_exists': MagicMock(return_value=True), + 'tls.cert_info': MagicMock(return_value={'not_after': expired_timestamp}), + 'file.check_perms': MagicMock( + side_effect=lambda a, x, b, c, d, follow_symlinks: ( + salt.utils.dictupdate.set_dict_key_value(x, 'result', True), + None + ) + ) + }): + self.assertEqual(acme.cert('test'), result_renew) diff --git a/tests/unit/modules/test_aliases.py b/tests/unit/modules/test_aliases.py index 21cb020062d2..a9217393ffc3 100644 --- a/tests/unit/modules/test_aliases.py +++ b/tests/unit/modules/test_aliases.py @@ -12,11 +12,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch -@skipIf(NO_MOCK, NO_MOCK_REASON) class AliasesTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.aliases module diff --git a/tests/unit/modules/test_alternatives.py b/tests/unit/modules/test_alternatives.py index a79d1ce88d78..06ac44d9eb90 100644 --- a/tests/unit/modules/test_alternatives.py +++ b/tests/unit/modules/test_alternatives.py @@ -11,16 +11,15 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs +from tests.support.unit import TestCase +from tests.support.helpers import TstSuiteLoggingHandler from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.helpers import TestsLoggingHandler -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch # Import salt libs import salt.modules.alternatives as alternatives -@skipIf(NO_MOCK, NO_MOCK_REASON) class AlternativesTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -72,7 +71,7 @@ def test_show_current(self): self.assertEqual('/etc/alternatives/salt', ret) mock.assert_called_once_with('/etc/alternatives/better-world') - with TestsLoggingHandler() as handler: + with TstSuiteLoggingHandler() as handler: mock.side_effect = OSError('Hell was not found!!!') self.assertFalse(alternatives.show_current('hell')) mock.assert_called_with('/etc/alternatives/hell') diff --git a/tests/unit/modules/test_ansiblegate.py b/tests/unit/modules/test_ansiblegate.py index 1fbb083eb758..5613a0e79b38 100644 --- a/tests/unit/modules/test_ansiblegate.py +++ b/tests/unit/modules/test_ansiblegate.py @@ -29,8 +29,6 @@ from tests.support.mock import ( patch, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import salt.modules.ansiblegate as ansible @@ -38,7 +36,6 @@ from salt.exceptions import LoaderError -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(NO_PYTEST, False) @skipIf(salt.utils.platform.is_windows(), 'Not supported on Windows') class AnsiblegateTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/modules/test_apache.py b/tests/unit/modules/test_apache.py index 7e6511223a12..accf112304a5 100644 --- a/tests/unit/modules/test_apache.py +++ b/tests/unit/modules/test_apache.py @@ -8,13 +8,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, mock_open, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -23,7 +21,6 @@ from salt.utils.odict import OrderedDict -@skipIf(NO_MOCK, NO_MOCK_REASON) class ApacheTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.apache diff --git a/tests/unit/modules/test_aptpkg.py b/tests/unit/modules/test_aptpkg.py index 1e963ee5dbd0..c5e5310357ae 100644 --- a/tests/unit/modules/test_aptpkg.py +++ b/tests/unit/modules/test_aptpkg.py @@ -14,7 +14,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.mock import Mock, MagicMock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.mock import Mock, MagicMock, patch # Import Salt Libs from salt.ext import six @@ -90,7 +90,26 @@ 'name': 'wget', 'section': 'web', 'source': 'wget', - 'version': '1.15-1ubuntu1.14.04.2' + 'version': '1.15-1ubuntu1.14.04.2', + 'status': 'ii', + }, + 'apache2': { + 'architecture': 'amd64', + 'description': """Apache HTTP Server + The Apache HTTP Server Project's goal is to build a secure, efficient and + extensible HTTP server as standards-compliant open source software. The + result has long been the number one web server on the Internet. + . + Installing this package results in a full installation, including the + configuration files, init scripts and support scripts.""", + 'homepage': 'http://httpd.apache.org/', + 'install_date': '2016-08-30T22:20:15Z', + 'maintainer': 'Ubuntu Developers ', + 'name': 'apache2', + 'section': 'httpd', + 'source': 'apache2', + 'version': '2.4.18-2ubuntu3.9', + 'status': 'rc', } } @@ -139,7 +158,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class AptPkgTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.aptpkg @@ -244,14 +262,16 @@ def test_info_installed(self): 'url': 'homepage' } - installed = copy.deepcopy(LOWPKG_INFO) + installed = copy.deepcopy({'wget': LOWPKG_INFO['wget']}) for name in names: if installed['wget'].get(names[name], False): installed['wget'][name] = installed['wget'].pop(names[name]) mock = MagicMock(return_value=LOWPKG_INFO) with patch.dict(aptpkg.__salt__, {'lowpkg.info': mock}): + del installed['wget']['status'] self.assertEqual(aptpkg.info_installed('wget'), installed) + self.assertEqual(len(aptpkg.info_installed()), 1) def test_owner(self): ''' @@ -348,6 +368,39 @@ def test_upgrade(self): with patch.multiple(aptpkg, **patch_kwargs): self.assertEqual(aptpkg.upgrade(), dict()) + def test_upgrade_downloadonly(self): + ''' + Tests the download-only options for upgrade. + ''' + with patch('salt.utils.pkg.clear_rtag', MagicMock()): + with patch('salt.modules.aptpkg.list_pkgs', + MagicMock(return_value=UNINSTALL)): + mock_cmd = MagicMock(return_value={ + 'retcode': 0, + 'stdout': UPGRADE + }) + patch_kwargs = { + '__salt__': { + 'config.get': MagicMock(return_value=True), + 'cmd.run_all': mock_cmd + }, + } + with patch.multiple(aptpkg, **patch_kwargs): + aptpkg.upgrade() + args_matching = [True for args in patch_kwargs['__salt__']['cmd.run_all'].call_args[0] if "--download-only" in args] + # Here we shouldn't see the parameter and args_matching should be empty. + self.assertFalse(any(args_matching)) + + aptpkg.upgrade(downloadonly=True) + args_matching = [True for args in patch_kwargs['__salt__']['cmd.run_all'].call_args[0] if "--download-only" in args] + # --download-only should be in the args list and we should have at least on True in the list. + self.assertTrue(any(args_matching)) + + aptpkg.upgrade(download_only=True) + args_matching = [True for args in patch_kwargs['__salt__']['cmd.run_all'].call_args[0] if "--download-only" in args] + # --download-only should be in the args list and we should have at least on True in the list. + self.assertTrue(any(args_matching)) + def test_show(self): ''' Test that the pkg.show function properly parses apt-cache show output. diff --git a/tests/unit/modules/test_archive.py b/tests/unit/modules/test_archive.py index 66e7da93c977..d0a402e395df 100644 --- a/tests/unit/modules/test_archive.py +++ b/tests/unit/modules/test_archive.py @@ -14,7 +14,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch # Import salt libs import salt.modules.archive as archive @@ -37,7 +37,6 @@ def namelist(self): return self._files -@skipIf(NO_MOCK, NO_MOCK_REASON) class ArchiveTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_artifactory.py b/tests/unit/modules/test_artifactory.py index 865e37c57c34..67bcf67a6c35 100644 --- a/tests/unit/modules/test_artifactory.py +++ b/tests/unit/modules/test_artifactory.py @@ -5,14 +5,13 @@ # Import Salt testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt libs import salt.modules.artifactory as artifactory -@skipIf(NO_MOCK, NO_MOCK_REASON) class ArtifactoryTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_at.py b/tests/unit/modules/test_at.py index d8b1ebf86ae7..0b5978a2b019 100644 --- a/tests/unit/modules/test_at.py +++ b/tests/unit/modules/test_at.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ import salt.modules.at as at -@skipIf(NO_MOCK, NO_MOCK_REASON) class AtTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for the salt.modules.at module diff --git a/tests/unit/modules/test_azurearm_dns.py b/tests/unit/modules/test_azurearm_dns.py new file mode 100644 index 000000000000..c641a399df93 --- /dev/null +++ b/tests/unit/modules/test_azurearm_dns.py @@ -0,0 +1,202 @@ + +# -*- coding: utf-8 -*- + +# import Python Libs +from __future__ import absolute_import, print_function, unicode_literals +import logging + +# Import Salt Libs +import salt.config +import salt.loader +import salt.modules.azurearm_dns as azurearm_dns + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import skipIf, TestCase +from tests.support.mock import MagicMock + +# Azure libs +# pylint: disable=import-error +HAS_LIBS = False +try: + import azure.mgmt.dns.models + HAS_LIBS = True +except ImportError: + pass + +# pylint: enable=import-error + +log = logging.getLogger(__name__) + +MOCK_CREDENTIALS = { + 'client_id': 'CLIENT_ID', + 'secret': 'SECRET', + 'subscription_id': 'SUBSCRIPTION_ID', + 'tenant': 'TENANT' +} + + +class AzureObjMock(object): + ''' + mock azure object for as_dict calls + ''' + args = None + kwargs = None + + def __init__(self, args, kwargs, return_value=None): + self.args = args + self.kwargs = kwargs + self.__return_value = return_value + + def __getattr__(self, item): + return self + + def __call__(self, *args, **kwargs): + return MagicMock(return_value=self.__return_value)() + + def as_dict(self, *args, **kwargs): + return self.args, self.kwargs + + +class AzureFuncMock(object): + ''' + mock azure client function calls + ''' + def __init__(self, return_value=None): + self.__return_value = return_value + + def __getattr__(self, item): + return self + + def __call__(self, *args, **kwargs): + return MagicMock(return_value=self.__return_value)() + + def create_or_update(self, *args, **kwargs): + azure_obj = AzureObjMock(args, kwargs) + return azure_obj + + +class AzureSubMock(object): + ''' + mock azure client sub-modules + ''' + record_sets = AzureFuncMock() + zones = AzureFuncMock() + + def __init__(self, return_value=None): + self.__return_value = return_value + + def __getattr__(self, item): + return self + + def __call__(self, *args, **kwargs): + return MagicMock(return_value=self.__return_value)() + + +class AzureClientMock(object): + ''' + mock azure client + ''' + def __init__(self, return_value=AzureSubMock): + self.__return_value = return_value + + def __getattr__(self, item): + return self + + def __call__(self, *args, **kwargs): + return MagicMock(return_value=self.__return_value)() + + +@skipIf(HAS_LIBS is False, 'The azure.mgmt.dns module must be installed.') +class AzureRmDnsTestCase(TestCase, LoaderModuleMockMixin): + ''' + TestCase for salt.modules.azurearm_dns module + ''' + def setup_loader_modules(self): + ''' + setup loader modules and override the azurearm.get_client utility + ''' + self.opts = salt.config.DEFAULT_MINION_OPTS.copy() + utils = salt.loader.utils(self.opts) + funcs = salt.loader.minion_mods(self.opts, utils=utils, whitelist=['azurearm_dns', 'config']) + utils['azurearm.get_client'] = AzureClientMock() + return { + azurearm_dns: { + '__opts__': self.opts, + '__utils__': utils, + '__salt__': funcs + }, + } + + def setUp(self): + ''' + setup + ''' + TestCase.setUp(self) + azurearm_dns.__virtual__() + + def tearDown(self): + ''' + tear down + ''' + del self.opts + + def test_record_set_create_or_update(self): # pylint: disable=invalid-name + ''' + tests record set object creation + ''' + expected = { + 'if_match': None, + 'if_none_match': None, + 'parameters': { + 'arecords': [{'ipv4_address': '10.0.0.1'}], + 'ttl': 300 + }, + 'record_type': 'A', + 'relative_record_set_name': 'myhost', + 'resource_group_name': 'testgroup', + 'zone_name': 'myzone' + } + + record_set_args, record_set_kwargs = azurearm_dns.record_set_create_or_update( + 'myhost', + 'myzone', + 'testgroup', + 'A', + arecords=[{'ipv4_address': '10.0.0.1'}], + ttl=300, + **MOCK_CREDENTIALS + ) + + for key, val in record_set_kwargs.items(): + if isinstance(val, azure.mgmt.dns.models.RecordSet): + record_set_kwargs[key] = val.as_dict() + + self.assertEqual(record_set_kwargs, expected) + + def test_zone_create_or_update(self): + ''' + tests zone object creation + ''' + expected = { + 'if_match': None, + 'if_none_match': None, + 'parameters': { + 'location': 'global', + 'zone_type': 'Public' + }, + 'resource_group_name': 'testgroup', + 'zone_name': 'myzone' + } + + zone_args, zone_kwargs = azurearm_dns.zone_create_or_update( + 'myzone', + 'testgroup', + **MOCK_CREDENTIALS + ) + + for key, val in zone_kwargs.items(): + if isinstance(val, azure.mgmt.dns.models.Zone): + zone_kwargs[key] = val.as_dict() + + self.assertEqual(zone_kwargs, expected) diff --git a/tests/unit/modules/test_beacons.py b/tests/unit/modules/test_beacons.py index 4ea673ef685c..fd1eaf34ca4a 100644 --- a/tests/unit/modules/test_beacons.py +++ b/tests/unit/modules/test_beacons.py @@ -8,28 +8,28 @@ import os # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.beacons as beacons from salt.utils.event import SaltEvent -SOCK_DIR = os.path.join(TMP, 'test-socks') - -@skipIf(NO_MOCK, NO_MOCK_REASON) class BeaconsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.beacons ''' + + @classmethod + def setUpClass(cls): + cls.sock_dir = os.path.join(RUNTIME_VARS.TMP, 'test-socks') + def setup_loader_modules(self): return {beacons: {}} @@ -44,7 +44,7 @@ def test_delete(self): 'beacons': {}}, ] - with patch.dict(beacons.__opts__, {'beacons': {'ps': [{'processes': {'salt-master': 'stopped', 'apache2': 'stopped'}}]}, 'sock_dir': SOCK_DIR}): + with patch.dict(beacons.__opts__, {'beacons': {'ps': [{'processes': {'salt-master': 'stopped', 'apache2': 'stopped'}}]}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(beacons.__salt__, {'event.fire': mock}): with patch.object(SaltEvent, 'get_event', side_effect=event_returns): @@ -70,7 +70,7 @@ def test_add(self): 'tag': '/salt/minion/minion_beacon_add_complete', 'beacons': {'ps': [{'processes': {'salt-master': 'stopped', 'apache2': 'stopped'}}]}}] - with patch.dict(beacons.__opts__, {'beacons': {}, 'sock_dir': SOCK_DIR}): + with patch.dict(beacons.__opts__, {'beacons': {}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(beacons.__salt__, {'event.fire': mock}): with patch.object(SaltEvent, 'get_event', side_effect=event_returns): @@ -81,10 +81,11 @@ def test_save(self): ''' Test saving beacons. ''' - comm1 = 'Beacons saved to {0}beacons.conf.'.format(TMP + os.sep) - with patch.dict(beacons.__opts__, {'config_dir': '', 'beacons': {}, - 'default_include': TMP + os.sep, - 'sock_dir': SOCK_DIR}): + comm1 = 'Beacons saved to {0}beacons.conf.'.format(RUNTIME_VARS.TMP + os.sep) + with patch.dict(beacons.__opts__, {'conf_file': os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'foo'), + 'beacons': {}, + 'default_include': RUNTIME_VARS.TMP + os.sep, + 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(beacons.__salt__, {'event.fire': mock}): @@ -104,7 +105,7 @@ def test_disable(self): 'ps': [{'processes': {'salt-master': 'stopped', 'apache2': 'stopped'}}]}}] - with patch.dict(beacons.__opts__, {'beacons': {}, 'sock_dir': SOCK_DIR}): + with patch.dict(beacons.__opts__, {'beacons': {}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(beacons.__salt__, {'event.fire': mock}): with patch.object(SaltEvent, 'get_event', side_effect=event_returns): @@ -122,7 +123,7 @@ def test_enable(self): 'ps': [{'processes': {'salt-master': 'stopped', 'apache2': 'stopped'}}]}}] - with patch.dict(beacons.__opts__, {'beacons': {}, 'sock_dir': SOCK_DIR}): + with patch.dict(beacons.__opts__, {'beacons': {}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(beacons.__salt__, {'event.fire': mock}): with patch.object(SaltEvent, 'get_event', side_effect=event_returns): diff --git a/tests/unit/modules/test_bluez_bluetooth.py b/tests/unit/modules/test_bluez_bluetooth.py index f9ac979cab4f..78b5cfcf9fcd 100644 --- a/tests/unit/modules/test_bluez_bluetooth.py +++ b/tests/unit/modules/test_bluez_bluetooth.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -37,7 +35,6 @@ def discover_devices(lookup_names): return [['a', 'b', 'c'], ['d', 'e', 'f']] -@skipIf(NO_MOCK, NO_MOCK_REASON) class BluezTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.bluez diff --git a/tests/unit/modules/test_boto_apigateway.py b/tests/unit/modules/test_boto_apigateway.py index be26b3f093db..ac36d2c23ceb 100644 --- a/tests/unit/modules/test_boto_apigateway.py +++ b/tests/unit/modules/test_boto_apigateway.py @@ -10,7 +10,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch # Import Salt libs import salt.loader @@ -173,7 +173,7 @@ class BotoApiGatewayTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( opts, whitelist=['boto3', 'args', 'systemd', 'path', 'platform']) @@ -232,7 +232,6 @@ def _diff_list_dicts(self, listdict1, listdict2, sortkey): @skipIf(_has_required_botocore() is False, 'The botocore module must be greater than' ' or equal to version {0}'.format(required_botocore_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoApiGatewayTestCase(BotoApiGatewayTestCaseBase, BotoApiGatewayTestCaseMixin): ''' TestCase for salt.modules.boto_apigateway module diff --git a/tests/unit/modules/test_boto_cloudtrail.py b/tests/unit/modules/test_boto_cloudtrail.py index b01ed59de20a..f044bed64319 100644 --- a/tests/unit/modules/test_boto_cloudtrail.py +++ b/tests/unit/modules/test_boto_cloudtrail.py @@ -11,8 +11,6 @@ from tests.support.unit import skipIf, TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -97,12 +95,11 @@ def _has_required_boto(): @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCloudTrailTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( opts, whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], diff --git a/tests/unit/modules/test_boto_cloudwatch_event.py b/tests/unit/modules/test_boto_cloudwatch_event.py index 6057331e38e2..a191b9be4d80 100644 --- a/tests/unit/modules/test_boto_cloudwatch_event.py +++ b/tests/unit/modules/test_boto_cloudwatch_event.py @@ -11,8 +11,6 @@ from tests.support.unit import skipIf, TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -91,7 +89,7 @@ class BotoCloudWatchEventTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( opts, whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], @@ -125,7 +123,6 @@ class BotoCloudWatchEventTestCaseMixin(object): @skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCloudWatchEventTestCase(BotoCloudWatchEventTestCaseBase, BotoCloudWatchEventTestCaseMixin): ''' TestCase for salt.modules.boto_cloudwatch_event module diff --git a/tests/unit/modules/test_boto_cognitoidentity.py b/tests/unit/modules/test_boto_cognitoidentity.py index 0030b68be1ef..2661547b32ad 100644 --- a/tests/unit/modules/test_boto_cognitoidentity.py +++ b/tests/unit/modules/test_boto_cognitoidentity.py @@ -9,7 +9,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch # Import Salt libs import salt.config @@ -116,7 +116,7 @@ class BotoCognitoIdentityTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( opts, whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], @@ -154,7 +154,6 @@ class BotoCognitoIdentityTestCaseMixin(object): @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCognitoIdentityTestCase(BotoCognitoIdentityTestCaseBase, BotoCognitoIdentityTestCaseMixin): ''' TestCase for salt.modules.boto_cognitoidentity module diff --git a/tests/unit/modules/test_boto_elasticsearch_domain.py b/tests/unit/modules/test_boto_elasticsearch_domain.py index e39487ac30d0..50c826d0d363 100644 --- a/tests/unit/modules/test_boto_elasticsearch_domain.py +++ b/tests/unit/modules/test_boto_elasticsearch_domain.py @@ -11,8 +11,6 @@ from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -88,7 +86,7 @@ class BotoElasticsearchDomainTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): - self.opts = salt.config.DEFAULT_MINION_OPTS + self.opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( self.opts, whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], @@ -126,7 +124,6 @@ class BotoElasticsearchDomainTestCaseMixin(object): @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoElasticsearchDomainTestCase(BotoElasticsearchDomainTestCaseBase, BotoElasticsearchDomainTestCaseMixin): ''' TestCase for salt.modules.boto_elasticsearch_domain module diff --git a/tests/unit/modules/test_boto_elb.py b/tests/unit/modules/test_boto_elb.py index 9da5c74c33b3..b84f8b8805b8 100644 --- a/tests/unit/modules/test_boto_elb.py +++ b/tests/unit/modules/test_boto_elb.py @@ -8,14 +8,14 @@ import os.path import sys -# imprt salt paths -from tests.support.paths import TESTS_DIR +# Import test support libs +from tests.support.runtests import RUNTIME_VARS # import Python Third Party Libs # pylint: disable=import-error try: import boto - boto.ENDPOINTS_PATH = os.path.join(TESTS_DIR, 'unit/files/endpoints.json') + boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') import boto.ec2.elb HAS_BOTO = True except ImportError: @@ -60,7 +60,6 @@ def stub_function(self): # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON log = logging.getLogger(__name__) @@ -93,7 +92,6 @@ def _has_required_moto(): return True -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_moto() is False, 'The moto module must be >= to {0} for ' @@ -104,7 +102,7 @@ class BotoElbTestCase(TestCase, LoaderModuleMockMixin): ''' def setup_loader_modules(self): - opts = salt.config.DEFAULT_MASTER_OPTS + opts = salt.config.DEFAULT_MASTER_OPTS.copy() utils = salt.loader.utils( opts, whitelist=['boto', 'args', 'systemd', 'path', 'platform']) diff --git a/tests/unit/modules/test_boto_iot.py b/tests/unit/modules/test_boto_iot.py index 4475a93d41ee..7b14bd57b0c8 100644 --- a/tests/unit/modules/test_boto_iot.py +++ b/tests/unit/modules/test_boto_iot.py @@ -11,8 +11,6 @@ from tests.support.unit import skipIf, TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -122,12 +120,11 @@ def _has_required_boto(): @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoIoTTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( opts, whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], @@ -635,7 +632,6 @@ def test_that_when_detach_principal_policy_version_fails_the_detach_principal_po ' module must be greater than or equal to' ' version {1}.' .format(required_boto3_version, required_botocore_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoIoTTopicRuleTestCase(BotoIoTTestCaseBase, BotoIoTTestCaseMixin): ''' TestCase for salt.modules.boto_iot module diff --git a/tests/unit/modules/test_boto_lambda.py b/tests/unit/modules/test_boto_lambda.py index aeb9ae09d41c..bb37484ade96 100644 --- a/tests/unit/modules/test_boto_lambda.py +++ b/tests/unit/modules/test_boto_lambda.py @@ -19,8 +19,6 @@ from tests.support.unit import skipIf, TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -117,12 +115,11 @@ def _has_required_boto(): ('The boto3 module must be greater than or equal to version {0}, ' 'and botocore must be greater than or equal to {1}'.format( required_boto3_version, required_botocore_version))) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoLambdaTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( opts, whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], @@ -468,7 +465,6 @@ def test_that_when_listing_function_versions_fails_the_list_function_versions_me @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoLambdaAliasTestCase(BotoLambdaTestCaseBase, BotoLambdaTestCaseMixin): ''' TestCase for salt.modules.boto_lambda module aliases @@ -619,7 +615,6 @@ def test_that_when_updating_an_alias_fails_the_update_alias_method_returns_error @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoLambdaEventSourceMappingTestCase(BotoLambdaTestCaseBase, BotoLambdaTestCaseMixin): ''' TestCase for salt.modules.boto_lambda module mappings diff --git a/tests/unit/modules/test_boto_route53.py b/tests/unit/modules/test_boto_route53.py index b36c4cb31666..a01e29c7f7ad 100644 --- a/tests/unit/modules/test_boto_route53.py +++ b/tests/unit/modules/test_boto_route53.py @@ -18,14 +18,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON -from tests.support.paths import TESTS_DIR +from tests.support.runtests import RUNTIME_VARS # import Python Third Party Libs # pylint: disable=import-error try: import boto - boto.ENDPOINTS_PATH = os.path.join(TESTS_DIR, 'unit/files/endpoints.json') + boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') from moto import mock_route53_deprecated HAS_MOTO = True except ImportError: @@ -66,7 +65,6 @@ def _has_required_moto(): return True -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_moto() is False, 'The moto module must be >= to {0} for ' 'PY2 or {1} for PY3.'.format(required_moto, required_moto_py3)) @@ -76,7 +74,7 @@ class BotoRoute53TestCase(TestCase, LoaderModuleMockMixin): TestCase for salt.modules.boto_route53 module ''' def setup_loader_modules(self): - self.opts = salt.config.DEFAULT_MINION_OPTS + self.opts = salt.config.DEFAULT_MINION_OPTS.copy() self.opts['route53.keyid'] = 'GKTADJGHEIQSXMKKRBJ08H' self.opts['route53.key'] = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' utils = salt.loader.utils(self.opts) diff --git a/tests/unit/modules/test_boto_s3_bucket.py b/tests/unit/modules/test_boto_s3_bucket.py index ab3d85c5471d..ab990a81c1d7 100644 --- a/tests/unit/modules/test_boto_s3_bucket.py +++ b/tests/unit/modules/test_boto_s3_bucket.py @@ -12,8 +12,6 @@ from tests.support.unit import skipIf, TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -200,12 +198,11 @@ def _has_required_boto(): @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoS3BucketTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( opts, whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], diff --git a/tests/unit/modules/test_boto_secgroup.py b/tests/unit/modules/test_boto_secgroup.py index c5722737ead5..76eb5aa0e967 100644 --- a/tests/unit/modules/test_boto_secgroup.py +++ b/tests/unit/modules/test_boto_secgroup.py @@ -11,8 +11,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON -from tests.support.paths import TESTS_DIR +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.config @@ -26,7 +25,7 @@ from salt.ext.six.moves import range # pylint: disable=redefined-builtin try: import boto - boto.ENDPOINTS_PATH = os.path.join(TESTS_DIR, 'unit/files/endpoints.json') + boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') import boto.ec2 # pylint: enable=unused-import HAS_BOTO = True except ImportError: @@ -83,7 +82,6 @@ def _has_required_boto(): return True -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -95,7 +93,7 @@ class BotoSecgroupTestCase(TestCase, LoaderModuleMockMixin): ''' def setup_loader_modules(self): - opts = salt.config.DEFAULT_MASTER_OPTS + opts = salt.config.DEFAULT_MASTER_OPTS.copy() utils = salt.loader.utils( opts, whitelist=['boto', 'args', 'systemd', 'path', 'platform']) diff --git a/tests/unit/modules/test_boto_vpc.py b/tests/unit/modules/test_boto_vpc.py index dab3e7eb5c83..2c147d029f0f 100644 --- a/tests/unit/modules/test_boto_vpc.py +++ b/tests/unit/modules/test_boto_vpc.py @@ -17,8 +17,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch -from tests.support.paths import TESTS_DIR +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.config @@ -35,7 +35,7 @@ # pylint: disable=no-name-in-module,unused-import try: import boto - boto.ENDPOINTS_PATH = os.path.join(TESTS_DIR, 'unit/files/endpoints.json') + boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') import boto3 from boto.exception import BotoServerError HAS_BOTO = True @@ -128,7 +128,6 @@ def _has_required_moto(): return True -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -140,7 +139,7 @@ class BotoVpcTestCaseBase(TestCase, LoaderModuleMockMixin): conn3 = None def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( opts, whitelist=['boto', 'boto3', 'args', 'systemd', 'path', 'platform']) @@ -586,7 +585,6 @@ def test_that_when_describing_vpc_but_providing_no_vpc_id_the_describe_method_ra boto_vpc.describe(vpc_id=None, **conn_parameters) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -890,7 +888,6 @@ def test_create_subnet_passes_availability_zone(self): self.assertEqual(describe_subnet_results['subnets'][0]['availability_zone'], 'us-east-1a') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -952,7 +949,6 @@ def test_that_when_creating_an_internet_gateway_with_vpc_id_specified_the_create self.assertTrue(igw_creation_result.get('created')) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -1002,7 +998,6 @@ def test_that_when_creating_an_nat_gateway_with_subnet_name_specified_the_create self.assertTrue(ngw_creation_result.get('created')) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -1040,7 +1035,6 @@ def test_that_when_a_subnet_does_not_exist_the_subnet_exists_method_returns_fals self.assertFalse(gw_exists_result['exists']) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -1209,7 +1203,6 @@ def test_that_when_checking_if_dhcp_options_exists_but_providing_no_filters_the_ boto_vpc.dhcp_options_exists(**conn_parameters) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -1572,7 +1565,6 @@ def test_that_when_disassociating_network_acl_for_a_non_existent_subnet_the_disa self.assertFalse(dhcp_disassociate_result) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -1792,7 +1784,6 @@ def test_that_when_replacing_a_route_with_a_non_existent_route_table_the_replace self.assertFalse(route_replacing_result) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' diff --git a/tests/unit/modules/test_bower.py b/tests/unit/modules/test_bower.py index 1daa650eedac..381a04a3d772 100644 --- a/tests/unit/modules/test_bower.py +++ b/tests/unit/modules/test_bower.py @@ -8,15 +8,14 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.modules.bower as bower from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class BowerTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.bower diff --git a/tests/unit/modules/test_bridge.py b/tests/unit/modules/test_bridge.py index 53ea4cd1c00f..fba8cc3785bb 100644 --- a/tests/unit/modules/test_bridge.py +++ b/tests/unit/modules/test_bridge.py @@ -8,14 +8,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.modules.bridge as bridge -@skipIf(NO_MOCK, NO_MOCK_REASON) class BridgeTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.bridge diff --git a/tests/unit/modules/test_btrfs.py b/tests/unit/modules/test_btrfs.py index ebd28a645184..0ff1a6975e19 100644 --- a/tests/unit/modules/test_btrfs.py +++ b/tests/unit/modules/test_btrfs.py @@ -7,13 +7,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( mock_open, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -23,7 +21,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class BtrfsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.btrfs diff --git a/tests/unit/modules/test_cassandra.py b/tests/unit/modules/test_cassandra.py index dfc6bee507ef..4f561682ef4a 100644 --- a/tests/unit/modules/test_cassandra.py +++ b/tests/unit/modules/test_cassandra.py @@ -7,12 +7,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -20,7 +18,6 @@ import salt.modules.cassandra as cassandra -@skipIf(NO_MOCK, NO_MOCK_REASON) class CassandraTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.cassandra diff --git a/tests/unit/modules/test_chef.py b/tests/unit/modules/test_chef.py index c85a7e91a241..6dff51134373 100644 --- a/tests/unit/modules/test_chef.py +++ b/tests/unit/modules/test_chef.py @@ -7,19 +7,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.chef as chef -@skipIf(NO_MOCK, NO_MOCK_REASON) class ChefTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.chef diff --git a/tests/unit/modules/test_chocolatey.py b/tests/unit/modules/test_chocolatey.py new file mode 100644 index 000000000000..e3f2987f0802 --- /dev/null +++ b/tests/unit/modules/test_chocolatey.py @@ -0,0 +1,154 @@ +# -*- coding: utf-8 -*- +''' +Test for the chocolatey module +''' + +# Import Python libs +from __future__ import absolute_import +import os + +# Import Salt Libs +import salt.modules.chocolatey as chocolatey +import salt.utils.platform +import salt.utils + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase, skipIf +from tests.support.mock import MagicMock, patch + + +@skipIf(not salt.utils.platform.is_windows(), 'Not a Windows system') +class ChocolateyTestCase(TestCase, LoaderModuleMockMixin): + ''' + Chocolatey private functions tests + ''' + + @classmethod + def setUpClass(cls): + cls.choco_path = 'C:\\path\\to\\chocolatey.exe' + cls.choco_path_pd = os.path.join( + os.environ.get('ProgramData'), 'Chocolatey', 'bin', 'chocolatey.exe') + cls.choco_path_sd = os.path.join( + os.environ.get('SystemDrive'), 'Chocolatey', 'bin', 'chocolatey.bat') + cls.mock_false = MagicMock(return_value=False) + cls.mock_true = MagicMock(return_value=True) + + @classmethod + def tearDownClass(cls): + del cls.choco_path + del cls.choco_path_pd + del cls.choco_path_sd + del cls.mock_false + del cls.mock_true + + def setup_loader_modules(self): + return {chocolatey: { + '__context__': {}, + '__salt__': {} + }} + + def test__clear_context(self): + ''' + Tests _clear_context function + ''' + context = {'chocolatey._yes': ['--yes'], + 'chocolatey._path': self.choco_path, + 'chocolatey._version': '0.9.9'} + with patch.dict(chocolatey.__context__, context): + chocolatey._clear_context() + # Did it clear all chocolatey items from __context__P? + self.assertEqual(chocolatey.__context__, {}) + + def test__yes_context(self): + ''' + Tests _yes function when it exists in __context__ + ''' + with patch.dict(chocolatey.__context__, {'chocolatey._yes': ['--yes']}): + result = chocolatey._yes() + expected = ['--yes'] + # Did it return correctly + self.assertListEqual(result, expected) + # Did it populate __context__ + self.assertEqual(chocolatey.__context__['chocolatey._yes'], + expected) + + def test__yes_version_greater(self): + ''' + Test _yes when Chocolatey version is greater than 0.9.9 + ''' + mock_version = MagicMock(return_value='10.0.0') + with patch('salt.modules.chocolatey.chocolatey_version', mock_version): + result = chocolatey._yes() + expected = ['--yes'] + # Did it return correctly + self.assertListEqual(result, expected) + # Did it populate __context__ + self.assertEqual(chocolatey.__context__['chocolatey._yes'], + expected) + + def test__yes_version_less_than(self): + ''' + Test _yes when Chocolatey version is less than 0.9.9 + ''' + mock_version = MagicMock(return_value='0.9.0') + with patch('salt.modules.chocolatey.chocolatey_version', mock_version): + result = chocolatey._yes() + expected = [] + # Did it return correctly + self.assertListEqual(result, expected) + # Did it populate __context__ + self.assertEqual(chocolatey.__context__['chocolatey._yes'], + expected) + + def test__find_chocolatey_context(self): + ''' + Test _find_chocolatey when it exists in __context__ + ''' + with patch.dict(chocolatey.__context__, + {'chocolatey._path': self.choco_path}): + result = chocolatey._find_chocolatey() + expected = self.choco_path + self.assertEqual(result, expected) + + def test__find_chocolatey_which(self): + ''' + Test _find_chocolatey when found with `cmd.which` + ''' + mock_which = MagicMock(return_value=self.choco_path) + with patch.dict(chocolatey.__salt__, {'cmd.which': mock_which}): + result = chocolatey._find_chocolatey() + expected = self.choco_path + # Does it return the correct path + self.assertEqual(result, expected) + # Does it populate __context__ + self.assertEqual(chocolatey.__context__['chocolatey._path'], + expected) + + def test__find_chocolatey_programdata(self): + ''' + Test _find_chocolatey when found in ProgramData + ''' + with patch.dict(chocolatey.__salt__, {'cmd.which': self.mock_false}),\ + patch('os.path.isfile', self.mock_true): + result = chocolatey._find_chocolatey() + expected = self.choco_path_pd + # Does it return the correct path + self.assertEqual(result, expected) + # Does it populate __context__ + self.assertEqual(chocolatey.__context__['chocolatey._path'], + expected) + + def test__find_chocolatey_systemdrive(self): + ''' + Test _find_chocolatey when found on SystemDrive (older versions) + ''' + with patch.dict(chocolatey.__salt__, {'cmd.which': self.mock_false}),\ + patch('os.path.isfile', MagicMock(side_effect=[False, True])): + result = chocolatey._find_chocolatey() + expected = self.choco_path_sd + # Does it return the correct path + self.assertEqual(result, expected) + # Does it populate __context__ + self.assertEqual(chocolatey.__context__['chocolatey._path'], + expected) diff --git a/tests/unit/modules/test_chroot.py b/tests/unit/modules/test_chroot.py new file mode 100644 index 000000000000..de3041e98fbd --- /dev/null +++ b/tests/unit/modules/test_chroot.py @@ -0,0 +1,277 @@ +# -*- coding: utf-8 -*- +# +# Author: Alberto Planas +# +# Copyright 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +''' +:maintainer: Alberto Planas +:platform: Linux +''' + +# Import Python Libs +from __future__ import absolute_import, print_function, unicode_literals +import sys + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import skipIf, TestCase +from tests.support.mock import MagicMock, patch + +from salt.exceptions import CommandExecutionError +import salt.utils.platform +import salt.modules.chroot as chroot + + +@skipIf(salt.utils.platform.is_windows(), 'This test cannot work on Windows') +class ChrootTestCase(TestCase, LoaderModuleMockMixin): + ''' + Test cases for salt.modules.chroot + ''' + + def setup_loader_modules(self): + return { + chroot: { + '__salt__': {}, + '__utils__': {}, + '__opts__': {'cachedir': ''}, + } + } + + @patch('os.path.isdir') + def test_exist(self, isdir): + ''' + Test if the chroot environment exist. + ''' + isdir.side_effect = (True, True, True, True) + self.assertTrue(chroot.exist('/chroot')) + + isdir.side_effect = (True, True, True, False) + self.assertFalse(chroot.exist('/chroot')) + + @patch('os.makedirs') + @patch('salt.modules.chroot.exist') + def test_create(self, exist, makedirs): + ''' + Test the creation of an empty chroot environment. + ''' + exist.return_value = True + self.assertTrue(chroot.create('/chroot')) + makedirs.assert_not_called() + + exist.return_value = False + self.assertTrue(chroot.create('/chroot')) + makedirs.assert_called() + + @patch('salt.modules.chroot.exist') + def test_call_fails_input_validation(self, exist): + ''' + Test execution of Salt functions in chroot. + ''' + # Basic input validation + exist.return_value = False + self.assertRaises(CommandExecutionError, chroot.call, '/chroot', '') + self.assertRaises(CommandExecutionError, chroot.call, '/chroot', + 'test.ping') + + @patch('salt.modules.chroot.exist') + @patch('tempfile.mkdtemp') + def test_call_fails_untar(self, mkdtemp, exist): + ''' + Test execution of Salt functions in chroot. + ''' + # Fail the tar command + exist.return_value = True + mkdtemp.return_value = '/chroot/tmp01' + utils_mock = { + 'thin.gen_thin': MagicMock(return_value='/salt-thin.tgz'), + 'files.rm_rf': MagicMock(), + } + salt_mock = { + 'cmd.run': MagicMock(return_value='Error'), + 'config.option': MagicMock(), + } + with patch.dict(chroot.__utils__, utils_mock), \ + patch.dict(chroot.__salt__, salt_mock): + self.assertEqual(chroot.call('/chroot', 'test.ping'), { + 'result': False, + 'comment': 'Error' + }) + utils_mock['thin.gen_thin'].assert_called_once() + salt_mock['config.option'].assert_called() + salt_mock['cmd.run'].assert_called_once() + utils_mock['files.rm_rf'].assert_called_once() + + @patch('salt.modules.chroot.exist') + @patch('tempfile.mkdtemp') + def test_call_fails_salt_thin(self, mkdtemp, exist): + ''' + Test execution of Salt functions in chroot. + ''' + # Fail the inner command + exist.return_value = True + mkdtemp.return_value = '/chroot/tmp01' + utils_mock = { + 'thin.gen_thin': MagicMock(return_value='/salt-thin.tgz'), + 'files.rm_rf': MagicMock(), + 'json.find_json': MagicMock(return_value={'return': {}}) + } + salt_mock = { + 'cmd.run': MagicMock(return_value=''), + 'config.option': MagicMock(), + 'cmd.run_chroot': MagicMock(return_value={ + 'retcode': 1, + 'stderr': 'Error', + }), + } + with patch.dict(chroot.__utils__, utils_mock), \ + patch.dict(chroot.__salt__, salt_mock): + self.assertEqual(chroot.call('/chroot', 'test.ping'), { + 'result': False, + 'comment': "Can't parse container command output" + }) + utils_mock['thin.gen_thin'].assert_called_once() + salt_mock['config.option'].assert_called() + salt_mock['cmd.run'].assert_called_once() + salt_mock['cmd.run_chroot'].assert_called_with( + '/chroot', + ['python{}'.format(sys.version_info[0]), '/tmp01/salt-call', + '--metadata', '--local', + '--log-file', '/tmp01/log', '--cachedir', '/tmp01/cache', + '--out', 'json', '-l', 'quiet', '--', 'test.ping']) + utils_mock['files.rm_rf'].assert_called_once() + + @patch('salt.modules.chroot.exist') + @patch('tempfile.mkdtemp') + def test_call_success(self, mkdtemp, exist): + ''' + Test execution of Salt functions in chroot. + ''' + # Success test + exist.return_value = True + mkdtemp.return_value = '/chroot/tmp01' + utils_mock = { + 'thin.gen_thin': MagicMock(return_value='/salt-thin.tgz'), + 'files.rm_rf': MagicMock(), + 'json.find_json': MagicMock(return_value={'return': 'result'}) + } + salt_mock = { + 'cmd.run': MagicMock(return_value=''), + 'config.option': MagicMock(), + 'cmd.run_chroot': MagicMock(return_value={ + 'retcode': 0, + 'stdout': '', + }), + } + with patch.dict(chroot.__utils__, utils_mock), \ + patch.dict(chroot.__salt__, salt_mock): + self.assertEqual(chroot.call('/chroot', 'test.ping'), 'result') + utils_mock['thin.gen_thin'].assert_called_once() + salt_mock['config.option'].assert_called() + salt_mock['cmd.run'].assert_called_once() + salt_mock['cmd.run_chroot'].assert_called_with( + '/chroot', + ['python{}'.format(sys.version_info[0]), '/tmp01/salt-call', + '--metadata', '--local', + '--log-file', '/tmp01/log', '--cachedir', '/tmp01/cache', + '--out', 'json', '-l', 'quiet', '--', 'test.ping']) + utils_mock['files.rm_rf'].assert_called_once() + + @patch('salt.modules.chroot.exist') + @patch('tempfile.mkdtemp') + def test_call_success_parameters(self, mkdtemp, exist): + ''' + Test execution of Salt functions in chroot with parameters. + ''' + # Success test + exist.return_value = True + mkdtemp.return_value = '/chroot/tmp01' + utils_mock = { + 'thin.gen_thin': MagicMock(return_value='/salt-thin.tgz'), + 'files.rm_rf': MagicMock(), + 'json.find_json': MagicMock(return_value={'return': 'result'}) + } + salt_mock = { + 'cmd.run': MagicMock(return_value=''), + 'config.option': MagicMock(), + 'cmd.run_chroot': MagicMock(return_value={ + 'retcode': 0, + 'stdout': '', + }), + } + with patch.dict(chroot.__utils__, utils_mock), \ + patch.dict(chroot.__salt__, salt_mock): + self.assertEqual(chroot.call('/chroot', 'module.function', + key='value'), 'result') + utils_mock['thin.gen_thin'].assert_called_once() + salt_mock['config.option'].assert_called() + salt_mock['cmd.run'].assert_called_once() + salt_mock['cmd.run_chroot'].assert_called_with( + '/chroot', + ['python{}'.format(sys.version_info[0]), '/tmp01/salt-call', + '--metadata', '--local', + '--log-file', '/tmp01/log', '--cachedir', '/tmp01/cache', + '--out', 'json', '-l', 'quiet', + '--', 'module.function', 'key=value']) + utils_mock['files.rm_rf'].assert_called_once() + + @patch('salt.modules.chroot._create_and_execute_salt_state') + @patch('salt.client.ssh.state.SSHHighState') + @patch('salt.fileclient.get_file_client') + @patch('salt.utils.state.get_sls_opts') + def test_sls(self, get_sls_opts, get_file_client, SSHHighState, + _create_and_execute_salt_state): + ''' + Test execution of Salt states in chroot. + ''' + SSHHighState.return_value = SSHHighState + SSHHighState.render_highstate.return_value = (None, []) + SSHHighState.state.reconcile_extend.return_value = (None, []) + SSHHighState.state.requisite_in.return_value = (None, []) + SSHHighState.state.verify_high.return_value = [] + + _create_and_execute_salt_state.return_value = 'result' + opts_mock = { + 'hash_type': 'md5', + } + get_sls_opts.return_value = opts_mock + with patch.dict(chroot.__opts__, opts_mock): + self.assertEqual(chroot.sls('/chroot', 'module'), 'result') + _create_and_execute_salt_state.assert_called_once() + + @patch('salt.modules.chroot._create_and_execute_salt_state') + @patch('salt.client.ssh.state.SSHHighState') + @patch('salt.fileclient.get_file_client') + @patch('salt.utils.state.get_sls_opts') + def test_highstate(self, get_sls_opts, get_file_client, SSHHighState, + _create_and_execute_salt_state): + ''' + Test execution of Salt states in chroot. + ''' + SSHHighState.return_value = SSHHighState + + _create_and_execute_salt_state.return_value = 'result' + opts_mock = { + 'hash_type': 'md5', + } + get_sls_opts.return_value = opts_mock + with patch.dict(chroot.__opts__, opts_mock): + self.assertEqual(chroot.highstate('/chroot'), 'result') + _create_and_execute_salt_state.assert_called_once() diff --git a/tests/unit/modules/test_cmdmod.py b/tests/unit/modules/test_cmdmod.py index 8da672dd229c..af3aebe302e9 100644 --- a/tests/unit/modules/test_cmdmod.py +++ b/tests/unit/modules/test_cmdmod.py @@ -20,13 +20,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.paths import FILES +from tests.support.runtests import RUNTIME_VARS from tests.support.mock import ( mock_open, Mock, MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -69,7 +67,6 @@ def stderr(self): return self._stderr -@skipIf(NO_MOCK, NO_MOCK_REASON) class CMDMODTestCase(TestCase, LoaderModuleMockMixin): ''' Unit tests for the salt.modules.cmdmod module @@ -232,23 +229,29 @@ def test_run_no_vt_os_error(self): ''' Tests error raised when not useing vt and OSError is provided ''' + expected_error = "expect error" with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): with patch('salt.utils.platform.is_windows', MagicMock(return_value=False)): with patch('os.path.isfile', MagicMock(return_value=True)): with patch('os.access', MagicMock(return_value=True)): - with patch('salt.utils.timed_subprocess.TimedProc', MagicMock(side_effect=OSError)): - self.assertRaises(CommandExecutionError, cmdmod._run, 'foo') + with patch('salt.utils.timed_subprocess.TimedProc', MagicMock(side_effect=OSError(expected_error))): + with self.assertRaises(CommandExecutionError) as error: + cmdmod.run('foo') + assert error.exception.args[0].endswith(expected_error), repr(error.exception.args[0]) def test_run_no_vt_io_error(self): ''' Tests error raised when not useing vt and IOError is provided ''' + expected_error = "expect error" with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): with patch('salt.utils.platform.is_windows', MagicMock(return_value=False)): with patch('os.path.isfile', MagicMock(return_value=True)): with patch('os.access', MagicMock(return_value=True)): - with patch('salt.utils.timed_subprocess.TimedProc', MagicMock(side_effect=IOError)): - self.assertRaises(CommandExecutionError, cmdmod._run, 'foo') + with patch('salt.utils.timed_subprocess.TimedProc', MagicMock(side_effect=IOError(expected_error))): + with self.assertRaises(CommandExecutionError) as error: + cmdmod.run('foo') + assert error.exception.args[0].endswith(expected_error), repr(error.exception.args[0]) @skipIf(salt.utils.platform.is_windows(), 'Do not run on Windows') @skipIf(True, 'Test breaks unittests runs') @@ -351,7 +354,7 @@ def test_run_all_binary_replace(self): /dev/stdout. ''' # Since we're using unicode_literals, read the random bytes from a file - rand_bytes_file = os.path.join(FILES, 'file', 'base', 'random_bytes') + rand_bytes_file = os.path.join(RUNTIME_VARS.BASE_FILES, 'random_bytes') with salt.utils.files.fopen(rand_bytes_file, 'rb') as fp_: stdout_bytes = fp_.read() @@ -433,3 +436,33 @@ def test_run_all_output_encoding(self): ret = cmdmod.run_all('some command', output_encoding='latin1') self.assertEqual(ret['stdout'], stdout) + + def test_run_chroot_mount(self): + ''' + Test cmdmod.run_chroot mount / umount balance + ''' + mock_mount = MagicMock() + mock_umount = MagicMock() + mock_run_all = MagicMock() + with patch.dict(cmdmod.__salt__, { + 'mount.mount': mock_mount, + 'mount.umount': mock_umount}): + with patch('salt.modules.cmdmod.run_all', mock_run_all): + cmdmod.run_chroot('/mnt', 'cmd') + self.assertEqual(mock_mount.call_count, 3) + self.assertEqual(mock_umount.call_count, 3) + + def test_run_chroot_mount_bind(self): + ''' + Test cmdmod.run_chroot mount / umount balance with bind mount + ''' + mock_mount = MagicMock() + mock_umount = MagicMock() + mock_run_all = MagicMock() + with patch.dict(cmdmod.__salt__, { + 'mount.mount': mock_mount, + 'mount.umount': mock_umount}): + with patch('salt.modules.cmdmod.run_all', mock_run_all): + cmdmod.run_chroot('/mnt', 'cmd', binds=['/var']) + self.assertEqual(mock_mount.call_count, 4) + self.assertEqual(mock_umount.call_count, 4) diff --git a/tests/unit/modules/test_composer.py b/tests/unit/modules/test_composer.py index 774ff604b3ae..2663e3a92e5b 100644 --- a/tests/unit/modules/test_composer.py +++ b/tests/unit/modules/test_composer.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ from salt.exceptions import CommandExecutionError, CommandNotFoundError, SaltInvocationError -@skipIf(NO_MOCK, NO_MOCK_REASON) class ComposerTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.composer diff --git a/tests/unit/modules/test_config.py b/tests/unit/modules/test_config.py index b87e987a4908..088e3c0675a7 100644 --- a/tests/unit/modules/test_config.py +++ b/tests/unit/modules/test_config.py @@ -1,66 +1,172 @@ # -*- coding: utf-8 -*- - # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import fnmatch # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch +from tests.support.unit import TestCase +from tests.support.mock import patch # Import Salt libs import salt.modules.config as config DEFAULTS = { - 'test.option.all': 'value of test.option.all in DEFAULTS', - 'test.option': 'value of test.option in DEFAULTS' + 'test.option.foo': 'value of test.option.foo in DEFAULTS', + 'test.option.bar': 'value of test.option.bar in DEFAULTS', + 'test.option.baz': 'value of test.option.baz in DEFAULTS', + 'test.option': 'value of test.option in DEFAULTS', } -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestModulesConfig(TestCase, LoaderModuleMockMixin): + no_match = 'test.option.nope' + opt_name = 'test.option.foo' + wildcard_opt_name = 'test.option.b*' + def setup_loader_modules(self): return { config: { '__opts__': { - 'test.option.all': 'value of test.option.all in __opts__' + 'test.option.foo': 'value of test.option.foo in __opts__', + 'test.option.bar': 'value of test.option.bar in __opts__', + 'test.option.baz': 'value of test.option.baz in __opts__', }, '__pillar__': { - 'test.option.all': 'value of test.option.all in __pillar__', + 'test.option.foo': 'value of test.option.foo in __pillar__', + 'test.option.bar': 'value of test.option.bar in __pillar__', + 'test.option.baz': 'value of test.option.baz in __pillar__', 'master': { - 'test.option.all': 'value of test.option.all in master' + 'test.option.foo': 'value of test.option.foo in master', + 'test.option.bar': 'value of test.option.bar in master', + 'test.option.baz': 'value of test.option.baz in master', } + }, + '__grains__': { + 'test.option.foo': 'value of test.option.foo in __grains__', + 'test.option.bar': 'value of test.option.bar in __grains__', + 'test.option.baz': 'value of test.option.baz in __grains__', } } } + def _wildcard_match(self, data): + return {x: data[x] for x in fnmatch.filter(data, self.wildcard_opt_name)} + def test_defaults_only_name(self): with patch.dict(config.DEFAULTS, DEFAULTS): opt_name = 'test.option' opt = config.option(opt_name) self.assertEqual(opt, config.DEFAULTS[opt_name]) + def test_no_match(self): + ''' + Make sure that the defa + ''' + with patch.dict(config.DEFAULTS, DEFAULTS): + ret = config.option(self.no_match) + assert ret == '', ret + + default = 'wat' + ret = config.option(self.no_match, default=default) + assert ret == default, ret + + ret = config.option(self.no_match, wildcard=True) + assert ret == {}, ret + + default = {'foo': 'bar'} + ret = config.option(self.no_match, default=default, wildcard=True) + assert ret == default, ret + + # Should be no match since wildcard=False + ret = config.option(self.wildcard_opt_name) + assert ret == '', ret + def test_omits(self): with patch.dict(config.DEFAULTS, DEFAULTS): - opt_name = 'test.option.all' - opt = config.option(opt_name, - omit_opts=False, - omit_master=True, - omit_pillar=True) - self.assertEqual(opt, config.__opts__[opt_name]) + # ********** OMIT NOTHING ********** + + # Match should be in __opts__ dict + ret = config.option(self.opt_name) + assert ret == config.__opts__[self.opt_name], ret + + # Wildcard match + ret = config.option(self.wildcard_opt_name, wildcard=True) + assert ret == self._wildcard_match(config.__opts__), ret + + # ********** OMIT __opts__ ********** + + # Match should be in __grains__ dict + ret = config.option(self.opt_name, + omit_opts=True) + assert ret == config.__grains__[self.opt_name], ret - opt = config.option(opt_name, + # Wildcard match + ret = config.option(self.wildcard_opt_name, omit_opts=True, - omit_master=True, - omit_pillar=False) + wildcard=True) + assert ret == self._wildcard_match(config.__grains__), ret + + # ********** OMIT __opts__, __grains__ ********** + + # Match should be in __pillar__ dict + ret = config.option(self.opt_name, + omit_opts=True, + omit_grains=True) + assert ret == config.__pillar__[self.opt_name], ret - self.assertEqual(opt, config.__pillar__[opt_name]) - opt = config.option(opt_name, + # Wildcard match + ret = config.option(self.wildcard_opt_name, omit_opts=True, - omit_master=False, + omit_grains=True, + wildcard=True) + assert ret == self._wildcard_match(config.__pillar__), ret + + # ********** OMIT __opts__, __grains__, __pillar__ ********** + + # Match should be in master opts + ret = config.option(self.opt_name, + omit_opts=True, + omit_grains=True, omit_pillar=True) + assert ret == config.__pillar__['master'][self.opt_name], ret + + # Wildcard match + ret = config.option(self.wildcard_opt_name, + omit_opts=True, + omit_grains=True, + omit_pillar=True, + wildcard=True) + assert ret == self._wildcard_match(config.__pillar__['master']), ret + + # ********** OMIT ALL THE THINGS ********** + + # Match should be in master opts + ret = config.option(self.opt_name, + omit_opts=True, + omit_grains=True, + omit_pillar=True, + omit_master=True) + assert ret == config.DEFAULTS[self.opt_name], ret + + # Wildcard match + ret = config.option(self.wildcard_opt_name, + omit_opts=True, + omit_grains=True, + omit_pillar=True, + omit_master=True, + wildcard=True) + assert ret == self._wildcard_match(config.DEFAULTS), ret + + # Match should be in master opts + ret = config.option(self.opt_name, + omit_all=True) + assert ret == config.DEFAULTS[self.opt_name], ret - self.assertEqual( - opt, config.__pillar__['master'][opt_name]) + # Wildcard match + ret = config.option(self.wildcard_opt_name, + omit_all=True, + wildcard=True) + assert ret == self._wildcard_match(config.DEFAULTS), ret diff --git a/tests/unit/modules/test_cp.py b/tests/unit/modules/test_cp.py index a49257ad4209..738ea9b6d457 100644 --- a/tests/unit/modules/test_cp.py +++ b/tests/unit/modules/test_cp.py @@ -8,14 +8,12 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( Mock, MagicMock, mock_open, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -27,7 +25,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class CpTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.cp module @@ -143,14 +140,14 @@ def test_push(self): _auth=MagicMock(**{'return_value.gen_token.return_value': 'token'}), __opts__={'id': 'abc', 'file_buffer_size': 10}), \ patch('salt.utils.files.fopen', mock_open(read_data=b'content')) as m_open, \ - patch('salt.transport.client.ReqChannel.factory', MagicMock()): + patch('salt.transport.client.ReqChannel.factory', MagicMock()) as req_channel_factory_mock: response = cp.push(filename) assert response, response num_opens = len(m_open.filehandles[filename]) assert num_opens == 1, num_opens fh_ = m_open.filehandles[filename][0] assert fh_.read.call_count == 2, fh_.read.call_count - salt.transport.client.ReqChannel.factory({}).send.assert_called_once_with( + req_channel_factory_mock().__enter__().send.assert_called_once_with( dict( loc=fh_.tell(), # pylint: disable=resource-leakage cmd='_file_recv', diff --git a/tests/unit/modules/test_cpan.py b/tests/unit/modules/test_cpan.py index 3841e156844a..648a7fb1d342 100644 --- a/tests/unit/modules/test_cpan.py +++ b/tests/unit/modules/test_cpan.py @@ -7,18 +7,15 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.cpan as cpan -@skipIf(NO_MOCK, NO_MOCK_REASON) class CpanTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.cpan diff --git a/tests/unit/modules/test_cron.py b/tests/unit/modules/test_cron.py index 92d1ff335986..90cd0312990b 100644 --- a/tests/unit/modules/test_cron.py +++ b/tests/unit/modules/test_cron.py @@ -8,8 +8,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch, call +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch, call # Import Salt libs import salt.modules.cron as cron @@ -60,7 +60,6 @@ def write_crontab(*args, **kw): return MagicMock() -@skipIf(NO_MOCK, NO_MOCK_REASON) class CronTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -635,7 +634,7 @@ def test__load_tab(self): def test_write_cron_file_root_rh(self): ''' Assert that write_cron_file() is called with the correct cron command and user: RedHat - - If instance running uid matches crontab user uid, runas STUB_USER without -u flag. + - If instance running uid matches crontab user uid, run without -u flag. ''' with patch.dict(cron.__grains__, {'os_family': 'RedHat'}), \ patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}), \ @@ -643,20 +642,20 @@ def test_write_cron_file_root_rh(self): new=MagicMock(return_value=True)): cron.write_cron_file(STUB_USER, STUB_PATH) cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", - runas=STUB_USER, python_shell=False) def test_write_cron_file_foo_rh(self): ''' Assert that write_cron_file() is called with the correct cron command and user: RedHat - - If instance running with uid that doesn't match crontab user uid, run with -u flag + - If instance running with uid that doesn't match crontab user uid, runas foo ''' with patch.dict(cron.__grains__, {'os_family': 'RedHat'}), \ patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}), \ patch('salt.modules.cron._check_instance_uid_match', MagicMock(return_value=False)): cron.write_cron_file('foo', STUB_PATH) - cron.__salt__['cmd.retcode'].assert_called_with("crontab -u foo /tmp", + cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", + runas='foo', python_shell=False) def test_write_cron_file_root_sol(self): @@ -664,10 +663,8 @@ def test_write_cron_file_root_sol(self): Assert that write_cron_file() is called with the correct cron command and user: Solaris - Solaris should always run without a -u flag ''' - with patch.dict(cron.__grains__, {'os_family': 'RedHat'}), \ - patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=True)): + with patch.dict(cron.__grains__, {'os_family': 'Solaris'}), \ + patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}): cron.write_cron_file(STUB_USER, STUB_PATH) cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", runas=STUB_USER, @@ -679,9 +676,7 @@ def test_write_cron_file_foo_sol(self): - Solaris should always run without a -u flag ''' with patch.dict(cron.__grains__, {'os_family': 'Solaris'}), \ - patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=False)): + patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}): cron.write_cron_file('foo', STUB_PATH) cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", runas='foo', @@ -693,9 +688,7 @@ def test_write_cron_file_root_aix(self): - AIX should always run without a -u flag ''' with patch.dict(cron.__grains__, {'os_family': 'AIX'}), \ - patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=True)): + patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}): cron.write_cron_file(STUB_USER, STUB_PATH) cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", runas=STUB_USER, @@ -707,9 +700,7 @@ def test_write_cron_file_foo_aix(self): - AIX should always run without a -u flag ''' with patch.dict(cron.__grains__, {'os_family': 'AIX'}), \ - patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=False)): + patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}): cron.write_cron_file('foo', STUB_PATH) cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", runas='foo', @@ -718,7 +709,7 @@ def test_write_cron_file_foo_aix(self): def test_write_cr_file_v_root_rh(self): ''' Assert that write_cron_file_verbose() is called with the correct cron command and user: RedHat - - If instance running uid matches crontab user uid, runas STUB_USER without -u flag. + - If instance running uid matches crontab user uid, run without -u flag. ''' with patch.dict(cron.__grains__, {'os_family': 'Redhat'}), \ patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}), \ @@ -726,20 +717,20 @@ def test_write_cr_file_v_root_rh(self): MagicMock(return_value=True)): cron.write_cron_file_verbose(STUB_USER, STUB_PATH) cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", - runas=STUB_USER, python_shell=False) def test_write_cr_file_v_foo_rh(self): ''' Assert that write_cron_file_verbose() is called with the correct cron command and user: RedHat - - If instance running with uid that doesn't match crontab user uid, run with -u flag + - If instance running with uid that doesn't match crontab user uid, runas 'foo' ''' with patch.dict(cron.__grains__, {'os_family': 'Redhat'}), \ patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}), \ patch('salt.modules.cron._check_instance_uid_match', MagicMock(return_value=False)): cron.write_cron_file_verbose('foo', STUB_PATH) - cron.__salt__['cmd.run_all'].assert_called_with("crontab -u foo /tmp", + cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", + runas='foo', python_shell=False) def test_write_cr_file_v_root_sol(self): @@ -748,9 +739,7 @@ def test_write_cr_file_v_root_sol(self): - Solaris should always run without a -u flag ''' with patch.dict(cron.__grains__, {'os_family': 'Solaris'}), \ - patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=True)): + patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}): cron.write_cron_file_verbose(STUB_USER, STUB_PATH) cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", runas=STUB_USER, @@ -762,9 +751,7 @@ def test_write_cr_file_v_foo_sol(self): - Solaris should always run without a -u flag ''' with patch.dict(cron.__grains__, {'os_family': 'Solaris'}), \ - patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=False)): + patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}): cron.write_cron_file_verbose('foo', STUB_PATH) cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", runas='foo', @@ -776,9 +763,7 @@ def test_write_cr_file_v_root_aix(self): - AIX should always run without a -u flag ''' with patch.dict(cron.__grains__, {'os_family': 'AIX'}), \ - patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=True)): + patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}): cron.write_cron_file_verbose(STUB_USER, STUB_PATH) cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", runas=STUB_USER, @@ -790,9 +775,7 @@ def test_write_cr_file_v_foo_aix(self): - AIX should always run without a -u flag ''' with patch.dict(cron.__grains__, {'os_family': 'AIX'}), \ - patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=False)): + patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}): cron.write_cron_file_verbose('foo', STUB_PATH) cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", runas='foo', @@ -809,7 +792,6 @@ def test_raw_cron_root_redhat(self): MagicMock(return_value=True)): 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) @@ -824,7 +806,8 @@ def test_raw_cron_foo_redhat(self): patch('salt.modules.cron._check_instance_uid_match', MagicMock(return_value=False)): cron.raw_cron(STUB_USER) - cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -u root -l", + cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l", + runas=STUB_USER, ignore_retcode=True, rstrip=False, python_shell=False) @@ -894,7 +877,6 @@ def test_raw_cron_foo_aix(self): python_shell=False) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PsTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_cyg.py b/tests/unit/modules/test_cyg.py index 7d2f3c273d17..6bc1252b6964 100644 --- a/tests/unit/modules/test_cyg.py +++ b/tests/unit/modules/test_cyg.py @@ -5,7 +5,7 @@ # # Import Salt Testing libs # from tests.support.unit import skipIf, TestCase -# from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +# from tests.support.mock import MagicMock, patch # # Import salt libs # import salt.modules.cyg as cyg @@ -13,7 +13,6 @@ # cyg.__salt__ = {} -# @skipIf(NO_MOCK, NO_MOCK_REASON) # class TestcygModule(TestCase): # def test__get_cyg_dir(self): diff --git a/tests/unit/modules/test_daemontools.py b/tests/unit/modules/test_daemontools.py index 65728b1ec1b3..0e5fa558ec74 100644 --- a/tests/unit/modules/test_daemontools.py +++ b/tests/unit/modules/test_daemontools.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) import os @@ -22,7 +20,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class DaemontoolsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.daemontools diff --git a/tests/unit/modules/test_data.py b/tests/unit/modules/test_data.py index 6d5307cb87a3..2a6e52ae19e3 100644 --- a/tests/unit/modules/test_data.py +++ b/tests/unit/modules/test_data.py @@ -7,20 +7,17 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, mock_open, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.data as data -@skipIf(NO_MOCK, NO_MOCK_REASON) class DataTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.data diff --git a/tests/unit/modules/test_ddns.py b/tests/unit/modules/test_ddns.py index 74e6e12067fa..191c53cfd584 100644 --- a/tests/unit/modules/test_ddns.py +++ b/tests/unit/modules/test_ddns.py @@ -20,8 +20,6 @@ mock_open, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -30,7 +28,6 @@ @skipIf(HAS_DNS is False, 'dnspython libs not installed') -@skipIf(NO_MOCK, NO_MOCK_REASON) class DDNSTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for the salt.modules.ddns module diff --git a/tests/unit/modules/test_deb_apache.py b/tests/unit/modules/test_deb_apache.py index ab0fca3c0bc3..11a6ac61f82e 100644 --- a/tests/unit/modules/test_deb_apache.py +++ b/tests/unit/modules/test_deb_apache.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) class DebApacheTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.deb_apache diff --git a/tests/unit/modules/test_deb_postgres.py b/tests/unit/modules/test_deb_postgres.py index 6bcd6c27b519..8439b87c5b0d 100644 --- a/tests/unit/modules/test_deb_postgres.py +++ b/tests/unit/modules/test_deb_postgres.py @@ -5,8 +5,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, Mock, patch +from tests.support.unit import TestCase +from tests.support.mock import Mock, patch # Import salt libs from salt.ext import six @@ -20,7 +20,6 @@ ''' -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresClusterTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -65,7 +64,6 @@ def test_cluster_create(self): # datadir='/opt/postgresql')) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresLsClusterTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -121,7 +119,6 @@ def test_cluster_exists(self): self.assertFalse(deb_postgres.cluster_exists('3.4', 'main')) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresDeleteClusterTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_debconfmod.py b/tests/unit/modules/test_debconfmod.py index d230578718ae..c0a89d62597c 100644 --- a/tests/unit/modules/test_debconfmod.py +++ b/tests/unit/modules/test_debconfmod.py @@ -7,12 +7,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -20,7 +18,6 @@ import os -@skipIf(NO_MOCK, NO_MOCK_REASON) class DebconfmodTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.DebconfmodTestCase diff --git a/tests/unit/modules/test_debian_ip.py b/tests/unit/modules/test_debian_ip.py index f21ca4101995..74b4e54b684a 100644 --- a/tests/unit/modules/test_debian_ip.py +++ b/tests/unit/modules/test_debian_ip.py @@ -13,8 +13,6 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -25,7 +23,6 @@ import jinja2.exceptions -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(salt.utils.platform.is_windows(), 'Do not run these tests on Windows') class DebianIpTestCase(TestCase, LoaderModuleMockMixin): ''' @@ -52,6 +49,18 @@ def test_build_bond(self): 'pkg.install': mock}): self.assertEqual(debian_ip.build_bond('bond0'), '') + def test_error_message_iface_should_process_non_str_expected(self): + values = [1, True, False, 'no-kaboom'] + iface = 'ethtest' + option = 'test' + msg = debian_ip._error_msg_iface(iface, option, values) + self.assertTrue(msg.endswith('[1|True|False|no-kaboom]'), msg) + + def test_error_message_network_should_process_non_str_expected(self): + values = [1, True, False, 'no-kaboom'] + msg = debian_ip._error_msg_network('fnord', values) + self.assertTrue(msg.endswith('[1|True|False|no-kaboom]'), msg) + def test_build_bond_exception(self): ''' Test if it create a bond script in /etc/modprobe.d with the passed diff --git a/tests/unit/modules/test_debian_service.py b/tests/unit/modules/test_debian_service.py index bdfa580160ff..3ef7a3d35c28 100644 --- a/tests/unit/modules/test_debian_service.py +++ b/tests/unit/modules/test_debian_service.py @@ -7,19 +7,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.debian_service as debian_service -@skipIf(NO_MOCK, NO_MOCK_REASON) class DebianServicesTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.debian_service diff --git a/tests/unit/modules/test_defaults.py b/tests/unit/modules/test_defaults.py index 50707ca592c9..6a65435c5256 100644 --- a/tests/unit/modules/test_defaults.py +++ b/tests/unit/modules/test_defaults.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.defaults as defaults -@skipIf(NO_MOCK, NO_MOCK_REASON) class DefaultsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.defaults diff --git a/tests/unit/modules/test_devmap.py b/tests/unit/modules/test_devmap.py index 9b348ec58cc6..4a48f00f2766 100644 --- a/tests/unit/modules/test_devmap.py +++ b/tests/unit/modules/test_devmap.py @@ -9,19 +9,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.devmap as devmap -@skipIf(NO_MOCK, NO_MOCK_REASON) class DevMapTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.devmap diff --git a/tests/unit/modules/test_dig.py b/tests/unit/modules/test_dig.py index 10331fcf49db..501862d5a35c 100644 --- a/tests/unit/modules/test_dig.py +++ b/tests/unit/modules/test_dig.py @@ -9,7 +9,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.mock import MagicMock, patch # Import salt libs import salt.modules.dig as dig @@ -50,7 +50,6 @@ def _spf_side_effect(key, python_shell=False): 'stdout': ''}) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(dig.__virtual__() is False, 'Dig must be installed') class DigTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/modules/test_djangomod.py b/tests/unit/modules/test_djangomod.py index 52489c96b264..3dccff910785 100644 --- a/tests/unit/modules/test_djangomod.py +++ b/tests/unit/modules/test_djangomod.py @@ -7,19 +7,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.djangomod as djangomod -@skipIf(NO_MOCK, NO_MOCK_REASON) class DjangomodTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.djangomod @@ -86,7 +83,6 @@ def test_collectstatic(self): self.assertTrue(djangomod.collectstatic('DJANGO_SETTINGS_MODULE')) -@skipIf(NO_MOCK, NO_MOCK_REASON) class DjangomodCliCommandTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.djangomod diff --git a/tests/unit/modules/test_dnsmasq.py b/tests/unit/modules/test_dnsmasq.py index 55080c68f3b7..375d9a39150f 100644 --- a/tests/unit/modules/test_dnsmasq.py +++ b/tests/unit/modules/test_dnsmasq.py @@ -9,13 +9,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( mock_open, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -23,7 +21,6 @@ import salt.modules.dnsmasq as dnsmasq -@skipIf(NO_MOCK, NO_MOCK_REASON) class DnsmasqTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for the salt.modules.at module diff --git a/tests/unit/modules/test_dnsutil.py b/tests/unit/modules/test_dnsutil.py index 0111c2d2199f..7ba6730bc525 100644 --- a/tests/unit/modules/test_dnsutil.py +++ b/tests/unit/modules/test_dnsutil.py @@ -13,8 +13,6 @@ MagicMock, patch, mock_open, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -49,27 +47,25 @@ 'NS land-5.com.\n\n' '1 PTR localhost.') -if NO_MOCK is False: - mock_writes_list = salt.utils.data.decode([ - '##\n', - '# Host Database\n', - '#\n', - '# localhost is used to configure the loopback interface\n', - '# when the system is booting. Do not change this entry.\n', - '##\n', - '127.0.0.1 localhost', - '\n', - '255.255.255.255 broadcasthost', - '\n', - '::1 localhost', - '\n', - 'fe80::1%lo0 localhost', - '\n' +mock_writes_list = salt.utils.data.decode([ + '##\n', + '# Host Database\n', + '#\n', + '# localhost is used to configure the loopback interface\n', + '# when the system is booting. Do not change this entry.\n', + '##\n', + '127.0.0.1 localhost', + '\n', + '255.255.255.255 broadcasthost', + '\n', + '::1 localhost', + '\n', + 'fe80::1%lo0 localhost', + '\n' ], to_str=True ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class DNSUtilTestCase(TestCase): def test_parse_hosts(self): with patch('salt.utils.files.fopen', mock_open(read_data=mock_hosts_file)): diff --git a/tests/unit/modules/test_dockermod.py b/tests/unit/modules/test_dockermod.py index 168ab3125cff..a33a818871b3 100644 --- a/tests/unit/modules/test_dockermod.py +++ b/tests/unit/modules/test_dockermod.py @@ -12,8 +12,6 @@ from tests.support.mock import ( MagicMock, Mock, - NO_MOCK, - NO_MOCK_REASON, patch, call ) @@ -37,7 +35,6 @@ def _docker_py_version(): return (0,) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(docker_mod.HAS_DOCKER_PY is False, 'docker-py must be installed to run these tests. Skipping.') class DockerTestCase(TestCase, LoaderModuleMockMixin): ''' @@ -45,7 +42,7 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin): ''' def setup_loader_modules(self): utils = salt.loader.utils( - salt.config.DEFAULT_MINION_OPTS, + salt.config.DEFAULT_MINION_OPTS.copy(), whitelist=['args', 'docker', 'json', 'state', 'thin', 'systemd', 'path', 'platform'] ) @@ -78,7 +75,12 @@ def test_failed_login(self): with patch.dict(docker_mod.__pillar__, {'docker-registries': {'portus.example.com:5000': {'username': 'admin', 'password': 'linux12345', 'email': 'tux@example.com'}}}): with patch.object(docker_mod, '_get_client', get_client_mock): - with patch.dict(docker_mod.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}): + dunder_salt = { + 'cmd.run_all': MagicMock(return_value=ref_out), + 'config.get': MagicMock(return_value={}), + 'config.option': MagicMock(return_value={}), + } + with patch.dict(docker_mod.__salt__, dunder_salt): ret = docker_mod.login('portus.example.com:5000') self.assertIn('retcode', ret) self.assertNotEqual(ret['retcode'], 0) @@ -168,13 +170,20 @@ def test_check_mine_cache_is_refreshed_on_container_change_event(self): {'mine.send': mine_send, 'container_resource.run': MagicMock(), 'config.get': MagicMock(return_value=True), + 'config.option': MagicMock(return_value=True), 'cp.cache_file': MagicMock(return_value=False)}): with patch.dict(docker_mod.__utils__, {'docker.get_client_args': client_args_mock}): with patch.object(docker_mod, '_get_client', client): command('container', *args) - mine_send.assert_called_with('docker.ps', verbose=True, all=True, - host=True) + try: + mine_send.assert_called_with( + 'docker.ps', verbose=True, all=True, host=True) + except AssertionError as exc: + raise Exception( + 'command \'{0}\' did not call docker.ps with expected ' + 'arguments: {1}'.format(command_name, exc) + ) def test_update_mine(self): ''' @@ -199,6 +208,7 @@ def config_get_enabled(val, default): mine_mock = Mock() dunder_salt = { 'config.get': MagicMock(side_effect=config_get_disabled), + 'config.option': MagicMock(return_value=False), 'mine.send': mine_mock, } with patch.dict(docker_mod.__salt__, dunder_salt), \ @@ -211,6 +221,7 @@ def config_get_enabled(val, default): 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 + dunder_salt['config.option'].return_value = True docker_mod.stop('foo', timeout=1) self.assert_called_once(mine_mock) diff --git a/tests/unit/modules/test_dpkg_lowpkg.py b/tests/unit/modules/test_dpkg_lowpkg.py index bdcb7eec894d..449aa659afcb 100644 --- a/tests/unit/modules/test_dpkg_lowpkg.py +++ b/tests/unit/modules/test_dpkg_lowpkg.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.dpkg_lowpkg as dpkg -@skipIf(NO_MOCK, NO_MOCK_REASON) class DpkgTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.dpkg diff --git a/tests/unit/modules/test_drac.py b/tests/unit/modules/test_drac.py index 650efe7832c9..a1858ac57b40 100644 --- a/tests/unit/modules/test_drac.py +++ b/tests/unit/modules/test_drac.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.drac as drac -@skipIf(NO_MOCK, NO_MOCK_REASON) class DracTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.drac diff --git a/tests/unit/modules/test_drbd.py b/tests/unit/modules/test_drbd.py index 8df23540ea06..e81323d4cf8e 100644 --- a/tests/unit/modules/test_drbd.py +++ b/tests/unit/modules/test_drbd.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.drbd as drbd -@skipIf(NO_MOCK, NO_MOCK_REASON) class DrbdTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.drbd diff --git a/tests/unit/modules/test_elasticsearch.py b/tests/unit/modules/test_elasticsearch.py index 3483c71974c7..eb2eee5c4784 100644 --- a/tests/unit/modules/test_elasticsearch.py +++ b/tests/unit/modules/test_elasticsearch.py @@ -11,13 +11,11 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs from salt.modules import elasticsearch -from salt.exceptions import CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import elasticsearch exceptions NO_ELASTIC = False @@ -27,7 +25,6 @@ NO_ELASTIC = True -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(NO_ELASTIC, 'Install elasticsearch-py before running Elasticsearch unit tests.') class ElasticsearchTestCase(TestCase): ''' @@ -2264,3 +2261,111 @@ def delete_template(self, id=None): with patch.object(elasticsearch, '_get_instance', MagicMock(return_value=MockElastic())): self.assertRaises(CommandExecutionError, elasticsearch.search_template_delete, "foo") + + # Cluster settings tests below. + # We're assuming that _get_instance is properly tested + # These tests are very simple in nature, mostly checking default arguments. + def test_cluster_get_settings_succeess(self): + ''' + Test if cluster get_settings fetch succeeds + ''' + + expected_settings = {"transient": {}, "persistent": {}} + fake_es = MagicMock() + fake_es.cluster = MagicMock() + fake_es.cluster.get_settings = MagicMock(return_value=expected_settings) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + actual_settings = elasticsearch.cluster_get_settings() + fake_es.cluster.get_settings.assert_called_with(flat_settings=False, include_defaults=False) + assert actual_settings == expected_settings + + def test_cluster_get_settings_failure(self): + ''' + Test if cluster get_settings fetch fails with CommandExecutionError + ''' + + fake_es = MagicMock() + fake_es.cluster = MagicMock() + fake_es.cluster.get_settings = MagicMock() + fake_es.cluster.get_settings.side_effect = TransportError("custom error", 123) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + self.assertRaises(CommandExecutionError, elasticsearch.cluster_get_settings) + + def test_cluster_put_settings_succeess(self): + ''' + Test if cluster put_settings succeeds + ''' + + expected_settings = {"acknowledged": True, + "transient": {}, + "persistent": {"indices": {"recovery": {"max_bytes_per_sec": "50mb"}}} + } + body = {"transient": {}, "persistent": {"indices.recovery.max_bytes_per_sec": "50mb"}} + fake_es = MagicMock() + fake_es.cluster = MagicMock() + fake_es.cluster.put_settings = MagicMock(return_value=expected_settings) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + actual_settings = elasticsearch.cluster_put_settings(body=body) + fake_es.cluster.put_settings.assert_called_with(body=body, flat_settings=False) + assert actual_settings == expected_settings + + def test_cluster_put_settings_failure(self): + ''' + Test if cluster put_settings fails with CommandExecutionError + ''' + + body = {"transient": {}, "persistent": {"indices.recovery.max_bytes_per_sec": "50mb"}} + fake_es = MagicMock() + fake_es.cluster = MagicMock() + fake_es.cluster.put_settings = MagicMock() + fake_es.cluster.put_settings.side_effect = TransportError("custom error", 123) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + self.assertRaises(CommandExecutionError, elasticsearch.cluster_put_settings, body=body) + + def test_cluster_put_settings_nobody(self): + ''' + Test if cluster put_settings fails with SaltInvocationError + ''' + + self.assertRaises(SaltInvocationError, elasticsearch.cluster_put_settings) + + # flush_synced tests below. + # We're assuming that _get_instance is properly tested + # These tests are very simple in nature, mostly checking default arguments. + def test_flush_synced_succeess(self): + ''' + Test if flush_synced succeeds + ''' + + expected_return = {'_shards': {'failed': 0, 'successful': 0, 'total': 0}} + fake_es = MagicMock() + fake_es.indices = MagicMock() + fake_es.indices.flush_synced = MagicMock(return_value=expected_return) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + output = elasticsearch.flush_synced(index='_all', ignore_unavailable=True, allow_no_indices=True, expand_wildcards='all') + fake_es.indices.flush_synced.assert_called_with({'index': '_all', 'ignore_unavailable': True, 'allow_no_indices': True, 'expand_wildcards': 'all'}) + assert output == expected_return + + def test_flush_synced_failure(self): + ''' + Test if flush_synced fails with CommandExecutionError + ''' + + fake_es = MagicMock() + fake_es.indices = MagicMock() + fake_es.indices.flush_synced = MagicMock() + fake_es.indices.flush_synced.side_effect = TransportError("custom error", 123) + fake_instance = MagicMock(return_value=fake_es) + + with patch.object(elasticsearch, '_get_instance', fake_instance): + self.assertRaises(CommandExecutionError, elasticsearch.flush_synced) diff --git a/tests/unit/modules/test_environ.py b/tests/unit/modules/test_environ.py index be62347cbdac..3bfe1c60df11 100644 --- a/tests/unit/modules/test_environ.py +++ b/tests/unit/modules/test_environ.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.environ as environ -@skipIf(NO_MOCK, NO_MOCK_REASON) class EnvironTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.environ diff --git a/tests/unit/modules/test_esxcluster.py b/tests/unit/modules/test_esxcluster.py index e478b4a9cbc0..39c0117ec919 100644 --- a/tests/unit/modules/test_esxcluster.py +++ b/tests/unit/modules/test_esxcluster.py @@ -13,16 +13,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class GetDetailsTestCase(TestCase, LoaderModuleMockMixin): '''Tests for salt.modules.esxcluster.get_details''' def setup_loader_modules(self): diff --git a/tests/unit/modules/test_esxdatacenter.py b/tests/unit/modules/test_esxdatacenter.py index 3c64653e057c..4a5de7bbb2aa 100644 --- a/tests/unit/modules/test_esxdatacenter.py +++ b/tests/unit/modules/test_esxdatacenter.py @@ -13,16 +13,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class GetDetailsTestCase(TestCase, LoaderModuleMockMixin): '''Tests for salt.modules.esxdatacenter.get_details''' def setup_loader_modules(self): diff --git a/tests/unit/modules/test_etcd_mod.py b/tests/unit/modules/test_etcd_mod.py index 2758e923a11e..6f1bb139825e 100644 --- a/tests/unit/modules/test_etcd_mod.py +++ b/tests/unit/modules/test_etcd_mod.py @@ -8,13 +8,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( create_autospec, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -22,7 +20,6 @@ import salt.utils.etcd_util as etcd_util -@skipIf(NO_MOCK, NO_MOCK_REASON) class EtcdModTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.etcd_mod diff --git a/tests/unit/modules/test_event.py b/tests/unit/modules/test_event.py index e9140670cb30..157c662a74f6 100644 --- a/tests/unit/modules/test_event.py +++ b/tests/unit/modules/test_event.py @@ -6,14 +6,12 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ import salt.utils.event -@skipIf(NO_MOCK, NO_MOCK_REASON) class EventTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.event @@ -31,7 +28,7 @@ def setup_loader_modules(self): event: { '__opts__': { 'id': 'id', - 'sock_dir': TMP, + 'sock_dir': RUNTIME_VARS.TMP, 'transport': 'zeromq' } } diff --git a/tests/unit/modules/test_extfs.py b/tests/unit/modules/test_extfs.py index 09d1e88377a9..98e2f132c7cb 100644 --- a/tests/unit/modules/test_extfs.py +++ b/tests/unit/modules/test_extfs.py @@ -8,14 +8,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.modules.extfs as extfs -@skipIf(NO_MOCK, NO_MOCK_REASON) class ExtfsTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.extfs diff --git a/tests/unit/modules/test_file.py b/tests/unit/modules/test_file.py index 3cb1e901c0f8..b9a78a243852 100644 --- a/tests/unit/modules/test_file.py +++ b/tests/unit/modules/test_file.py @@ -8,9 +8,9 @@ import textwrap # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.helpers import with_tempfile from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP from tests.support.unit import TestCase, skipIf from tests.support.mock import MagicMock, Mock, patch, mock_open, DEFAULT @@ -1308,9 +1308,9 @@ def test_line_modecheck_failure(self): :return: ''' for mode, err_msg in [(None, 'How to process the file'), ('nonsense', 'Unknown mode')]: - with pytest.raises(CommandExecutionError) as cmd_err: + with pytest.raises(CommandExecutionError) as exc_info: filemod.line('foo', mode=mode) - self.assertIn(err_msg, six.text_type(cmd_err.value)) + self.assertIn(err_msg, six.text_type(exc_info.value)) @patch('os.path.realpath', MagicMock(wraps=lambda x: x)) @patch('os.path.isfile', MagicMock(return_value=True)) @@ -1320,10 +1320,10 @@ def test_line_no_content(self): :return: ''' for mode in ['insert', 'ensure', 'replace']: - with pytest.raises(CommandExecutionError) as cmd_err: + with pytest.raises(CommandExecutionError) as exc_info: filemod.line('foo', mode=mode) self.assertIn('Content can only be empty if mode is "delete"', - six.text_type(cmd_err.value)) + six.text_type(exc_info.value)) @patch('os.path.realpath', MagicMock(wraps=lambda x: x)) @patch('os.path.isfile', MagicMock(return_value=True)) @@ -1335,10 +1335,10 @@ def test_line_insert_no_location_no_before_no_after(self): ''' files_fopen = mock_open(read_data='test data') with patch('salt.utils.files.fopen', files_fopen): - with pytest.raises(CommandExecutionError) as cmd_err: + with pytest.raises(CommandExecutionError) as exc_info: filemod.line('foo', content='test content', mode='insert') self.assertIn('"location" or "before/after"', - six.text_type(cmd_err.value)) + six.text_type(exc_info.value)) def test_util_starts_till(self): ''' @@ -1944,11 +1944,11 @@ def test_line_insert_ensure_beforeafter_rangelines(self): with patch('salt.utils.files.fopen', files_fopen): atomic_opener = mock_open() with patch('salt.utils.atomicfile.atomic_open', atomic_opener): - with pytest.raises(CommandExecutionError) as cmd_err: + with pytest.raises(CommandExecutionError) as exc_info: filemod.line('foo', content=cfg_content, after=_after, before=_before, mode='ensure') self.assertIn( 'Found more than one line between boundaries "before" and "after"', - six.text_type(cmd_err.value)) + six.text_type(exc_info.value)) @with_tempfile() def test_line_delete(self, name): @@ -2061,7 +2061,7 @@ def setUp(self): self.tfile.close() self.addCleanup(os.remove, self.tfile.name) self.addCleanup(delattr, self, 'tfile') - self.myfile = os.path.join(TMP, 'myfile') + self.myfile = os.path.join(RUNTIME_VARS.TMP, 'myfile') with salt.utils.files.fopen(self.myfile, 'w+') as fp: fp.write(salt.utils.stringutils.to_str('Hello\n')) self.addCleanup(os.remove, self.myfile) @@ -2074,6 +2074,30 @@ def test_symlink_already_in_desired_state(self): result = filemod.symlink(self.tfile.name, self.directory + '/a_link') self.assertTrue(result) + @skipIf(salt.utils.platform.is_windows(), 'os.link is not available on Windows') + def test_hardlink_sanity(self): + target = os.path.join(self.directory, 'a_hardlink') + self.addCleanup(os.remove, target) + result = filemod.link(self.tfile.name, target) + self.assertTrue(result) + + @skipIf(salt.utils.platform.is_windows(), 'os.link is not available on Windows') + def test_hardlink_numlinks(self): + target = os.path.join(self.directory, 'a_hardlink') + self.addCleanup(os.remove, target) + result = filemod.link(self.tfile.name, target) + name_i = os.stat(self.tfile.name).st_nlink + self.assertTrue(name_i > 1) + + @skipIf(salt.utils.platform.is_windows(), 'os.link is not available on Windows') + def test_hardlink_working(self): + target = os.path.join(self.directory, 'a_hardlink') + self.addCleanup(os.remove, target) + result = filemod.link(self.tfile.name, target) + name_i = os.stat(self.tfile.name).st_ino + target_i = os.stat(target).st_ino + self.assertTrue(name_i == target_i) + def test_source_list_for_list_returns_file_from_dict_via_http(self): with patch('salt.modules.file.os.remove') as remove: remove.return_value = None diff --git a/tests/unit/modules/test_firewalld.py b/tests/unit/modules/test_firewalld.py index 1d61122f4747..0a8a9623d97a 100644 --- a/tests/unit/modules/test_firewalld.py +++ b/tests/unit/modules/test_firewalld.py @@ -7,19 +7,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.firewalld as firewalld -@skipIf(NO_MOCK, NO_MOCK_REASON) class FirewalldTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.firewalld diff --git a/tests/unit/modules/test_gem.py b/tests/unit/modules/test_gem.py index 4012cc1c4069..0d4d8f9e6542 100644 --- a/tests/unit/modules/test_gem.py +++ b/tests/unit/modules/test_gem.py @@ -5,14 +5,13 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.modules.gem as gem -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestGemModule(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_genesis.py b/tests/unit/modules/test_genesis.py index 3bf079430f27..305ec097d04f 100644 --- a/tests/unit/modules/test_genesis.py +++ b/tests/unit/modules/test_genesis.py @@ -10,19 +10,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.genesis as genesis -@skipIf(NO_MOCK, NO_MOCK_REASON) class GenesisTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.genesis diff --git a/tests/unit/modules/test_gentoo_service.py b/tests/unit/modules/test_gentoo_service.py index d88a02bad52f..c986a7482eeb 100644 --- a/tests/unit/modules/test_gentoo_service.py +++ b/tests/unit/modules/test_gentoo_service.py @@ -5,12 +5,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, call ) @@ -18,7 +16,6 @@ import salt.modules.gentoo_service as gentoo_service -@skipIf(NO_MOCK, NO_MOCK_REASON) class GentooServicesTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.gentoo_service diff --git a/tests/unit/modules/test_git.py b/tests/unit/modules/test_git.py index a70714df57d8..d17df59482e6 100644 --- a/tests/unit/modules/test_git.py +++ b/tests/unit/modules/test_git.py @@ -12,13 +12,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( Mock, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -72,7 +70,6 @@ def _git_version(): return LooseVersion(git_version.split()[-1]) -@skipIf(NO_MOCK, NO_MOCK_REASON) class GitTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.git diff --git a/tests/unit/modules/test_glusterfs.py b/tests/unit/modules/test_glusterfs.py index 832ce603b53d..eed03b4971b0 100644 --- a/tests/unit/modules/test_glusterfs.py +++ b/tests/unit/modules/test_glusterfs.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -434,7 +432,6 @@ class peer_probe(object): """ -@skipIf(NO_MOCK, NO_MOCK_REASON) class GlusterfsTestCase(TestCase, LoaderModuleMockMixin): ''' diff --git a/tests/unit/modules/test_gnomedesktop.py b/tests/unit/modules/test_gnomedesktop.py index 70385ad95718..90ee6ec5be3b 100644 --- a/tests/unit/modules/test_gnomedesktop.py +++ b/tests/unit/modules/test_gnomedesktop.py @@ -7,18 +7,15 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.gnomedesktop as gnomedesktop -@skipIf(NO_MOCK, NO_MOCK_REASON) class GnomedesktopTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.gnomedesktop diff --git a/tests/unit/modules/test_google_chat.py b/tests/unit/modules/test_google_chat.py index 09b844598e5b..a3f88c471d28 100644 --- a/tests/unit/modules/test_google_chat.py +++ b/tests/unit/modules/test_google_chat.py @@ -6,8 +6,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch +from tests.support.unit import TestCase +from tests.support.mock import patch # Import Salt Libs import salt.modules.google_chat as gchat @@ -29,7 +29,6 @@ def mocked_http_query_failure(url, method, **kwargs): # pylint: disable=unused- 'dict': None} -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestModulesCfutils(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.google_chat diff --git a/tests/unit/modules/test_gpg.py b/tests/unit/modules/test_gpg.py index d7445114616e..dddc6e213c13 100644 --- a/tests/unit/modules/test_gpg.py +++ b/tests/unit/modules/test_gpg.py @@ -11,20 +11,14 @@ import datetime import time -# Import Salt Testing libs +# Import Salt Testing Libs from tests.support.helpers import destructiveTest from tests.support.unit import TestCase, skipIf -from tests.support.paths import TMP from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import ( - MagicMock, - patch, - NO_MOCK, - NO_MOCK_REASON -) +from tests.support.runtests import RUNTIME_VARS +from tests.support.mock import MagicMock, patch # Import Salt libs -import salt.utils.path import salt.utils.platform import salt.utils.files import salt.modules.gpg as gpg @@ -168,7 +162,6 @@ @destructiveTest -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not salt.utils.platform.is_linux(), 'These tests can only be run on linux') class GpgTestCase(TestCase, LoaderModuleMockMixin): ''' @@ -180,14 +173,14 @@ def setup_loader_modules(self): @skipIf(not HAS_GPG, 'GPG Module Unavailable') def setUp(self): super(GpgTestCase, self).setUp() - self.gpghome = os.path.join(TMP, 'gpghome') + self.gpghome = os.path.join(RUNTIME_VARS.TMP, 'gpghome') if not os.path.isdir(self.gpghome): # left behind... Don't fail because of this! os.makedirs(self.gpghome) - self.gpgfile_pub = os.path.join(TMP, 'gpgfile.pub') + self.gpgfile_pub = os.path.join(RUNTIME_VARS.TMP, 'gpgfile.pub') with salt.utils.files.fopen(self.gpgfile_pub, 'wb') as fp: fp.write(salt.utils.stringutils.to_bytes(GPG_TEST_PUB_KEY)) - self.gpgfile_priv = os.path.join(TMP, 'gpgfile.priv') + self.gpgfile_priv = os.path.join(RUNTIME_VARS.TMP, 'gpgfile.priv') with salt.utils.files.fopen(self.gpgfile_priv, 'wb') as fp: fp.write(salt.utils.stringutils.to_bytes(GPG_TEST_PRIV_KEY)) self.user = 'salt' @@ -300,6 +293,7 @@ def test_get_key(self): ret = gpg.get_key('xxxxxxxxxxxxxxxx') self.assertEqual(ret, _expected_result) + @destructiveTest # Need to run as root!? @skipIf(not HAS_GPG, 'GPG Module Unavailable') def test_delete_key(self): ''' diff --git a/tests/unit/modules/test_grains.py b/tests/unit/modules/test_grains.py index 96d44199f1d9..0b6d264147ca 100644 --- a/tests/unit/modules/test_grains.py +++ b/tests/unit/modules/test_grains.py @@ -6,14 +6,12 @@ import copy # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt libs @@ -25,12 +23,11 @@ from salt.utils.odict import OrderedDict -@skipIf(NO_MOCK, NO_MOCK_REASON) class GrainsModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - conf_file = os.path.join(TMP, '__salt_test_grains') - cachedir = os.path.join(TMP, '__salt_test_grains_cache_dir') + conf_file = os.path.join(RUNTIME_VARS.TMP, '__salt_test_grains') + cachedir = os.path.join(RUNTIME_VARS.TMP, '__salt_test_grains_cache_dir') if not os.path.isdir(cachedir): os.makedirs(cachedir) return { diff --git a/tests/unit/modules/test_groupadd.py b/tests/unit/modules/test_groupadd.py index 8e0e64749ad4..a8ee5ebd6e15 100644 --- a/tests/unit/modules/test_groupadd.py +++ b/tests/unit/modules/test_groupadd.py @@ -13,14 +13,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.modules.groupadd as groupadd import salt.utils.platform -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(salt.utils.platform.is_windows(), "Module not available on Windows") class GroupAddTestCase(TestCase, LoaderModuleMockMixin): ''' @@ -84,21 +83,19 @@ def test_chgid_gid_same(self): ''' Tests if the group id is the same as argument ''' - mock_pre_gid = MagicMock(return_value=10) - with patch.dict(groupadd.__salt__, - {'file.group_to_gid': mock_pre_gid}): + mock = MagicMock(return_value={'gid': 10}) + with patch.object(groupadd, 'info', mock): self.assertTrue(groupadd.chgid('test', 10)) def test_chgid(self): ''' Tests the gid for a named group was changed ''' - mock_pre_gid = MagicMock(return_value=0) - mock_cmdrun = MagicMock(return_value=0) - with patch.dict(groupadd.__salt__, - {'file.group_to_gid': mock_pre_gid}): - with patch.dict(groupadd.__salt__, {'cmd.run': mock_cmdrun}): - self.assertFalse(groupadd.chgid('test', 500)) + mock = MagicMock(return_value=None) + with patch.dict(groupadd.__salt__, {'cmd.run': mock}): + mock = MagicMock(side_effect=[{'gid': 10}, {'gid': 500}]) + with patch.object(groupadd, 'info', mock): + self.assertTrue(groupadd.chgid('test', 500)) # 'delete' function tests: 1 diff --git a/tests/unit/modules/test_grub_legacy.py b/tests/unit/modules/test_grub_legacy.py index ed4cfc425586..26365ee241bd 100644 --- a/tests/unit/modules/test_grub_legacy.py +++ b/tests/unit/modules/test_grub_legacy.py @@ -9,13 +9,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( mock_open, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -24,7 +22,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class GrublegacyTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.grub_legacy diff --git a/tests/unit/modules/test_guestfs.py b/tests/unit/modules/test_guestfs.py index 064b9e6cc37e..c568e6d6f45a 100644 --- a/tests/unit/modules/test_guestfs.py +++ b/tests/unit/modules/test_guestfs.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.guestfs as guestfs -@skipIf(NO_MOCK, NO_MOCK_REASON) class GuestfsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.guestfs diff --git a/tests/unit/modules/test_hadoop.py b/tests/unit/modules/test_hadoop.py index 423a85ceecad..9f693d93614c 100644 --- a/tests/unit/modules/test_hadoop.py +++ b/tests/unit/modules/test_hadoop.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.hadoop as hadoop -@skipIf(NO_MOCK, NO_MOCK_REASON) class HadoopTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.hadoop diff --git a/tests/unit/modules/test_haproxyconn.py b/tests/unit/modules/test_haproxyconn.py index 92fe1b1cd9ac..678fc5f971b8 100644 --- a/tests/unit/modules/test_haproxyconn.py +++ b/tests/unit/modules/test_haproxyconn.py @@ -8,8 +8,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase # Import Salt Libs import salt.modules.haproxyconn as haproxyconn @@ -100,7 +99,6 @@ def sendCmd(self, ha_cmd, objectify=False): return ha_cmd -@skipIf(NO_MOCK, NO_MOCK_REASON) class HaproxyConnTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.haproxyconn diff --git a/tests/unit/modules/test_hashutil.py b/tests/unit/modules/test_hashutil.py index d021ed12da41..17955cd0d8c3 100644 --- a/tests/unit/modules/test_hashutil.py +++ b/tests/unit/modules/test_hashutil.py @@ -20,6 +20,7 @@ class HashutilTestCase(ModuleCase): the_string_sha256 = 'd49859ccbc854fa68d800b5734efc70d72383e6479d545468bc300263164ff33' the_string_sha512 = 'a8c174a7941c64a068e686812a2fafd7624c840fde800f5965fbeca675f2f6e37061ffe41e17728c919bdea290eab7a21e13c04ae71661955a87f2e0e04bb045' the_string_hmac = 'eBWf9bstXg+NiP5AOwppB5HMvZiYMPzEM9W5YMm/AmQ=' + the_string_hmac_compute = '78159ff5bb2d5e0f8d88fe403b0a690791ccbd989830fcc433d5b960c9bf0264' the_string_github = 'sha1=b06aa56bdf4935eec82c4e53e83ed03f03fdb32d' def setUp(self): @@ -53,6 +54,10 @@ def test_hmac_signature(self): self.the_string_hmac) self.assertTrue(ret) + def test_hmac_compute(self): + ret = self.hashutil['hashutil.hmac_compute'](self.the_string, 'shared secret') + self.assertEqual(ret, self.the_string_hmac_compute) + def test_github_signature(self): ret = self.hashutil['hashutil.github_signature']( self.the_string, diff --git a/tests/unit/modules/test_hg.py b/tests/unit/modules/test_hg.py index 6843f4d30610..d62090daaa81 100644 --- a/tests/unit/modules/test_hg.py +++ b/tests/unit/modules/test_hg.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.hg as hg -@skipIf(NO_MOCK, NO_MOCK_REASON) class HgTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.hg diff --git a/tests/unit/modules/test_hipchat.py b/tests/unit/modules/test_hipchat.py deleted file mode 100644 index b31be8c93398..000000000000 --- a/tests/unit/modules/test_hipchat.py +++ /dev/null @@ -1,93 +0,0 @@ -# -*- coding: utf-8 -*- -''' - :codeauthor: Jayesh Kariya -''' - -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, - NO_MOCK, - NO_MOCK_REASON -) - -# Import Salt Libs -import salt.modules.hipchat as hipchat - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -class HipchatTestCase(TestCase, LoaderModuleMockMixin): - ''' - Test cases for salt.modules.hipchat - ''' - def setup_loader_modules(self): - return {hipchat: {}} - - # 'list_rooms' function tests: 1 - - def test_list_rooms(self): - ''' - Test if it list all HipChat rooms. - ''' - with patch('salt.modules.hipchat._query', MagicMock(return_value=True)): - self.assertEqual(hipchat.list_rooms(), True) - - # 'list_users' function tests: 1 - - def test_list_users(self): - ''' - Test if it list all HipChat users. - ''' - with patch('salt.modules.hipchat._query', MagicMock(return_value=True)): - self.assertEqual(hipchat.list_users(), True) - - # 'find_room' function tests: 1 - - def test_find_room(self): - ''' - Test if it find a room by name and return it. - ''' - mock = MagicMock(return_value=[{'name': 'Development Room'}]) - with patch.object(hipchat, 'list_rooms', mock): - self.assertEqual(hipchat.find_room('Development Room'), - {'name': 'Development Room'}) - - self.assertEqual(hipchat.find_room('QA Room'), False) - - # 'find_user' function tests: 1 - - def test_find_user(self): - ''' - Test if it find a user by name and return it. - ''' - mock = MagicMock(return_value=[{'name': 'Thomas Hatch'}]) - with patch.object(hipchat, 'list_rooms', mock): - self.assertEqual(hipchat.find_room('Thomas Hatch'), - {'name': 'Thomas Hatch'}) - - self.assertEqual(hipchat.find_user('Salt QA'), False) - - # 'send_message' function tests: 1 - - def test_send_message(self): - ''' - Test if it send a message to a HipChat room. - ''' - with patch('salt.modules.hipchat._query', MagicMock(return_value=True)): - self.assertEqual(hipchat.send_message('Development Room', - 'Build is done', - 'Build Server'), True) - - def test_send_message_false(self): - ''' - Test if it send a message to a HipChat room. - ''' - with patch('salt.modules.hipchat._query', MagicMock(return_value=False)): - self.assertEqual(hipchat.send_message('Development Room', - 'Build is done', - 'Build Server'), False) diff --git a/tests/unit/modules/test_http.py b/tests/unit/modules/test_http.py index 088382dd420e..f4c66b8a3944 100644 --- a/tests/unit/modules/test_http.py +++ b/tests/unit/modules/test_http.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -20,7 +18,6 @@ import salt.utils.http -@skipIf(NO_MOCK, NO_MOCK_REASON) class HttpTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.http diff --git a/tests/unit/modules/test_ilo.py b/tests/unit/modules/test_ilo.py index c289b584ed61..7e28815b6f7e 100644 --- a/tests/unit/modules/test_ilo.py +++ b/tests/unit/modules/test_ilo.py @@ -6,27 +6,45 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import logging +import tempfile + # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.ilo as ilo +import salt.modules.file + +log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) class IloTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.ilo ''' def setup_loader_modules(self): - return {ilo: {}} + return {ilo: + {'__opts__': {'cachedir': tempfile.gettempdir()}, + '__salt__': {'file.remove': salt.modules.file.remove}} + } + + # '__execute_cmd' function tests: 1 + + def test_execute_cmd(self): + ''' + Test if __execute_command opens the temporary file + properly when calling global_settings. + ''' + mock_cmd_run = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + with patch.dict(ilo.__salt__, {'cmd.run_all': mock_cmd_run}): + ret = ilo.global_settings() + self.assertEqual(ret, True) # 'global_settings' function tests: 1 diff --git a/tests/unit/modules/test_incron.py b/tests/unit/modules/test_incron.py index 09890c6c025d..76aa4aa463c3 100644 --- a/tests/unit/modules/test_incron.py +++ b/tests/unit/modules/test_incron.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.incron as incron -@skipIf(NO_MOCK, NO_MOCK_REASON) class IncronTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.incron diff --git a/tests/unit/modules/test_influxdb08mod.py b/tests/unit/modules/test_influxdb08mod.py index 359a1fdaf859..39aae8758f3b 100644 --- a/tests/unit/modules/test_influxdb08mod.py +++ b/tests/unit/modules/test_influxdb08mod.py @@ -7,12 +7,10 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -57,7 +55,6 @@ def query(self, query, time_precision, chunked): return query, time_precision, chunked -@skipIf(NO_MOCK, NO_MOCK_REASON) class InfluxTestCase(TestCase): ''' TestCase for the salt.modules.at module diff --git a/tests/unit/modules/test_introspect.py b/tests/unit/modules/test_introspect.py index 30df0bd9ada6..1c79f6a0ac4c 100644 --- a/tests/unit/modules/test_introspect.py +++ b/tests/unit/modules/test_introspect.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.introspect as introspect -@skipIf(NO_MOCK, NO_MOCK_REASON) class IntrospectTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.introspect diff --git a/tests/unit/modules/test_iosconfig.py b/tests/unit/modules/test_iosconfig.py index 879d531be5a7..90b528e5e094 100644 --- a/tests/unit/modules/test_iosconfig.py +++ b/tests/unit/modules/test_iosconfig.py @@ -9,15 +9,13 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase # Import Salt modules from salt.utils.odict import OrderedDict import salt.modules.iosconfig as iosconfig -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestModulesIOSConfig(TestCase, LoaderModuleMockMixin): running_config = textwrap.dedent('''\ diff --git a/tests/unit/modules/test_ipset.py b/tests/unit/modules/test_ipset.py index b20bae2ef90a..d43cbc98c6a3 100644 --- a/tests/unit/modules/test_ipset.py +++ b/tests/unit/modules/test_ipset.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.ipset as ipset -@skipIf(NO_MOCK, NO_MOCK_REASON) class IpsetTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.aptpkg diff --git a/tests/unit/modules/test_iptables.py b/tests/unit/modules/test_iptables.py index 810221902588..870bfb92912d 100644 --- a/tests/unit/modules/test_iptables.py +++ b/tests/unit/modules/test_iptables.py @@ -9,19 +9,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.iptables as iptables -@skipIf(NO_MOCK, NO_MOCK_REASON) class IptablesTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.iptables diff --git a/tests/unit/modules/test_jboss7.py b/tests/unit/modules/test_jboss7.py index 87383a0d54aa..8e7943599332 100644 --- a/tests/unit/modules/test_jboss7.py +++ b/tests/unit/modules/test_jboss7.py @@ -5,15 +5,14 @@ # Import salt testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import MagicMock # Import salt libs from salt.utils.odict import OrderedDict import salt.modules.jboss7 as jboss7 -@skipIf(NO_MOCK, NO_MOCK_REASON) class JBoss7TestCase(TestCase, LoaderModuleMockMixin): jboss_config = {} diff --git a/tests/unit/modules/test_k8s.py b/tests/unit/modules/test_k8s.py index 2f0136b46ede..c2cf96b7a5d8 100644 --- a/tests/unit/modules/test_k8s.py +++ b/tests/unit/modules/test_k8s.py @@ -47,7 +47,7 @@ def test_get_one_namespace(self): def test_create_namespace(self): hash = hashlib.sha1() - hash.update(six.text_Type(time.time())) + hash.update(six.text_type(time.time())) nsname = hash.hexdigest()[:16] res = k8s.create_namespace(nsname, apiserver_url="http://127.0.0.1:8080") proc = Popen(["kubectl", "get", "namespaces", nsname, "-o", "json"], stdout=PIPE) diff --git a/tests/unit/modules/test_kernelpkg_linux_apt.py b/tests/unit/modules/test_kernelpkg_linux_apt.py index 2a1cc0c364a7..6eb5e1540887 100644 --- a/tests/unit/modules/test_kernelpkg_linux_apt.py +++ b/tests/unit/modules/test_kernelpkg_linux_apt.py @@ -15,7 +15,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf - from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON + from tests.support.mock import MagicMock, patch # Import Salt Libs from tests.support.kernelpkg import KernelPkgTestCase @@ -26,7 +26,6 @@ HAS_MODULES = False -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_MODULES, 'Salt modules could not be loaded') class AptKernelPkgTestCase(KernelPkgTestCase, TestCase, LoaderModuleMockMixin): ''' diff --git a/tests/unit/modules/test_kernelpkg_linux_yum.py b/tests/unit/modules/test_kernelpkg_linux_yum.py index 68bc0b6a9701..5d55fafd4797 100644 --- a/tests/unit/modules/test_kernelpkg_linux_yum.py +++ b/tests/unit/modules/test_kernelpkg_linux_yum.py @@ -14,7 +14,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf - from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON + from tests.support.mock import MagicMock, patch # Import Salt Libs from tests.support.kernelpkg import KernelPkgTestCase @@ -26,7 +26,6 @@ HAS_MODULES = False -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_MODULES, 'Salt modules could not be loaded') class YumKernelPkgTestCase(KernelPkgTestCase, TestCase, LoaderModuleMockMixin): ''' diff --git a/tests/unit/modules/test_key.py b/tests/unit/modules/test_key.py index 3f718ee5ce48..30a18b0c3b50 100644 --- a/tests/unit/modules/test_key.py +++ b/tests/unit/modules/test_key.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -22,7 +20,6 @@ import salt.modules.key as key -@skipIf(NO_MOCK, NO_MOCK_REASON) class KeyTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.key diff --git a/tests/unit/modules/test_keyboard.py b/tests/unit/modules/test_keyboard.py index 682ba3eed8b1..5182549cf09f 100644 --- a/tests/unit/modules/test_keyboard.py +++ b/tests/unit/modules/test_keyboard.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.keyboard as keyboard -@skipIf(NO_MOCK, NO_MOCK_REASON) class KeyboardTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.keyboard diff --git a/tests/unit/modules/test_keystone.py b/tests/unit/modules/test_keystone.py index dcf91b96f533..ce72ee171bac 100644 --- a/tests/unit/modules/test_keystone.py +++ b/tests/unit/modules/test_keystone.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -422,7 +420,6 @@ def Client(self, **kwargs): return True -@skipIf(NO_MOCK, NO_MOCK_REASON) class KeystoneTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.keystone diff --git a/tests/unit/modules/test_kubernetesmod.py b/tests/unit/modules/test_kubernetesmod.py index c2210e630662..f3dcbfcd0691 100644 --- a/tests/unit/modules/test_kubernetesmod.py +++ b/tests/unit/modules/test_kubernetesmod.py @@ -2,6 +2,7 @@ ''' :codeauthor: Jochen Breuer ''' +# pylint: disable=no-value-for-parameter # Import Python Libs from __future__ import absolute_import @@ -15,8 +16,6 @@ from tests.support.mock import ( Mock, patch, - NO_MOCK, - NO_MOCK_REASON ) import salt.utils.files @@ -35,7 +34,6 @@ def mock_kubernetes_library(): yield mock_kubernetes_lib -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not kubernetes.HAS_LIBS, "Kubernetes client lib is not installed. " "Skipping test_kubernetes.py") class KubernetesTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/modules/test_launchctl_service.py b/tests/unit/modules/test_launchctl_service.py index c4ade65748dd..306f48b29be7 100644 --- a/tests/unit/modules/test_launchctl_service.py +++ b/tests/unit/modules/test_launchctl_service.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -22,7 +20,6 @@ import salt.modules.launchctl_service as launchctl -@skipIf(NO_MOCK, NO_MOCK_REASON) class LaunchctlTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.launchctl diff --git a/tests/unit/modules/test_ldapmod.py b/tests/unit/modules/test_ldapmod.py index 16652345df06..e9c83dca93da 100644 --- a/tests/unit/modules/test_ldapmod.py +++ b/tests/unit/modules/test_ldapmod.py @@ -9,19 +9,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.ldapmod as ldapmod -@skipIf(NO_MOCK, NO_MOCK_REASON) class LdapmodTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.ldapmod diff --git a/tests/unit/modules/test_libcloud_compute.py b/tests/unit/modules/test_libcloud_compute.py index ce377cea6ad3..ee9e0a53523e 100644 --- a/tests/unit/modules/test_libcloud_compute.py +++ b/tests/unit/modules/test_libcloud_compute.py @@ -15,8 +15,6 @@ from tests.support.mock import ( patch, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import salt.modules.libcloud_compute as libcloud_compute @@ -199,7 +197,6 @@ def delete_key_pair(self, key_pair): MockComputeDriver = object -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_LIBCLOUD, 'No libcloud installed') @patch('salt.modules.libcloud_compute._get_driver', MagicMock(return_value=MockComputeDriver())) diff --git a/tests/unit/modules/test_libcloud_dns.py b/tests/unit/modules/test_libcloud_dns.py index c6b5b418085c..e0158f57dd6c 100644 --- a/tests/unit/modules/test_libcloud_dns.py +++ b/tests/unit/modules/test_libcloud_dns.py @@ -12,8 +12,6 @@ from tests.support.mock import ( patch, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import salt.modules.libcloud_dns as libcloud_dns @@ -27,7 +25,6 @@ def get_mock_driver(): return MockDNSDriver() -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not libcloud_dns.HAS_LIBCLOUD, 'No libcloud installed') class LibcloudDnsModuleTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/modules/test_libcloud_loadbalancer.py b/tests/unit/modules/test_libcloud_loadbalancer.py index 9a7da90be50f..6b8b6e72ceb9 100644 --- a/tests/unit/modules/test_libcloud_loadbalancer.py +++ b/tests/unit/modules/test_libcloud_loadbalancer.py @@ -12,8 +12,6 @@ from tests.support.mock import ( patch, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import salt.modules.libcloud_loadbalancer as libcloud_loadbalancer @@ -87,8 +85,7 @@ def get_mock_driver(): return MockLBDriver() -@skipIf(not HAS_LIBCLOUD, NO_MOCK_REASON) -@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not HAS_LIBCLOUD, 'No libcloud package') @patch('salt.modules.libcloud_loadbalancer._get_driver', MagicMock(return_value=MockLBDriver())) class LibcloudLoadBalancerModuleTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/modules/test_libcloud_storage.py b/tests/unit/modules/test_libcloud_storage.py index a72690bdd75b..8c4786e5b8b4 100644 --- a/tests/unit/modules/test_libcloud_storage.py +++ b/tests/unit/modules/test_libcloud_storage.py @@ -12,8 +12,6 @@ from tests.support.mock import ( patch, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import salt.modules.libcloud_storage as libcloud_storage @@ -63,7 +61,6 @@ def get_mock_driver(): return MockStorageDriver() -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_LIBCLOUD, 'No libcloud installed') @patch('salt.modules.libcloud_storage._get_driver', MagicMock(return_value=MockStorageDriver())) diff --git a/tests/unit/modules/test_linux_acl.py b/tests/unit/modules/test_linux_acl.py index 4dc544f493b6..ec594d718272 100644 --- a/tests/unit/modules/test_linux_acl.py +++ b/tests/unit/modules/test_linux_acl.py @@ -5,15 +5,14 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.modules.linux_acl as linux_acl from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class LinuxAclTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_linux_lvm.py b/tests/unit/modules/test_linux_lvm.py index d06049a78cbd..77ebf63720f2 100644 --- a/tests/unit/modules/test_linux_lvm.py +++ b/tests/unit/modules/test_linux_lvm.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -22,7 +20,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class LinuxLVMTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for the salt.modules.linux_lvm module @@ -66,6 +63,12 @@ def test_pvdisplay(self): with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): self.assertDictEqual(linux_lvm.pvdisplay(), {}) + mock = MagicMock(return_value={'retcode': 1}) + with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): + self.assertDictEqual(linux_lvm.pvdisplay(quiet=True), {}) + mock.assert_called_with(['pvdisplay', '-c'], ignore_retcode=True, + python_shell=False) + mock = MagicMock(return_value={'retcode': 0, 'stdout': 'A:B:C:D:E:F:G:H:I:J:K'}) with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): @@ -106,6 +109,12 @@ def test_vgdisplay(self): with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): self.assertDictEqual(linux_lvm.vgdisplay(), {}) + mock = MagicMock(return_value={'retcode': 1}) + with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): + self.assertDictEqual(linux_lvm.vgdisplay(quiet=True), {}) + mock.assert_called_with(['vgdisplay', '-c'], ignore_retcode=True, + python_shell=False) + mock = MagicMock(return_value={'retcode': 0, 'stdout': 'A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q'}) with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): diff --git a/tests/unit/modules/test_linux_sysctl.py b/tests/unit/modules/test_linux_sysctl.py index 0f6cb442c6d7..49cfb0a22feb 100644 --- a/tests/unit/modules/test_linux_sysctl.py +++ b/tests/unit/modules/test_linux_sysctl.py @@ -13,17 +13,14 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, mock_open, patch, - NO_MOCK, - NO_MOCK_REASON ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class LinuxSysctlTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.linux_sysctl module diff --git a/tests/unit/modules/test_localemod.py b/tests/unit/modules/test_localemod.py index fdccb22ca8aa..af77394bafee 100644 --- a/tests/unit/modules/test_localemod.py +++ b/tests/unit/modules/test_localemod.py @@ -13,8 +13,6 @@ MagicMock, Mock, patch, - NO_MOCK, - NO_MOCK_REASON ) try: import pytest @@ -28,7 +26,6 @@ @skipIf(not pytest, False) -@skipIf(NO_MOCK, NO_MOCK_REASON) class LocalemodTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.localemod @@ -160,24 +157,24 @@ def test_localectl_status_parser_no_systemd(self): Test localectl status parser raises an exception if no systemd installed. :return: ''' - with pytest.raises(CommandExecutionError) as err: + with pytest.raises(CommandExecutionError) as exc_info: localemod._localectl_status() - assert 'Unable to find "localectl"' in six.text_type(err.value) + assert 'Unable to find "localectl"' in six.text_type(exc_info.value) assert not localemod.log.debug.called @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock(return_value=locale_ctl_out_empty)}) def test_localectl_status_parser_empty(self): - with pytest.raises(CommandExecutionError) as err: + with pytest.raises(CommandExecutionError) as exc_info: localemod._localectl_status() - assert 'Unable to parse result of "localectl"' in six.text_type(err.value) + assert 'Unable to parse result of "localectl"' in six.text_type(exc_info.value) @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock(return_value=locale_ctl_out_broken)}) def test_localectl_status_parser_broken(self): - with pytest.raises(CommandExecutionError) as err: + with pytest.raises(CommandExecutionError) as exc_info: localemod._localectl_status() - assert 'Unable to parse result of "localectl"' in six.text_type(err.value) + assert 'Unable to parse result of "localectl"' in six.text_type(exc_info.value) @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock(return_value=locale_ctl_out_structure)}) @@ -293,9 +290,9 @@ def test_get_locale_with_no_systemd_unknown(self): Test getting current system locale with systemd and dbus available on Gentoo. :return: ''' - with pytest.raises(CommandExecutionError) as err: + with pytest.raises(CommandExecutionError) as exc_info: localemod.get_locale() - assert '"DrunkDragon" is unsupported' in six.text_type(err.value) + assert '"DrunkDragon" is unsupported' in six.text_type(exc_info.value) @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) @patch('salt.modules.localemod.__grains__', {'os_family': 'Ubuntu', 'osmajorrelease': 42}) @@ -395,10 +392,10 @@ def test_set_locale_with_no_systemd_debian_no_update_locale(self): :return: ''' loc = 'de_DE.utf8' - with pytest.raises(CommandExecutionError) as err: + with pytest.raises(CommandExecutionError) as exc_info: localemod.set_locale(loc) assert not localemod._localectl_set.called - assert 'Cannot set locale: "update-locale" was not found.' in six.text_type(err.value) + assert 'Cannot set locale: "update-locale" was not found.' in six.text_type(exc_info.value) @patch('salt.utils.path.which', MagicMock(return_value=None)) @patch('salt.modules.localemod.__grains__', {'os_family': 'Gentoo', 'osmajorrelease': 42}) @@ -467,9 +464,9 @@ def test_set_locale_with_no_systemd_unknown(self): Test setting current system locale without systemd on unknown system. :return: ''' - with pytest.raises(CommandExecutionError) as err: + with pytest.raises(CommandExecutionError) as exc_info: localemod.set_locale('de_DE.utf8') - assert 'Unsupported platform' in six.text_type(err.value) + assert 'Unsupported platform' in six.text_type(exc_info.value) @patch('salt.utils.locales.normalize_locale', MagicMock(return_value='en_US.UTF-8 UTF-8')) @patch('salt.modules.localemod.__salt__', {'locale.list_avail': MagicMock(return_value=['A', 'B'])}) @@ -537,9 +534,9 @@ def test_gen_locale_suse_localedef_error_handling(self): Tests the location where gen_locale is handling error while calling not installed localedef on Suse os-family. :return: ''' - with pytest.raises(CommandExecutionError) as err: + with pytest.raises(CommandExecutionError) as exc_info: localemod.gen_locale('de_DE.utf8') - assert 'Command "locale-gen" or "localedef" was not found on this system.' in six.text_type(err.value) + assert 'Command "locale-gen" or "localedef" was not found on this system.' in six.text_type(exc_info.value) def test_gen_locale_debian(self): ''' diff --git a/tests/unit/modules/test_locate.py b/tests/unit/modules/test_locate.py index d595c22e7580..a7c6e48882f2 100644 --- a/tests/unit/modules/test_locate.py +++ b/tests/unit/modules/test_locate.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.locate as locate -@skipIf(NO_MOCK, NO_MOCK_REASON) class LocateTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.locate diff --git a/tests/unit/modules/test_logadm.py b/tests/unit/modules/test_logadm.py index c51564cd99ff..0329ddcd77f7 100644 --- a/tests/unit/modules/test_logadm.py +++ b/tests/unit/modules/test_logadm.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.logadm as logadm -@skipIf(NO_MOCK, NO_MOCK_REASON) class LogadmTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.logadm diff --git a/tests/unit/modules/test_logrotate.py b/tests/unit/modules/test_logrotate.py index 4ecb57039cba..c38b4ca7634b 100644 --- a/tests/unit/modules/test_logrotate.py +++ b/tests/unit/modules/test_logrotate.py @@ -12,12 +12,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) PARSE_CONF = { @@ -31,7 +29,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class LogrotateTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.logrotate diff --git a/tests/unit/modules/test_lvs.py b/tests/unit/modules/test_lvs.py index 24d8552787a9..7c14b5cea4fd 100644 --- a/tests/unit/modules/test_lvs.py +++ b/tests/unit/modules/test_lvs.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.lvs as lvs -@skipIf(NO_MOCK, NO_MOCK_REASON) class LvsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.lvs diff --git a/tests/unit/modules/test_mac_brew_pkg.py b/tests/unit/modules/test_mac_brew_pkg.py index b26395b91ff3..2562467b6ba3 100644 --- a/tests/unit/modules/test_mac_brew_pkg.py +++ b/tests/unit/modules/test_mac_brew_pkg.py @@ -13,15 +13,14 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, Mock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, Mock, patch TAPS_STRING = 'homebrew/dupes\nhomebrew/science\nhomebrew/x11' TAPS_LIST = ['homebrew/dupes', 'homebrew/science', 'homebrew/x11'] HOMEBREW_BIN = '/usr/local/bin/brew' -@skipIf(NO_MOCK, NO_MOCK_REASON) class BrewTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.mac_brew module diff --git a/tests/unit/modules/test_mac_desktop.py b/tests/unit/modules/test_mac_desktop.py index 8dbafab6f1b4..3b1c71ffc782 100644 --- a/tests/unit/modules/test_mac_desktop.py +++ b/tests/unit/modules/test_mac_desktop.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class MacDesktopTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.mac_desktop diff --git a/tests/unit/modules/test_mac_pkgutil.py b/tests/unit/modules/test_mac_pkgutil.py index de284e371521..29b5926b610b 100644 --- a/tests/unit/modules/test_mac_pkgutil.py +++ b/tests/unit/modules/test_mac_pkgutil.py @@ -8,11 +8,10 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch +from tests.support.unit import TestCase +from tests.support.mock import patch -@skipIf(NO_MOCK, NO_MOCK_REASON) class MacPkgutilTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_mac_power.py b/tests/unit/modules/test_mac_power.py index f4f74dd27edc..3c2d990de31b 100644 --- a/tests/unit/modules/test_mac_power.py +++ b/tests/unit/modules/test_mac_power.py @@ -7,15 +7,13 @@ from __future__ import absolute_import, unicode_literals, print_function # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase # Import Salt Libs import salt.modules.mac_power as mac_power from salt.exceptions import SaltInvocationError -@skipIf(NO_MOCK, NO_MOCK_REASON) class MacPowerTestCase(TestCase): ''' test mac_power execution module diff --git a/tests/unit/modules/test_mac_service.py b/tests/unit/modules/test_mac_service.py index 87bc50d5ada0..04392c427774 100644 --- a/tests/unit/modules/test_mac_service.py +++ b/tests/unit/modules/test_mac_service.py @@ -11,16 +11,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class MacServiceTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.mac_service module diff --git a/tests/unit/modules/test_mac_sysctl.py b/tests/unit/modules/test_mac_sysctl.py index c1e889b5a648..35cc00f1b821 100644 --- a/tests/unit/modules/test_mac_sysctl.py +++ b/tests/unit/modules/test_mac_sysctl.py @@ -12,18 +12,15 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, mock_open, patch, - NO_MOCK, - NO_MOCK_REASON, DEFAULT ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class DarwinSysctlTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.mac_sysctl module diff --git a/tests/unit/modules/test_mac_user.py b/tests/unit/modules/test_mac_user.py index a86c16678b42..a3ebf3832f41 100644 --- a/tests/unit/modules/test_mac_user.py +++ b/tests/unit/modules/test_mac_user.py @@ -13,7 +13,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.modules.mac_user as mac_user @@ -21,7 +21,6 @@ @skipIf(not HAS_PWD, "Missing required library 'pwd'") -@skipIf(NO_MOCK, NO_MOCK_REASON) class MacUserTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for the salt.modules.mac_user modules diff --git a/tests/unit/modules/test_mandrill.py b/tests/unit/modules/test_mandrill.py index e1ac0de353c8..368d2b8e068a 100644 --- a/tests/unit/modules/test_mandrill.py +++ b/tests/unit/modules/test_mandrill.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -34,7 +32,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class MandrillModuleTest(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.mandrill. diff --git a/tests/unit/modules/test_match.py b/tests/unit/modules/test_match.py index 1b9da2b1c0d6..3e62f4828a40 100644 --- a/tests/unit/modules/test_match.py +++ b/tests/unit/modules/test_match.py @@ -9,15 +9,14 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, ) # Import Salt Libs +import salt.loader import salt.modules.match as match import salt.matchers.compound_match as compound_match import salt.matchers.glob_match as glob_match @@ -33,7 +32,6 @@ MINION_ID = 'bar03' -@skipIf(NO_MOCK, NO_MOCK_REASON) @patch('salt.loader.matchers', MagicMock(return_value=MATCHERS_DICT)) class MatchTestCase(TestCase, LoaderModuleMockMixin): ''' @@ -61,6 +59,43 @@ def setup_loader_modules(self): } } + def test_compound_with_minion_id(self): + ''' + Make sure that when a minion_id IS past, that it is contained in opts + ''' + mock_compound_match = MagicMock() + target = 'bar04' + new_minion_id = 'new_minion_id' + + with patch.object(salt.loader, 'matchers', return_value={'compound_match.match': mock_compound_match}) as matchers: + match.compound(target, minion_id=new_minion_id) + + # The matcher should get called with MINION_ID + matchers.assert_called_once() + matchers_opts = matchers.call_args[0][0] + self.assertEqual(matchers_opts.get('id'), new_minion_id) + + # The compound matcher should not get MINION_ID, no opts should be passed + mock_compound_match.assert_called_once_with(target) + + def test_compound(self): + ''' + Test issue #55149 + ''' + mock_compound_match = MagicMock() + target = 'bar04' + + with patch.object(salt.loader, 'matchers', return_value={'compound_match.match': mock_compound_match}) as matchers: + match.compound(target) + + # The matcher should get called with MINION_ID + matchers.assert_called_once() + self.assertEqual(len(matchers.call_args[0]), 1) + self.assertEqual(matchers.call_args[0][0].get('id'), MINION_ID) + + # The compound matcher should not get MINION_ID, no opts should be passed + mock_compound_match.assert_called_once_with(target) + def test_filter_by(self): ''' Tests if filter_by returns the correct dictionary. diff --git a/tests/unit/modules/test_mdadm_raid.py b/tests/unit/modules/test_mdadm_raid.py index bfca3af8762e..16d36f96e1fa 100644 --- a/tests/unit/modules/test_mdadm_raid.py +++ b/tests/unit/modules/test_mdadm_raid.py @@ -12,14 +12,13 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.modules.mdadm_raid as mdadm -@skipIf(NO_MOCK, NO_MOCK_REASON) class MdadmTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -75,3 +74,26 @@ def test_create_test_mode(self): '--force -l 5 -e default -n 3 ' '/dev/sdb1 /dev/sdc1 /dev/sdd1'.split()), sorted(ret.split())) assert not mock.called, 'test mode failed, cmd.run called' + + def test_examine(self): + ''' + Test for mdadm_raid.examine + ''' + mock = MagicMock(return_value='ARRAY /dev/md/pool metadata=1.2 UUID=567da122:fb8e445e:55b853e0:81bd0a3e name=positron:pool') + with patch.dict(mdadm.__salt__, {'cmd.run_stdout': mock}): + self.assertEqual(mdadm.examine('/dev/md0'), + { + 'ARRAY /dev/md/pool metadata': '1.2 UUID=567da122:fb8e445e:55b853e0:81bd0a3e name=positron:pool' + }) + mock.assert_called_with('mdadm -Y -E /dev/md0', ignore_retcode=False, + python_shell=False) + + def test_examine_quiet(self): + ''' + Test for mdadm_raid.examine + ''' + mock = MagicMock(return_value='') + with patch.dict(mdadm.__salt__, {'cmd.run_stdout': mock}): + self.assertEqual(mdadm.examine('/dev/md0', quiet=True), {}) + mock.assert_called_with('mdadm -Y -E /dev/md0', ignore_retcode=True, + python_shell=False) diff --git a/tests/unit/modules/test_memcached.py b/tests/unit/modules/test_memcached.py index 9156645cb661..3c0d84fb08e5 100644 --- a/tests/unit/modules/test_memcached.py +++ b/tests/unit/modules/test_memcached.py @@ -7,12 +7,10 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ from salt.ext.six import integer_types -@skipIf(NO_MOCK, NO_MOCK_REASON) class MemcachedTestCase(TestCase): ''' Test cases for salt.modules.memcached diff --git a/tests/unit/modules/test_mine.py b/tests/unit/modules/test_mine.py index 9e5faf50bcfb..fcd39189b08b 100644 --- a/tests/unit/modules/test_mine.py +++ b/tests/unit/modules/test_mine.py @@ -8,18 +8,15 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.mine as mine -@skipIf(NO_MOCK, NO_MOCK_REASON) class MineTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.mine diff --git a/tests/unit/modules/test_mod_random.py b/tests/unit/modules/test_mod_random.py index 88be09a766b9..b1fbec69ff9a 100644 --- a/tests/unit/modules/test_mod_random.py +++ b/tests/unit/modules/test_mod_random.py @@ -11,8 +11,6 @@ from tests.support.unit import TestCase, skipIf from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -45,7 +43,6 @@ def _test_hashlib(): @skipIf(not SUPPORTED_HASHLIB, 'Hashlib does not contain needed functionality') -@skipIf(NO_MOCK, NO_MOCK_REASON) class ModrandomTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.mod_random diff --git a/tests/unit/modules/test_modjk.py b/tests/unit/modules/test_modjk.py index f71ed54fb01d..e23d91fd35df 100644 --- a/tests/unit/modules/test_modjk.py +++ b/tests/unit/modules/test_modjk.py @@ -7,18 +7,15 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.modjk as modjk -@skipIf(NO_MOCK, NO_MOCK_REASON) class ModjkTestCase(TestCase): ''' Test cases for salt.modules.modjk diff --git a/tests/unit/modules/test_monit.py b/tests/unit/modules/test_monit.py index a27b8d40e794..9eb3f5a20055 100644 --- a/tests/unit/modules/test_monit.py +++ b/tests/unit/modules/test_monit.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.monit as monit -@skipIf(NO_MOCK, NO_MOCK_REASON) class MonitTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.aptpkg diff --git a/tests/unit/modules/test_moosefs.py b/tests/unit/modules/test_moosefs.py index 537f254ed448..2fbb1b0bcbe3 100644 --- a/tests/unit/modules/test_moosefs.py +++ b/tests/unit/modules/test_moosefs.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.moosefs as moosefs -@skipIf(NO_MOCK, NO_MOCK_REASON) class MoosefsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.moosefs diff --git a/tests/unit/modules/test_mount.py b/tests/unit/modules/test_mount.py index 1a0d64f2f582..628c5c26c79f 100644 --- a/tests/unit/modules/test_mount.py +++ b/tests/unit/modules/test_mount.py @@ -9,13 +9,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( mock_open, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -27,7 +25,6 @@ MOCK_SHELL_FILE = 'A B C D F G\n' -@skipIf(NO_MOCK, NO_MOCK_REASON) class MountTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.mount @@ -362,7 +359,7 @@ def test_mount(self): with patch.dict(mount.__salt__, {'cmd.run_all': mock}): self.assertTrue(mount.mount('name', 'device')) - def test_remount(self): + def test_remount_non_mounted(self): ''' Attempt to remount a device, if the device is not already mounted, mount is called @@ -381,6 +378,77 @@ def test_remount(self): with patch.object(mount, 'mount', mock): self.assertTrue(mount.remount('name', 'device')) + with patch.dict(mount.__grains__, {'os': 'Linux'}): + mock = MagicMock(return_value=[]) + with patch.object(mount, 'active', mock): + mock = MagicMock(return_value=True) + with patch.object(mount, 'mount', mock): + self.assertTrue(mount.remount('name', 'device')) + + def test_remount_already_mounted_no_fstype(self): + ''' + Attempt to remount a device already mounted that do not provides + fstype + ''' + with patch.dict(mount.__grains__, {'os': 'MacOS'}): + mock = MagicMock(return_value=['name']) + with patch.object(mount, 'active', mock): + mock = MagicMock(return_value={'retcode': 0}) + with patch.dict(mount.__salt__, {'cmd.run_all': mock}): + self.assertTrue(mount.remount('name', 'device')) + mock.assert_called_with('mount -u -o noowners device name ', + python_shell=False, runas=None) + + with patch.dict(mount.__grains__, {'os': 'AIX'}): + mock = MagicMock(return_value=['name']) + with patch.object(mount, 'active', mock): + mock = MagicMock(return_value={'retcode': 0}) + with patch.dict(mount.__salt__, {'cmd.run_all': mock}): + self.assertTrue(mount.remount('name', 'device')) + mock.assert_called_with('mount -o remount device name ', + python_shell=False, runas=None) + + with patch.dict(mount.__grains__, {'os': 'Linux'}): + mock = MagicMock(return_value=['name']) + with patch.object(mount, 'active', mock): + mock = MagicMock(return_value={'retcode': 0}) + with patch.dict(mount.__salt__, {'cmd.run_all': mock}): + self.assertTrue(mount.remount('name', 'device')) + mock.assert_called_with('mount -o defaults,remount device name ', + python_shell=False, runas=None) + + def test_remount_already_mounted_with_fstype(self): + ''' + Attempt to remount a device already mounted that do not provides + fstype + ''' + with patch.dict(mount.__grains__, {'os': 'MacOS'}): + mock = MagicMock(return_value=['name']) + with patch.object(mount, 'active', mock): + mock = MagicMock(return_value={'retcode': 0}) + with patch.dict(mount.__salt__, {'cmd.run_all': mock}): + self.assertTrue(mount.remount('name', 'device', fstype='type')) + mock.assert_called_with('mount -u -o noowners -t type device name ', + python_shell=False, runas=None) + + with patch.dict(mount.__grains__, {'os': 'AIX'}): + mock = MagicMock(return_value=['name']) + with patch.object(mount, 'active', mock): + mock = MagicMock(return_value={'retcode': 0}) + with patch.dict(mount.__salt__, {'cmd.run_all': mock}): + self.assertTrue(mount.remount('name', 'device', fstype='type')) + mock.assert_called_with('mount -o remount -v type device name ', + python_shell=False, runas=None) + + with patch.dict(mount.__grains__, {'os': 'Linux'}): + mock = MagicMock(return_value=['name']) + with patch.object(mount, 'active', mock): + mock = MagicMock(return_value={'retcode': 0}) + with patch.dict(mount.__salt__, {'cmd.run_all': mock}): + self.assertTrue(mount.remount('name', 'device', fstype='type')) + mock.assert_called_with('mount -o defaults,remount -t type device name ', + python_shell=False, runas=None) + def test_umount(self): ''' Attempt to unmount a device by specifying the directory it is diff --git a/tests/unit/modules/test_munin.py b/tests/unit/modules/test_munin.py index e1bc601c206e..eba929efd8e0 100644 --- a/tests/unit/modules/test_munin.py +++ b/tests/unit/modules/test_munin.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.munin as munin -@skipIf(NO_MOCK, NO_MOCK_REASON) class MuninTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.munin diff --git a/tests/unit/modules/test_mysql.py b/tests/unit/modules/test_mysql.py index 0942ca56d08b..4e8cd46aa0d3 100644 --- a/tests/unit/modules/test_mysql.py +++ b/tests/unit/modules/test_mysql.py @@ -13,7 +13,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch, call +from tests.support.mock import MagicMock, patch, call # Import salt libs import salt.modules.mysql as mysql @@ -71,7 +71,6 @@ ] -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(NO_MYSQL, 'Install MySQL bindings before running MySQL unit tests.') class MySQLTestCase(TestCase, LoaderModuleMockMixin): @@ -450,7 +449,9 @@ def test_query_error(self): connect_mock = MagicMock() with patch.object(mysql, '_connect', connect_mock): with patch.dict(mysql.__salt__, {'config.option': MagicMock()}): - side_effect = MySQLdb.OperationalError(9999, 'Something Went Wrong') + # Use the OperationalError from the salt mysql module because that + # exception can come from either MySQLdb or pymysql + side_effect = mysql.OperationalError(9999, 'Something Went Wrong') with patch.object(mysql, '_execute', MagicMock(side_effect=side_effect)): mysql.query('testdb', 'SELECT * FROM testdb') self.assertIn('mysql.error', mysql.__context__) diff --git a/tests/unit/modules/test_nacl.py b/tests/unit/modules/test_nacl.py index 20a9b5fc8534..1270a5f7e691 100644 --- a/tests/unit/modules/test_nacl.py +++ b/tests/unit/modules/test_nacl.py @@ -30,7 +30,7 @@ class NaclTest(TestCase, LoaderModuleMockMixin): ''' def setup_loader_modules(self): self.unencrypted_data = salt.utils.stringutils.to_bytes('hello') - self.opts = salt.config.DEFAULT_MINION_OPTS + self.opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils(self.opts) funcs = salt.loader.minion_mods(self.opts, utils=utils, whitelist=['nacl']) diff --git a/tests/unit/modules/test_nagios.py b/tests/unit/modules/test_nagios.py index 0ebb5026f6ab..ca2d9b4ce4b5 100644 --- a/tests/unit/modules/test_nagios.py +++ b/tests/unit/modules/test_nagios.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.nagios as nagios -@skipIf(NO_MOCK, NO_MOCK_REASON) class NagiosTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.nagios diff --git a/tests/unit/modules/test_napalm_bgp.py b/tests/unit/modules/test_napalm_bgp.py index cc5afdf45f73..8d92b0afc27e 100644 --- a/tests/unit/modules/test_napalm_bgp.py +++ b/tests/unit/modules/test_napalm_bgp.py @@ -8,18 +8,15 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import tests.support.napalm as napalm_test_support import salt.modules.napalm_bgp as napalm_bgp # NOQA -@skipIf(NO_MOCK, NO_MOCK_REASON) class NapalmBgpModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_napalm_formula.py b/tests/unit/modules/test_napalm_formula.py index bcd91b93f29d..e1799a4f0a54 100644 --- a/tests/unit/modules/test_napalm_formula.py +++ b/tests/unit/modules/test_napalm_formula.py @@ -9,14 +9,13 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch +from tests.support.unit import TestCase +from tests.support.mock import patch # Import Salt modules import salt.modules.napalm_formula as napalm_formula -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestModulesNAPALMFormula(TestCase, LoaderModuleMockMixin): model = { diff --git a/tests/unit/modules/test_napalm_netacl.py b/tests/unit/modules/test_napalm_netacl.py index 1ccff9a58b7d..c73048d13262 100644 --- a/tests/unit/modules/test_napalm_netacl.py +++ b/tests/unit/modules/test_napalm_netacl.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import tests.support.napalm as napalm_test_support @@ -53,7 +51,6 @@ def mock_capirca_get_term_pillar(filter_, term, *args, **kwargs): return {'test': 'value'} -@skipIf(NO_MOCK, NO_MOCK_REASON) class NapalmAclModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_napalm_network.py b/tests/unit/modules/test_napalm_network.py index 5fc0e362c846..cf7ea26f239f 100644 --- a/tests/unit/modules/test_napalm_network.py +++ b/tests/unit/modules/test_napalm_network.py @@ -8,18 +8,15 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import tests.support.napalm as napalm_test_support import salt.modules.napalm_network as napalm_network # NOQA -@skipIf(NO_MOCK, NO_MOCK_REASON) class NapalmNetworkModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_napalm_ntp.py b/tests/unit/modules/test_napalm_ntp.py index e94aec291cd5..62140a4810e5 100644 --- a/tests/unit/modules/test_napalm_ntp.py +++ b/tests/unit/modules/test_napalm_ntp.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import tests.support.napalm as napalm_test_support @@ -26,7 +24,6 @@ def mock_net_load_template(template, *args, **kwargs): assert '2.2.3.4' in kwargs['servers'] -@skipIf(NO_MOCK, NO_MOCK_REASON) class NapalmNtpModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_napalm_probes.py b/tests/unit/modules/test_napalm_probes.py index e4855ad2942e..3b8aacb959a2 100644 --- a/tests/unit/modules/test_napalm_probes.py +++ b/tests/unit/modules/test_napalm_probes.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import tests.support.napalm as napalm_test_support @@ -61,7 +59,6 @@ def mock_net_load(template, *args, **kwargs): raise ValueError("incorrect template {0}".format(template)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class NapalmProbesModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_napalm_route.py b/tests/unit/modules/test_napalm_route.py index 54b192260fca..c459ac818f90 100644 --- a/tests/unit/modules/test_napalm_route.py +++ b/tests/unit/modules/test_napalm_route.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import tests.support.napalm as napalm_test_support @@ -23,7 +21,6 @@ def mock_net_load(template, *args, **kwargs): raise ValueError("incorrect template {0}".format(template)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class NapalmRouteModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_napalm_snmp.py b/tests/unit/modules/test_napalm_snmp.py index dcb5bb1e37e3..d5f82f38a929 100644 --- a/tests/unit/modules/test_napalm_snmp.py +++ b/tests/unit/modules/test_napalm_snmp.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import tests.support.napalm as napalm_test_support @@ -20,7 +18,6 @@ import salt.modules.napalm_network as napalm_network # NOQA -@skipIf(NO_MOCK, NO_MOCK_REASON) class NapalmSnmpModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_napalm_users.py b/tests/unit/modules/test_napalm_users.py index 37f68c8b7212..f8214ea7609a 100644 --- a/tests/unit/modules/test_napalm_users.py +++ b/tests/unit/modules/test_napalm_users.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import tests.support.napalm as napalm_test_support @@ -20,7 +18,6 @@ import salt.modules.napalm_network as napalm_network # NOQA -@skipIf(NO_MOCK, NO_MOCK_REASON) class NapalmUsersModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_napalm_yang_mod.py b/tests/unit/modules/test_napalm_yang_mod.py index e2af6882d87b..2be6d203bd67 100644 --- a/tests/unit/modules/test_napalm_yang_mod.py +++ b/tests/unit/modules/test_napalm_yang_mod.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import tests.support.napalm as napalm_test_support @@ -57,7 +55,6 @@ def mock_net_load_config(**kwargs): return TEST_CONFIG -@skipIf(NO_MOCK, NO_MOCK_REASON) class NapalmYangModModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_netbox.py b/tests/unit/modules/test_netbox.py index d408b74e086b..d6c005f6399f 100644 --- a/tests/unit/modules/test_netbox.py +++ b/tests/unit/modules/test_netbox.py @@ -19,8 +19,6 @@ patch, MagicMock, call, - NO_MOCK, - NO_MOCK_REASON ) import salt.modules.netbox as netbox @@ -49,7 +47,6 @@ def mocked_clean_kwargs_get(**kwargs): @skipIf(HAS_PYNETBOX is False, 'pynetbox lib not installed') -@skipIf(NO_MOCK, NO_MOCK_REASON) @patch('salt.modules.netbox._config', MagicMock()) class NetBoxTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/modules/test_netscaler.py b/tests/unit/modules/test_netscaler.py index ad79c3797e82..1c026d64719d 100644 --- a/tests/unit/modules/test_netscaler.py +++ b/tests/unit/modules/test_netscaler.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -555,7 +553,6 @@ def delete(obj, servicegroup): return MockNSSSLVServerSSLCertKeyBinding() -@skipIf(NO_MOCK, NO_MOCK_REASON) class NetscalerTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.netscaler diff --git a/tests/unit/modules/test_network.py b/tests/unit/modules/test_network.py index 0f9c28d9c64b..7b36f965f5ba 100644 --- a/tests/unit/modules/test_network.py +++ b/tests/unit/modules/test_network.py @@ -16,8 +16,6 @@ mock_open, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -30,7 +28,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) class NetworkTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.network diff --git a/tests/unit/modules/test_neutron.py b/tests/unit/modules/test_neutron.py index 8e303cf187b7..423b6f26aa89 100644 --- a/tests/unit/modules/test_neutron.py +++ b/tests/unit/modules/test_neutron.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -471,7 +469,6 @@ def delete_ipsecpolicy(ipsecpolicy): return ipsecpolicy -@skipIf(NO_MOCK, NO_MOCK_REASON) class NeutronTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.neutron diff --git a/tests/unit/modules/test_nexus.py b/tests/unit/modules/test_nexus.py index 9fa28c397607..3315b0867e46 100644 --- a/tests/unit/modules/test_nexus.py +++ b/tests/unit/modules/test_nexus.py @@ -5,14 +5,13 @@ # Import Salt testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt libs import salt.modules.nexus as nexus -@skipIf(NO_MOCK, NO_MOCK_REASON) class nexusTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_nfs3.py b/tests/unit/modules/test_nfs3.py index 6fa010d8ee2d..557dbac5dac7 100644 --- a/tests/unit/modules/test_nfs3.py +++ b/tests/unit/modules/test_nfs3.py @@ -8,20 +8,17 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, mock_open, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.nfs3 as nfs3 -@skipIf(NO_MOCK, NO_MOCK_REASON) class NfsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.nfs3 diff --git a/tests/unit/modules/test_nftables.py b/tests/unit/modules/test_nftables.py index 0746b5b638ab..f755c41339d6 100644 --- a/tests/unit/modules/test_nftables.py +++ b/tests/unit/modules/test_nftables.py @@ -8,13 +8,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, mock_open, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -23,7 +21,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class NftablesTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.nftables diff --git a/tests/unit/modules/test_nginx.py b/tests/unit/modules/test_nginx.py index c75906197c35..c634d2aa49a1 100644 --- a/tests/unit/modules/test_nginx.py +++ b/tests/unit/modules/test_nginx.py @@ -5,8 +5,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, Mock, patch +from tests.support.unit import TestCase +from tests.support.mock import Mock, patch # Import Salt Module import salt.modules.nginx as nginx @@ -26,7 +26,6 @@ def close(self): pass -@skipIf(NO_MOCK, NO_MOCK_REASON) class NginxTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_nova.py b/tests/unit/modules/test_nova.py index 56197ddba26d..4e3cc9e02e96 100644 --- a/tests/unit/modules/test_nova.py +++ b/tests/unit/modules/test_nova.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.nova as nova -@skipIf(NO_MOCK, NO_MOCK_REASON) class NovaTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.nova diff --git a/tests/unit/modules/test_npm.py b/tests/unit/modules/test_npm.py index 4bd42791f0bd..a2c87d765630 100644 --- a/tests/unit/modules/test_npm.py +++ b/tests/unit/modules/test_npm.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -23,7 +21,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class NpmTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.npm diff --git a/tests/unit/modules/test_openbsd_sysctl.py b/tests/unit/modules/test_openbsd_sysctl.py index 248adaf7673b..2a92e067952a 100644 --- a/tests/unit/modules/test_openbsd_sysctl.py +++ b/tests/unit/modules/test_openbsd_sysctl.py @@ -12,16 +12,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class OpenBSDSysctlTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.openbsd_sysctl module diff --git a/tests/unit/modules/test_openbsdpkg.py b/tests/unit/modules/test_openbsdpkg.py index fe607b7db4b9..f5666745696c 100644 --- a/tests/unit/modules/test_openbsdpkg.py +++ b/tests/unit/modules/test_openbsdpkg.py @@ -8,13 +8,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, call, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -35,7 +33,6 @@ def __call__(self): return pkgs -@skipIf(NO_MOCK, NO_MOCK_REASON) class OpenbsdpkgTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.openbsdpkg diff --git a/tests/unit/modules/test_openscap.py b/tests/unit/modules/test_openscap.py index 6e17148de19d..516b639ed2fd 100644 --- a/tests/unit/modules/test_openscap.py +++ b/tests/unit/modules/test_openscap.py @@ -8,20 +8,17 @@ import salt.modules.openscap as openscap # Import salt test libs -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( Mock, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import 3rd-party libs from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) class OpenscapTestCase(TestCase): random_temp_dir = '/tmp/unique-name' diff --git a/tests/unit/modules/test_openstack_config.py b/tests/unit/modules/test_openstack_config.py index 0e1c2e6c43cb..6f40412233ab 100644 --- a/tests/unit/modules/test_openstack_config.py +++ b/tests/unit/modules/test_openstack_config.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class OpenstackConfigTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.openstack_config diff --git a/tests/unit/modules/test_opkg.py b/tests/unit/modules/test_opkg.py index c52d196ecb48..a8614ed8edcf 100644 --- a/tests/unit/modules/test_opkg.py +++ b/tests/unit/modules/test_opkg.py @@ -11,12 +11,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -64,7 +62,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class OpkgTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.opkg @@ -143,6 +140,23 @@ def test_install(self): with patch.multiple(opkg, **patch_kwargs): self.assertEqual(opkg.install('vim:7.4'), INSTALLED) + def test_install_noaction(self): + ''' + Test - Install packages. + ''' + with patch('salt.modules.opkg.list_pkgs', MagicMock(return_value=({}))): + ret_value = {'retcode': 0} + mock = MagicMock(return_value=ret_value) + patch_kwargs = { + '__salt__': { + 'cmd.run_all': mock, + 'pkg_resource.parse_targets': MagicMock(return_value=({'vim': '7.4'}, 'repository')), + 'restartcheck.restartcheck': MagicMock(return_value='No packages seem to need to be restarted') + } + } + with patch.multiple(opkg, **patch_kwargs): + self.assertEqual(opkg.install('vim:7.4', test=True), {}) + def test_remove(self): ''' Test - Remove packages. @@ -160,6 +174,23 @@ def test_remove(self): with patch.multiple(opkg, **patch_kwargs): self.assertEqual(opkg.remove('vim'), REMOVED) + def test_remove_noaction(self): + ''' + Test - Remove packages. + ''' + with patch('salt.modules.opkg.list_pkgs', MagicMock(return_value=({}))): + ret_value = {'retcode': 0} + mock = MagicMock(return_value=ret_value) + patch_kwargs = { + '__salt__': { + 'cmd.run_all': mock, + 'pkg_resource.parse_targets': MagicMock(return_value=({'vim': '7.4'}, 'repository')), + 'restartcheck.restartcheck': MagicMock(return_value='No packages seem to need to be restarted') + } + } + with patch.multiple(opkg, **patch_kwargs): + self.assertEqual(opkg.remove('vim:7.4', test=True), {}) + def test_info_installed(self): ''' Test - Return the information of the named package(s) installed on the system. diff --git a/tests/unit/modules/test_oracle.py b/tests/unit/modules/test_oracle.py index 6672a282ee0c..b85dbe24d00d 100644 --- a/tests/unit/modules/test_oracle.py +++ b/tests/unit/modules/test_oracle.py @@ -9,19 +9,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.oracle as oracle -@skipIf(NO_MOCK, NO_MOCK_REASON) class OracleTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.oracle diff --git a/tests/unit/modules/test_osquery.py b/tests/unit/modules/test_osquery.py index 50052ecd7575..a727c89f0b79 100644 --- a/tests/unit/modules/test_osquery.py +++ b/tests/unit/modules/test_osquery.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.osquery as osquery -@skipIf(NO_MOCK, NO_MOCK_REASON) class OSQueryTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.iptables diff --git a/tests/unit/modules/test_pacmanpkg.py b/tests/unit/modules/test_pacmanpkg.py index 80c863c6a5e6..dc6b9c0cbe40 100644 --- a/tests/unit/modules/test_pacmanpkg.py +++ b/tests/unit/modules/test_pacmanpkg.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.pacmanpkg as pacman -@skipIf(NO_MOCK, NO_MOCK_REASON) class PacmanTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.pacman diff --git a/tests/unit/modules/test_pagerduty.py b/tests/unit/modules/test_pagerduty.py index 721cebf66707..ad702864d8c9 100644 --- a/tests/unit/modules/test_pagerduty.py +++ b/tests/unit/modules/test_pagerduty.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -22,7 +20,6 @@ import salt.modules.pagerduty as pagerduty -@skipIf(NO_MOCK, NO_MOCK_REASON) class PagerdutyTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.pagerduty diff --git a/tests/unit/modules/test_pam.py b/tests/unit/modules/test_pam.py index 780463996ea5..0ea0ac910bf3 100644 --- a/tests/unit/modules/test_pam.py +++ b/tests/unit/modules/test_pam.py @@ -12,8 +12,6 @@ from tests.support.mock import ( patch, mock_open, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -22,7 +20,6 @@ MOCK_FILE = 'ok ok ignore ' -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(sys.platform.startswith('openbsd'), 'OpenBSD does not use PAM') class PamTestCase(TestCase): ''' diff --git a/tests/unit/modules/test_parallels.py b/tests/unit/modules/test_parallels.py index 953092316ab8..694a741243be 100644 --- a/tests/unit/modules/test_parallels.py +++ b/tests/unit/modules/test_parallels.py @@ -10,14 +10,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import third party libs from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) class ParallelsTestCase(TestCase, LoaderModuleMockMixin): ''' Test parallels desktop execution module functions diff --git a/tests/unit/modules/test_parted_partition.py b/tests/unit/modules/test_parted_partition.py index 8f381d55a6a4..70deb61c2854 100644 --- a/tests/unit/modules/test_parted_partition.py +++ b/tests/unit/modules/test_parted_partition.py @@ -13,15 +13,14 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt libs from salt.exceptions import CommandExecutionError import salt.modules.parted_partition as parted -@skipIf(NO_MOCK, NO_MOCK_REASON) class PartedTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_pecl.py b/tests/unit/modules/test_pecl.py index adc16c95d3b8..d7b0e66865eb 100644 --- a/tests/unit/modules/test_pecl.py +++ b/tests/unit/modules/test_pecl.py @@ -7,18 +7,15 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.pecl as pecl -@skipIf(NO_MOCK, NO_MOCK_REASON) class PeclTestCase(TestCase): ''' Test cases for salt.modules.pecl diff --git a/tests/unit/modules/test_pillar.py b/tests/unit/modules/test_pillar.py index 6c058df34045..ef39b953ba01 100644 --- a/tests/unit/modules/test_pillar.py +++ b/tests/unit/modules/test_pillar.py @@ -5,12 +5,10 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt libs @@ -45,13 +43,11 @@ def test_obfuscate_inner_more_types(self): self.assertEqual(pillarmod._obfuscate_inner((1, 2)), ('', '')) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_obfuscate(self): with patch('salt.modules.pillar.items', MagicMock(return_value=pillar_value_1)): self.assertEqual(pillarmod.obfuscate(), dict(a='', b='')) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_ls(self): with patch('salt.modules.pillar.items', MagicMock(return_value=pillar_value_1)): ls = sorted(pillarmod.ls()) @@ -60,7 +56,6 @@ def test_ls(self): else: self.assertEqual(ls, ['a', 'b']) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_pillar_get_default_merge(self): defaults = {'int': 1, 'string': 'foo', diff --git a/tests/unit/modules/test_pip.py b/tests/unit/modules/test_pip.py index 23e5f03e8e5d..b2677295d119 100644 --- a/tests/unit/modules/test_pip.py +++ b/tests/unit/modules/test_pip.py @@ -7,8 +7,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.utils.platform @@ -16,7 +16,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class PipTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return {pip: {'__salt__': {'cmd.which_bin': lambda _: 'pip'}}} @@ -750,6 +749,53 @@ def test_install_proxy_argument_in_resulting_command(self): python_shell=False, ) + def test_install_proxy_false_argument_in_resulting_command(self): + ''' + Checking that there is no proxy set if proxy arg is set to False + even if the global proxy is set. + ''' + pkg = 'pep8' + proxy = False + mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + config_mock = {'proxy_host': 'salt-proxy', + 'proxy_port': '3128', + 'proxy_username': 'salt-user', + 'proxy_password': 'salt-passwd'} + with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + with patch.dict(pip.__opts__, config_mock): + pip.install(pkg, proxy=proxy) + expected = [sys.executable, '-m', 'pip', 'install', pkg] + mock.assert_called_with( + expected, + saltenv='base', + runas=None, + use_vt=False, + python_shell=False, + ) + + def test_install_global_proxy_in_resulting_command(self): + ''' + Checking that there is proxy set if global proxy is set. + ''' + pkg = 'pep8' + proxy = 'http://salt-user:salt-passwd@salt-proxy:3128' + mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + config_mock = {'proxy_host': 'salt-proxy', + 'proxy_port': '3128', + 'proxy_username': 'salt-user', + 'proxy_password': 'salt-passwd'} + with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + with patch.dict(pip.__opts__, config_mock): + pip.install(pkg) + expected = [sys.executable, '-m', 'pip', 'install', '--proxy', proxy, pkg] + mock.assert_called_with( + expected, + saltenv='base', + runas=None, + use_vt=False, + python_shell=False, + ) + def test_install_multiple_requirements_arguments_in_resulting_command(self): with patch('salt.modules.pip._get_cached_requirements') as get_cached_requirements: cached_reqs = [ @@ -803,6 +849,41 @@ def test_install_multiple_requirements_arguments_in_resulting_command(self): python_shell=False, ) + def test_install_extra_args_arguments_in_resulting_command(self): + pkg = 'pep8' + mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pip.install(pkg, extra_args=[ + {"--latest-pip-kwarg": "param"}, + "--latest-pip-arg" + ]) + expected = [ + sys.executable, '-m', 'pip', 'install', pkg, + "--latest-pip-kwarg", "param", "--latest-pip-arg" + ] + mock.assert_called_with( + expected, + saltenv='base', + runas=None, + use_vt=False, + python_shell=False, + ) + + def test_install_extra_args_arguments_recursion_error(self): + pkg = 'pep8' + mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + + self.assertRaises(TypeError, lambda: pip.install( + pkg, extra_args=[ + {"--latest-pip-kwarg": ["param1", "param2"]}, + ])) + + self.assertRaises(TypeError, lambda: pip.install( + pkg, extra_args=[ + {"--latest-pip-kwarg": [{"--too-deep": dict()}]}, + ])) + def test_uninstall_multiple_requirements_arguments_in_resulting_command(self): with patch('salt.modules.pip._get_cached_requirements') as get_cached_requirements: cached_reqs = [ @@ -859,21 +940,54 @@ def test_uninstall_multiple_requirements_arguments_in_resulting_command(self): python_shell=False, ) - def test_uninstall_proxy_argument_in_resulting_command(self): + def test_uninstall_global_proxy_in_resulting_command(self): + ''' + Checking that there is proxy set if global proxy is set. + ''' pkg = 'pep8' - proxy = 'salt-user:salt-passwd@salt-proxy:3128' + proxy = 'http://salt-user:salt-passwd@salt-proxy:3128' mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + config_mock = {'proxy_host': 'salt-proxy', + 'proxy_port': '3128', + 'proxy_username': 'salt-user', + 'proxy_password': 'salt-passwd'} with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.uninstall(pkg, proxy=proxy) - expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', '--proxy', proxy, pkg] - mock.assert_called_with( - expected, - saltenv='base', - cwd=None, - runas=None, - use_vt=False, - python_shell=False, - ) + with patch.dict(pip.__opts__, config_mock): + pip.uninstall(pkg) + expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', '--proxy', proxy, pkg] + mock.assert_called_with( + expected, + saltenv='base', + cwd=None, + runas=None, + use_vt=False, + python_shell=False, + ) + + def test_uninstall_proxy_false_argument_in_resulting_command(self): + ''' + Checking that there is no proxy set if proxy arg is set to False + even if the global proxy is set. + ''' + pkg = 'pep8' + proxy = False + mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + config_mock = {'proxy_host': 'salt-proxy', + 'proxy_port': '3128', + 'proxy_username': 'salt-user', + 'proxy_password': 'salt-passwd'} + with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + with patch.dict(pip.__opts__, config_mock): + pip.uninstall(pkg, proxy=proxy) + expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', pkg] + mock.assert_called_with( + expected, + saltenv='base', + cwd=None, + runas=None, + use_vt=False, + python_shell=False, + ) def test_uninstall_log_argument_in_resulting_command(self): pkg = 'pep8' @@ -1280,8 +1394,7 @@ def test_is_installed_false(self): def test_install_pre_argument_in_resulting_command(self): pkg = 'pep8' - # Lower than 1.4 versions don't end-up with `--pre` in the resulting - # output + # Lower than 1.4 versions don't end up with `--pre` in the resulting output mock = MagicMock(side_effect=[ {'retcode': 0, 'stdout': 'pip 1.2.0 /path/to/site-packages/pip'}, {'retcode': 0, 'stdout': ''} diff --git a/tests/unit/modules/test_pkg_resource.py b/tests/unit/modules/test_pkg_resource.py index b6d90cc92c85..02ba11048abb 100644 --- a/tests/unit/modules/test_pkg_resource.py +++ b/tests/unit/modules/test_pkg_resource.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -24,7 +22,6 @@ from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) class PkgresTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.pkg_resource diff --git a/tests/unit/modules/test_pkgin.py b/tests/unit/modules/test_pkgin.py index 4cc7167d157b..729870dbae06 100644 --- a/tests/unit/modules/test_pkgin.py +++ b/tests/unit/modules/test_pkgin.py @@ -6,19 +6,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.pkgin as pkgin -@skipIf(NO_MOCK, NO_MOCK_REASON) class PkginTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.pkgin diff --git a/tests/unit/modules/test_pkgng.py b/tests/unit/modules/test_pkgng.py index ff458a08aaa0..2a95b0a9ac5a 100644 --- a/tests/unit/modules/test_pkgng.py +++ b/tests/unit/modules/test_pkgng.py @@ -6,10 +6,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -17,7 +15,6 @@ import salt.modules.pkgng as pkgng -@skipIf(NO_MOCK, NO_MOCK_REASON) class PkgNgTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.pkgng diff --git a/tests/unit/modules/test_pkgutil.py b/tests/unit/modules/test_pkgutil.py index 462b3c5c444a..68e736b2fbd1 100644 --- a/tests/unit/modules/test_pkgutil.py +++ b/tests/unit/modules/test_pkgutil.py @@ -8,13 +8,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( Mock, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -23,7 +21,6 @@ import salt.utils.pkg -@skipIf(NO_MOCK, NO_MOCK_REASON) class PkgutilTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.pkgutil diff --git a/tests/unit/modules/test_portage_config.py b/tests/unit/modules/test_portage_config.py index c9f642422a97..c056ca70f59d 100644 --- a/tests/unit/modules/test_portage_config.py +++ b/tests/unit/modules/test_portage_config.py @@ -10,17 +10,16 @@ import re # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch -from tests.support.paths import TMP +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch import salt.utils.files # Import salt libs import salt.modules.portage_config as portage_config -@skipIf(NO_MOCK, NO_MOCK_REASON) class PortageConfigTestCase(TestCase, LoaderModuleMockMixin): class DummyAtom(object): def __init__(self): @@ -95,7 +94,7 @@ def test_enforce_nice_config(self): ('use', ['apple', '-banana', 'ananas', 'orange']), ] - base_path = TMP + '/package.{0}' + base_path = RUNTIME_VARS.TMP + '/package.{0}' def make_line(atom, addition): return atom + (' ' + addition if addition != '' else '') + '\n' diff --git a/tests/unit/modules/test_postfix.py b/tests/unit/modules/test_postfix.py index 5eac52ae52bd..d4e261483feb 100644 --- a/tests/unit/modules/test_postfix.py +++ b/tests/unit/modules/test_postfix.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -19,7 +17,6 @@ import salt.modules.postfix as postfix -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostfixTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.postfix diff --git a/tests/unit/modules/test_postgres.py b/tests/unit/modules/test_postgres.py index 03fb7fddfd76..f5cc27fdf27b 100644 --- a/tests/unit/modules/test_postgres.py +++ b/tests/unit/modules/test_postgres.py @@ -7,8 +7,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, Mock, patch, call +from tests.support.unit import TestCase +from tests.support.mock import Mock, patch, call # Import salt libs import salt.modules.postgres as postgres @@ -51,7 +51,6 @@ ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): patcher = patch('salt.utils.path.which', Mock(return_value='/usr/bin/pgsql')) diff --git a/tests/unit/modules/test_poudriere.py b/tests/unit/modules/test_poudriere.py index f123f35f07cf..9873ced17b0e 100644 --- a/tests/unit/modules/test_poudriere.py +++ b/tests/unit/modules/test_poudriere.py @@ -9,20 +9,17 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, mock_open, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.poudriere as poudriere -@skipIf(NO_MOCK, NO_MOCK_REASON) class PoudriereTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.poudriere diff --git a/tests/unit/modules/test_powerpath.py b/tests/unit/modules/test_powerpath.py index d16a020b6d68..6636cf65a3b9 100644 --- a/tests/unit/modules/test_powerpath.py +++ b/tests/unit/modules/test_powerpath.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.modules.powerpath as powerpath -@skipIf(NO_MOCK, NO_MOCK_REASON) class PowerpathTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.powerpath diff --git a/tests/unit/modules/test_proxy.py b/tests/unit/modules/test_proxy.py index e85e2168a3af..b2fa42035920 100644 --- a/tests/unit/modules/test_proxy.py +++ b/tests/unit/modules/test_proxy.py @@ -8,17 +8,14 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch, call ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class ProxyTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.proxy diff --git a/tests/unit/modules/test_publish.py b/tests/unit/modules/test_publish.py index 4cb9827509af..82055757672f 100644 --- a/tests/unit/modules/test_publish.py +++ b/tests/unit/modules/test_publish.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -65,8 +63,13 @@ def send(self, load): def close(self): pass + def __enter__(self): + return self + + def __exit__(self, *args): + pass + -@skipIf(NO_MOCK, NO_MOCK_REASON) class PublishTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.publish diff --git a/tests/unit/modules/test_puppet.py b/tests/unit/modules/test_puppet.py index fda080908c9c..9ee4e921ba1e 100644 --- a/tests/unit/modules/test_puppet.py +++ b/tests/unit/modules/test_puppet.py @@ -10,13 +10,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( mock_open, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -26,7 +24,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class PuppetTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.puppet diff --git a/tests/unit/modules/test_purefb.py b/tests/unit/modules/test_purefb.py index e3d2a4e958e2..5a201f818cf7 100644 --- a/tests/unit/modules/test_purefb.py +++ b/tests/unit/modules/test_purefb.py @@ -8,18 +8,15 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.purefb as purefb -@skipIf(NO_MOCK, NO_MOCK_REASON) class PureFBTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.purefb diff --git a/tests/unit/modules/test_pw_group.py b/tests/unit/modules/test_pw_group.py index 5a67ed34d5e3..b65bc7d66a5a 100644 --- a/tests/unit/modules/test_pw_group.py +++ b/tests/unit/modules/test_pw_group.py @@ -12,8 +12,6 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ import salt.utils.platform -@skipIf(NO_MOCK, NO_MOCK_REASON) class PwGroupTestCase(TestCase, LoaderModuleMockMixin): ''' Test for salt.module.pw_group diff --git a/tests/unit/modules/test_pw_user.py b/tests/unit/modules/test_pw_user.py index 019e070f31ff..38ac4dcc4614 100644 --- a/tests/unit/modules/test_pw_user.py +++ b/tests/unit/modules/test_pw_user.py @@ -12,8 +12,6 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -27,7 +25,6 @@ @skipIf(not HAS_PWD, 'These tests can only run on systems with the python pwd module') -@skipIf(NO_MOCK, NO_MOCK_REASON) class PwUserTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.pw_user diff --git a/tests/unit/modules/test_pyenv.py b/tests/unit/modules/test_pyenv.py index 38d99e7f39ae..8cb003c8d6ad 100644 --- a/tests/unit/modules/test_pyenv.py +++ b/tests/unit/modules/test_pyenv.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.pyenv as pyenv -@skipIf(NO_MOCK, NO_MOCK_REASON) class PyenvTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.pyenv diff --git a/tests/unit/modules/test_qemu_img.py b/tests/unit/modules/test_qemu_img.py index dd14c9701b04..bc7e5ee9034a 100644 --- a/tests/unit/modules/test_qemu_img.py +++ b/tests/unit/modules/test_qemu_img.py @@ -9,19 +9,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.qemu_img as qemu_img -@skipIf(NO_MOCK, NO_MOCK_REASON) class QemuimgTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.qemu_img diff --git a/tests/unit/modules/test_qemu_nbd.py b/tests/unit/modules/test_qemu_nbd.py index 7a39a3f58b81..7bd07c1d6fd0 100644 --- a/tests/unit/modules/test_qemu_nbd.py +++ b/tests/unit/modules/test_qemu_nbd.py @@ -11,19 +11,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.qemu_nbd as qemu_nbd -@skipIf(NO_MOCK, NO_MOCK_REASON) class QemuNbdTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.qemu_nbd diff --git a/tests/unit/modules/test_rabbitmq.py b/tests/unit/modules/test_rabbitmq.py index 408f866c1d67..36c2f7839da0 100644 --- a/tests/unit/modules/test_rabbitmq.py +++ b/tests/unit/modules/test_rabbitmq.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class RabbitmqTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.rabbitmq diff --git a/tests/unit/modules/test_rbenv.py b/tests/unit/modules/test_rbenv.py index 32397f040b4e..528925198c5d 100644 --- a/tests/unit/modules/test_rbenv.py +++ b/tests/unit/modules/test_rbenv.py @@ -9,10 +9,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -20,7 +18,6 @@ import salt.modules.rbenv as rbenv -@skipIf(NO_MOCK, NO_MOCK_REASON) class RbenvTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.rbenv diff --git a/tests/unit/modules/test_redismod.py b/tests/unit/modules/test_redismod.py index 5888fe972cf7..3f3ade95200f 100644 --- a/tests/unit/modules/test_redismod.py +++ b/tests/unit/modules/test_redismod.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock) # Import Salt Libs @@ -266,7 +264,6 @@ def zrange(self, key, start, stop): return 'A' -@skipIf(NO_MOCK, NO_MOCK_REASON) class RedismodTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.redismod diff --git a/tests/unit/modules/test_restartcheck.py b/tests/unit/modules/test_restartcheck.py index 7e054cc1b09d..f7b4417f72ce 100644 --- a/tests/unit/modules/test_restartcheck.py +++ b/tests/unit/modules/test_restartcheck.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libsrestartcheck @@ -22,7 +20,6 @@ # from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class RestartcheckTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.restartcheck diff --git a/tests/unit/modules/test_ret.py b/tests/unit/modules/test_ret.py index 690333bcff07..7103ca045930 100644 --- a/tests/unit/modules/test_ret.py +++ b/tests/unit/modules/test_ret.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ import salt.loader -@skipIf(NO_MOCK, NO_MOCK_REASON) class RetTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.ret diff --git a/tests/unit/modules/test_rh_ip.py b/tests/unit/modules/test_rh_ip.py index 78694de51c19..b1c6583cff57 100644 --- a/tests/unit/modules/test_rh_ip.py +++ b/tests/unit/modules/test_rh_ip.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -24,7 +22,6 @@ from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) class RhipTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.rh_ip @@ -32,6 +29,18 @@ class RhipTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return {rh_ip: {}} + def test_error_message_iface_should_process_non_str_expected(self): + values = [1, True, False, 'no-kaboom'] + iface = 'ethtest' + option = 'test' + msg = rh_ip._error_msg_iface(iface, option, values) + self.assertTrue(msg.endswith('[1|True|False|no-kaboom]'), msg) + + def test_error_message_network_should_process_non_str_expected(self): + values = [1, True, False, 'no-kaboom'] + msg = rh_ip._error_msg_network('fnord', values) + self.assertTrue(msg.endswith('[1|True|False|no-kaboom]'), msg) + def test_build_bond(self): ''' Test to create a bond script in /etc/modprobe.d with the passed diff --git a/tests/unit/modules/test_rh_service.py b/tests/unit/modules/test_rh_service.py index b9699ac8ff56..dab0e9d9e2c4 100644 --- a/tests/unit/modules/test_rh_service.py +++ b/tests/unit/modules/test_rh_service.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -24,7 +22,6 @@ 'salt-api', 'salt-master', 'salt-minion'] -@skipIf(NO_MOCK, NO_MOCK_REASON) class RhServiceTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.rh_service diff --git a/tests/unit/modules/test_riak.py b/tests/unit/modules/test_riak.py index 63f40ba1f5ed..b56448d2d8ea 100644 --- a/tests/unit/modules/test_riak.py +++ b/tests/unit/modules/test_riak.py @@ -7,17 +7,14 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, patch) # Import Salt Libs import salt.modules.riak as riak -@skipIf(NO_MOCK, NO_MOCK_REASON) class RiakTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.riak diff --git a/tests/unit/modules/test_rpm_lowpkg.py b/tests/unit/modules/test_rpm_lowpkg.py index 0a2359ccb2f5..527c8d3bf8c5 100644 --- a/tests/unit/modules/test_rpm_lowpkg.py +++ b/tests/unit/modules/test_rpm_lowpkg.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.rpm_lowpkg as rpm -@skipIf(NO_MOCK, NO_MOCK_REASON) class RpmTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.rpm diff --git a/tests/unit/modules/test_rsync.py b/tests/unit/modules/test_rsync.py index 7ff967dacfc6..73527369344a 100644 --- a/tests/unit/modules/test_rsync.py +++ b/tests/unit/modules/test_rsync.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -19,7 +17,6 @@ from salt.exceptions import CommandExecutionError, SaltInvocationError -@skipIf(NO_MOCK, NO_MOCK_REASON) class RsyncTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.rsync diff --git a/tests/unit/modules/test_rvm.py b/tests/unit/modules/test_rvm.py index 9ac417f2d060..811db3a95de9 100644 --- a/tests/unit/modules/test_rvm.py +++ b/tests/unit/modules/test_rvm.py @@ -5,14 +5,13 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch, call +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch, call # Import salt libs import salt.modules.rvm as rvm -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestRvmModule(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_s3.py b/tests/unit/modules/test_s3.py index 470bfbd92aab..83316075a77d 100644 --- a/tests/unit/modules/test_s3.py +++ b/tests/unit/modules/test_s3.py @@ -7,11 +7,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -19,7 +17,6 @@ import salt.modules.s3 as s3 -@skipIf(NO_MOCK, NO_MOCK_REASON) class S3TestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_s6.py b/tests/unit/modules/test_s6.py index f5174ffec389..fbd1fc937aed 100644 --- a/tests/unit/modules/test_s6.py +++ b/tests/unit/modules/test_s6.py @@ -9,19 +9,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.s6 as s6 -@skipIf(NO_MOCK, NO_MOCK_REASON) class S6TestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.s6 diff --git a/tests/unit/modules/test_salt_version.py b/tests/unit/modules/test_salt_version.py new file mode 100644 index 000000000000..5bfbc5309a9b --- /dev/null +++ b/tests/unit/modules/test_salt_version.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- +''' +Unit tests for salt/modules/salt_version.py +''' + +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Testing libs +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch + +# Import Salt libs +from salt.ext import six +import salt.modules.salt_version as salt_version +import salt.version + + +class SaltVersionTestCase(TestCase): + ''' + Test cases for salt.modules.salt_version + ''' + + def test_mocked_objects(self): + ''' + Test that the mocked objects actually have what we expect. + + For example, earlier tests incorrectly mocked the + salt.version.SaltStackVersion.LNAMES dict using upper-case indexes + ''' + assert isinstance(salt.version.SaltStackVersion.LNAMES, dict) + for k, v in salt.version.SaltStackVersion.LNAMES.items(): + assert k == k.lower() + assert isinstance(v, tuple) + assert len(v) == 2 + + sv = salt.version.SaltStackVersion(*salt.version.__version_info__).__str__() + assert isinstance(sv, six.string_types) + + with patch('salt.version.SaltStackVersion.LNAMES', {'neon': (2019, 8)}): + sv = salt.version.SaltStackVersion.from_name('Neon') + self.assertEqual(sv.string, '2019.8.0') + + # get_release_number tests: 3 + + def test_get_release_number_no_codename(self): + ''' + Test that None is returned when the codename isn't found. + ''' + assert salt_version.get_release_number('foo') is None + + @patch('salt.version.SaltStackVersion.LNAMES', {'foo': (12345, 0)}) + def test_get_release_number_unassigned(self): + ''' + Test that a string is returned when a version is found, but unassigned. + ''' + mock_str = 'No version assigned.' + assert salt_version.get_release_number('foo') == mock_str + + def test_get_release_number_success(self): + ''' + Test that a version is returned for a released codename + ''' + assert salt_version.get_release_number('Oxygen') == '2018.3' + + # equal tests: 3 + + @patch('salt.version.SaltStackVersion.LNAMES', {'foo': (1900, 5)}) + @patch('salt.version.SaltStackVersion', MagicMock(return_value='1900.5.0')) + def test_equal_success(self): + ''' + Test that the current version is equal to the codename + ''' + assert salt_version.equal('foo') is True + + @patch('salt.version.SaltStackVersion.LNAMES', {'oxygen': (2018, 3), + 'nitrogen': (2017, 7)}) + @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + def test_equal_older_codename(self): + ''' + Test that when an older codename is passed in, the function returns False. + ''' + assert salt_version.equal('Nitrogen') is False + + @patch('salt.version.SaltStackVersion.LNAMES', {'fluorine': (salt.version.MAX_SIZE - 100, 0)}) + @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + def test_equal_newer_codename(self): + ''' + Test that when a newer codename is passed in, the function returns False + ''' + assert salt_version.equal('Fluorine') is False + + # greater_than tests: 4 + + @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='2017.7')) + @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + def test_greater_than_success(self): + ''' + Test that the current version is newer than the codename + ''' + assert salt_version.greater_than('Nitrogen') is True + + @patch('salt.version.SaltStackVersion.LNAMES', {'oxygen': (2018, 3)}) + @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + def test_greater_than_with_equal_codename(self): + ''' + Test that when an equal codename is passed in, the function returns False. + ''' + assert salt_version.greater_than('Oxygen') is False + + @patch('salt.version.SaltStackVersion.LNAMES', {'fluorine': (2019, 2), + 'oxygen': (2018, 3)}) + @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + def test_greater_than_with_newer_codename(self): + ''' + Test that when a newer codename is passed in, the function returns False. + ''' + assert salt_version.greater_than('Fluorine') is False + + @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='No version assigned.')) + @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + def test_greater_than_unassigned(self): + ''' + Test that the unassigned codename is greater than the current version + ''' + assert salt_version.greater_than('Fluorine') is False + + # less_than tests: 4 + + @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='2019.2')) + @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + def test_less_than_success(self): + ''' + Test that when a newer codename is passed in, the function returns True. + ''' + assert salt_version.less_than('Fluorine') is True + + @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch('salt.version.SaltStackVersion.LNAMES', {'oxygen': (2018, 3)}) + def test_less_than_with_equal_codename(self): + ''' + Test that when an equal codename is passed in, the function returns False. + ''' + assert salt_version.less_than('Oxygen') is False + + @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='2017.7')) + @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + def test_less_than_with_older_codename(self): + ''' + Test that the current version is less than the codename. + ''' + assert salt_version.less_than('Nitrogen') is False + + @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='No version assigned.')) + @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + def test_less_than_with_unassigned_codename(self): + ''' + Test that when an unassigned codename greater than the current version. + ''' + assert salt_version.less_than('Fluorine') is True + + # _check_release_cmp tests: 2 + + def test_check_release_cmp_no_codename(self): + ''' + Test that None is returned when the codename isn't found. + ''' + assert salt_version._check_release_cmp('foo') is None + + def test_check_release_cmp_success(self): + ''' + Test that an int is returned from the version compare + ''' + assert isinstance(salt_version._check_release_cmp('Oxygen'), int) diff --git a/tests/unit/modules/test_saltcheck.py b/tests/unit/modules/test_saltcheck.py index da0d41c6542d..8b41a8e26bba 100644 --- a/tests/unit/modules/test_saltcheck.py +++ b/tests/unit/modules/test_saltcheck.py @@ -15,18 +15,15 @@ # Import Salt Testing Libs try: from tests.support.mixins import LoaderModuleMockMixin - from tests.support.unit import skipIf, TestCase + from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) except: raise -@skipIf(NO_MOCK, NO_MOCK_REASON) class LinuxSysctlTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.saltcheck module diff --git a/tests/unit/modules/test_saltcloudmod.py b/tests/unit/modules/test_saltcloudmod.py index 3e480101fab4..823ba1c27d53 100644 --- a/tests/unit/modules/test_saltcloudmod.py +++ b/tests/unit/modules/test_saltcloudmod.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ import salt.utils.json -@skipIf(NO_MOCK, NO_MOCK_REASON) class SaltcloudmodTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.saltcloudmod diff --git a/tests/unit/modules/test_schedule.py b/tests/unit/modules/test_schedule.py index 9fb01e94ec7d..884f25766892 100644 --- a/tests/unit/modules/test_schedule.py +++ b/tests/unit/modules/test_schedule.py @@ -8,31 +8,31 @@ import os # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.schedule as schedule from salt.utils.event import SaltEvent -SOCK_DIR = os.path.join(TMP, 'test-socks') - JOB1 = {'function': 'test.ping', 'maxrunning': 1, 'name': 'job1', 'jid_include': True, 'enabled': True} -@skipIf(NO_MOCK, NO_MOCK_REASON) class ScheduleTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.schedule ''' + + @classmethod + def setUpClass(cls): + cls.sock_dir = os.path.join(RUNTIME_VARS.TMP, 'test-socks') + def setup_loader_modules(self): return {schedule: {}} @@ -42,7 +42,7 @@ def test_purge(self): ''' Test if it purge all the jobs currently scheduled on the minion. ''' - with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': SOCK_DIR}): + with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(schedule.__salt__, {'event.fire': mock}): _ret_value = {'complete': True, 'schedule': {}} @@ -56,7 +56,7 @@ def test_delete(self): ''' Test if it delete a job from the minion's schedule. ''' - with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': SOCK_DIR}): + with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(schedule.__salt__, {'event.fire': mock}): _ret_value = {'complete': True, 'schedule': {}} @@ -119,7 +119,7 @@ def test_add(self): 'or "days" with "when" or "cron" options.' comm3 = 'Unable to use "when" and "cron" options together. Ignoring.' comm4 = 'Job: job2 would be added to schedule.' - with patch.dict(schedule.__opts__, {'schedule': {'job1': 'salt'}, 'sock_dir': SOCK_DIR}): + with patch.dict(schedule.__opts__, {'schedule': {'job1': 'salt'}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(schedule.__salt__, {'event.fire': mock}): _ret_value = {'complete': True, 'schedule': {'job1': {'salt': 'salt'}}} @@ -150,7 +150,7 @@ def test_run_job(self): ''' Test if it run a scheduled job on the minion immediately. ''' - with patch.dict(schedule.__opts__, {'schedule': {'job1': JOB1}, 'sock_dir': SOCK_DIR}): + with patch.dict(schedule.__opts__, {'schedule': {'job1': JOB1}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(schedule.__salt__, {'event.fire': mock}): _ret_value = {'complete': True, 'schedule': {'job1': JOB1}} @@ -165,7 +165,7 @@ def test_enable_job(self): ''' Test if it enable a job in the minion's schedule. ''' - with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': SOCK_DIR}): + with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(schedule.__salt__, {'event.fire': mock}): _ret_value = {'complete': True, 'schedule': {}} @@ -180,7 +180,7 @@ def test_disable_job(self): ''' Test if it disable a job in the minion's schedule. ''' - with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': SOCK_DIR}): + with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(schedule.__salt__, {'event.fire': mock}): _ret_value = {'complete': True, 'schedule': {}} @@ -196,9 +196,9 @@ def test_save(self): Test if it save all scheduled jobs on the minion. ''' comm1 = 'Schedule (non-pillar items) saved.' - with patch.dict(schedule.__opts__, {'config_dir': '', 'schedule': {}, + with patch.dict(schedule.__opts__, {'schedule': {}, 'default_include': '/tmp', - 'sock_dir': SOCK_DIR}): + 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(schedule.__salt__, {'event.fire': mock}): @@ -236,7 +236,7 @@ def test_move(self): comm1 = 'no servers answered the published schedule.add command' comm2 = 'the following minions return False' comm3 = 'Moved Job job1 from schedule.' - with patch.dict(schedule.__opts__, {'schedule': {'job1': JOB1}, 'sock_dir': SOCK_DIR}): + with patch.dict(schedule.__opts__, {'schedule': {'job1': JOB1}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(schedule.__salt__, {'event.fire': mock}): _ret_value = {'complete': True, 'schedule': {'job1': JOB1}} @@ -266,7 +266,7 @@ def test_move(self): 'result': False}) mock = MagicMock(side_effect=[{}, {'job1': {}}]) - with patch.dict(schedule.__opts__, {'schedule': mock, 'sock_dir': SOCK_DIR}): + with patch.dict(schedule.__opts__, {'schedule': mock, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(schedule.__salt__, {'event.fire': mock}): _ret_value = {'complete': True, 'schedule': {'job1': JOB1}} @@ -303,7 +303,7 @@ def test_copy(self): comm1 = 'no servers answered the published schedule.add command' comm2 = 'the following minions return False' comm3 = 'Copied Job job1 from schedule to minion(s).' - with patch.dict(schedule.__opts__, {'schedule': {'job1': JOB1}, 'sock_dir': SOCK_DIR}): + with patch.dict(schedule.__opts__, {'schedule': {'job1': JOB1}, 'sock_dir': self.sock_dir}): mock = MagicMock(return_value=True) with patch.dict(schedule.__salt__, {'event.fire': mock}): _ret_value = {'complete': True, 'schedule': {'job1': {'job1': JOB1}}} @@ -333,7 +333,7 @@ def test_copy(self): 'result': False}) mock = MagicMock(side_effect=[{}, {'job1': {}}]) - with patch.dict(schedule.__opts__, {'schedule': mock, 'sock_dir': SOCK_DIR}): + with patch.dict(schedule.__opts__, {'schedule': mock, 'sock_dir': self.sock_dir}): with patch.dict(schedule.__pillar__, {'schedule': {'job1': JOB1}}): mock = MagicMock(return_value=True) with patch.dict(schedule.__salt__, {'event.fire': mock}): diff --git a/tests/unit/modules/test_scsi.py b/tests/unit/modules/test_scsi.py index 0004ce438858..8f2186b57b05 100644 --- a/tests/unit/modules/test_scsi.py +++ b/tests/unit/modules/test_scsi.py @@ -9,10 +9,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -22,7 +20,6 @@ import salt.utils.path -@skipIf(NO_MOCK, NO_MOCK_REASON) class ScsiTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.scsi diff --git a/tests/unit/modules/test_sdb.py b/tests/unit/modules/test_sdb.py index 6c51f45ef33f..6f7bb2d530b9 100644 --- a/tests/unit/modules/test_sdb.py +++ b/tests/unit/modules/test_sdb.py @@ -8,17 +8,12 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON -) +from tests.support.unit import TestCase # Import Salt Libs import salt.modules.sdb as sdb -@skipIf(NO_MOCK, NO_MOCK_REASON) class SdbTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.sdb diff --git a/tests/unit/modules/test_seed.py b/tests/unit/modules/test_seed.py index c9246412f8b2..9e66ae8ec18c 100644 --- a/tests/unit/modules/test_seed.py +++ b/tests/unit/modules/test_seed.py @@ -10,10 +10,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -24,7 +22,6 @@ import salt.modules.seed as seed -@skipIf(NO_MOCK, NO_MOCK_REASON) class SeedTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.seed diff --git a/tests/unit/modules/test_selinux.py b/tests/unit/modules/test_selinux.py index a9ea0c9c25d8..00da3a648d63 100644 --- a/tests/unit/modules/test_selinux.py +++ b/tests/unit/modules/test_selinux.py @@ -3,12 +3,10 @@ # Import Salt Testing Libs from __future__ import absolute_import from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt libs @@ -16,7 +14,6 @@ import salt.modules.selinux as selinux -@skipIf(NO_MOCK, NO_MOCK_REASON) class SelinuxModuleTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.selinux diff --git a/tests/unit/modules/test_sensors.py b/tests/unit/modules/test_sensors.py index 5db8d53482a1..8d965b342635 100644 --- a/tests/unit/modules/test_sensors.py +++ b/tests/unit/modules/test_sensors.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.modules.sensors as sensors -@skipIf(NO_MOCK, NO_MOCK_REASON) class SensorTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.sensors diff --git a/tests/unit/modules/test_serverdensity_device.py b/tests/unit/modules/test_serverdensity_device.py index 9ab1e7849b67..edb9d90e9bdf 100644 --- a/tests/unit/modules/test_serverdensity_device.py +++ b/tests/unit/modules/test_serverdensity_device.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -74,7 +72,6 @@ def put(self, url, data=None, **kwargs): return self.return_request(url, data, **kwargs) -@skipIf(NO_MOCK, NO_MOCK_REASON) class ServerdensityDeviceTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.serverdensity_device diff --git a/tests/unit/modules/test_service.py b/tests/unit/modules/test_service.py index a182eade5838..5d46f3619114 100644 --- a/tests/unit/modules/test_service.py +++ b/tests/unit/modules/test_service.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -19,7 +17,6 @@ import salt.modules.service as service -@skipIf(NO_MOCK, NO_MOCK_REASON) class ServiceTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.service diff --git a/tests/unit/modules/test_servicenow.py b/tests/unit/modules/test_servicenow.py index 5853b2676338..5bb8b4e2dce2 100644 --- a/tests/unit/modules/test_servicenow.py +++ b/tests/unit/modules/test_servicenow.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON ) import salt.modules.servicenow as servicenow @@ -26,7 +24,6 @@ def get(self, query): 'query_value': query}] -@skipIf(NO_MOCK, NO_MOCK_REASON) class ServiceNowModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): module_globals = { diff --git a/tests/unit/modules/test_smartos_imgadm.py b/tests/unit/modules/test_smartos_imgadm.py new file mode 100644 index 000000000000..a2bbc183227f --- /dev/null +++ b/tests/unit/modules/test_smartos_imgadm.py @@ -0,0 +1,276 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: Jorge Schrauwen +''' + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Libs +import salt.modules.smartos_imgadm as imgadm +from salt.modules.smartos_imgadm import _parse_image_meta + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase + +image_orphan = { + "manifest": { + "uuid": "07f360fd-12d5-e624-a279-eb8a15b630f6" + }, + "zpool": "zones", + "cloneNames": [], + "clones": 0 +} +image_native = { + "manifest": { + "v": 2, + "uuid": "9d91e334-3bcf-11e8-bb0b-e7b49eb01e38", + "owner": "00000000-0000-0000-0000-000000000000", + "name": "pkgbuild", + "version": "18.1.0", + "state": "active", + "disabled": False, + "public": True, + "published_at": "2018-04-09T08:25:52Z", + "type": "zone-dataset", + "os": "smartos", + "files": [ + { + "sha1": "5efaf95b7f226eb09c7d5e6c3734f8aa654b811d", + "size": 465411979, + "compression": "gzip" + } + ], + "description": "A SmartOS image pre-configured for building pkgsrc packages.", + "homepage": "https://docs.joyent.com/images/smartos/pkgbuild", + "urn": "sdc:sdc:pkgbuild:18.1.0", + "requirements": { + "min_platform": { + "7.0": "20141030T081701Z" + }, + "networks": [ + { + "name": "net0", + "description": "public" + } + ] + }, + "tags": { + "role": "os", + "group": "pkgbuild" + } + }, + "zpool": "zones", + "source": "https://images.joyent.com", + "cloneNames": [ + "zones/dda70f61-70fe-65e7-cf70-d878d69442d4" + ], + "clones": 1 +} +image_lx = { + "manifest": { + "v": 2, + "uuid": "05140a7e-279f-11e6-aedf-47d4f69d2887", + "owner": "00000000-0000-0000-0000-000000000000", + "name": "ubuntu-16.04", + "version": "20160601", + "state": "active", + "disabled": False, + "public": True, + "published_at": "2016-06-01T02:17:41Z", + "type": "lx-dataset", + "os": "linux", + "files": [ + { + "sha1": "d342f137c5ccef0702ec479acb63c196cf81b38a", + "size": 134969110, + "compression": "gzip" + } + ], + "description": "Container-native Ubuntu 16.04 64-bit image. Built to run on containers with bare metal speed, while offering all the services of a typical unix host.", + "homepage": "https://docs.joyent.com/images/container-native-linux", + "requirements": { + "networks": [ + { + "name": "net0", + "description": "public" + } + ], + "min_platform": { + "7.0": "20160225T122859Z" + }, + "brand": "lx" + }, + "tags": { + "role": "os", + "kernel_version": "4.3.0" + } + }, + "zpool": "zones", + "source": "https://images.joyent.com", + "cloneNames": [ + "zones/e4c1f6b5-4429-e6c2-ae2a-d6aa58bdeebb" + ], + "clones": 1 +} +image_zvol = { + "manifest": { + "v": 2, + "uuid": "ac99517a-72ac-44c0-90e6-c7ce3d944a0a", + "owner": "00000000-0000-0000-0000-000000000000", + "name": "ubuntu-certified-18.04", + "version": "20180808", + "state": "active", + "disabled": False, + "public": True, + "published_at": "2018-10-11T12:45:24.804Z", + "type": "zvol", + "os": "linux", + "files": [ + { + "sha1": "9f7704969507bd97e160a8f42a3631487644e457", + "size": 372276887, + "compression": "gzip" + } + ], + "description": "Ubuntu 18.04 LTS (20180808 64-bit). Certified Ubuntu Server Cloud Image from Canonical. For kvm and bhyve.", + "homepage": "https://docs.joyent.com/images/linux/ubuntu-certified", + "requirements": { + "min_platform": { + "7.0": "20150929T232348Z" + }, + "networks": [ + { + "name": "net0", + "description": "public" + } + ], + "ssh_key": True + }, + "nic_driver": "virtio", + "disk_driver": "virtio", + "cpu_type": "host", + "image_size": 10240, + "tags": { + "default_user": "ubuntu", + "role": "os" + } + }, + "zpool": "zones", + "source": "https://images.joyent.com", + "cloneNames": [], + "clones": 0 +} +image_docker = { + "manifest": { + "v": 2, + "uuid": "4a3db8cb-0e94-ae23-588c-ee7934088927", + "owner": "00000000-0000-0000-0000-000000000000", + "name": "docker-layer", + "version": "62487cf6a7f6", + "disabled": False, + "public": True, + "published_at": "2019-03-23T01:32:25.320Z", + "type": "docker", + "os": "linux", + "description": "/bin/sh -c #(nop) CMD [\"/bin/bash\" \"/opt/start.sh\" \"-bash\"]", + "tags": { + "docker:repo": "busybox42/zimbra-docker-centos", + "docker:id": "sha256:62487cf6a7f698af4edc20707e14b1b3bba13b98bea3375f05af04859a30b222", + "docker:architecture": "amd64", + "docker:tag:latest": True, + "docker:config": { + "Cmd": [ + "/bin/bash", + "/opt/start.sh", + "-bash" + ], + "Entrypoint": None, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + ], + "WorkingDir": "" + } + }, + "origin": "2f0c529b-7bab-28d1-ff34-bdc9281b7a4b" + }, + "zpool": "zones", + "source": "https://docker.io", + "cloneNames": [], + "clones": 0 +} + + +class ImgadmTestCase(TestCase, LoaderModuleMockMixin): + ''' + TestCase for salt.modules.smartos_imgadm module + ''' + + def setup_loader_modules(self): + return {imgadm: {}} + + def test_parse_image_meta_orphan(self): + ''' + Test the internal _parse_image_meta methode + Feed it an 'orphan' image as we get it from from imgadm list -j + ''' + ret = {'Error': 'This looks like an orphaned image, image payload was invalid.'} + self.assertEqual(_parse_image_meta(image_orphan, True), ret) + + def test_parse_image_meta_native(self): + ''' + Test the internal _parse_image_meta methode + Feed it an 'native' image as we get it from from imgadm list -j + ''' + ret = {'description': 'A SmartOS image pre-configured for building pkgsrc packages.', + 'name': 'pkgbuild', + 'os': 'smartos', + 'published': '2018-04-09T08:25:52Z', + 'source': 'https://images.joyent.com', + 'version': '18.1.0'} + self.assertEqual(_parse_image_meta(image_native, True), ret) + + def test_parse_image_meta_lx(self): + ''' + Test the internal _parse_image_meta methode + Feed it an 'lx' image as we get it from from imgadm list -j + ''' + ret = {'description': 'Container-native Ubuntu 16.04 64-bit image. Built to run on ' + 'containers with bare metal speed, while offering all the ' + 'services of a typical unix host.', + 'name': 'ubuntu-16.04', + 'os': 'linux', + 'published': '2016-06-01T02:17:41Z', + 'source': 'https://images.joyent.com', + 'version': '20160601'} + self.assertEqual(_parse_image_meta(image_lx, True), ret) + + def test_parse_image_meta_zvol(self): + ''' + Test the internal _parse_image_meta methode + Feed it an 'zvol' image as we get it from from imgadm list -j + ''' + ret = {'description': 'Ubuntu 18.04 LTS (20180808 64-bit). Certified Ubuntu Server ' + 'Cloud Image from Canonical. For kvm and bhyve.', + 'name': 'ubuntu-certified-18.04', + 'os': 'linux', + 'published': '2018-10-11T12:45:24.804Z', + 'source': 'https://images.joyent.com', + 'version': '20180808'} + self.assertEqual(_parse_image_meta(image_zvol, True), ret) + + def test_parse_image_meta_docker(self): + ''' + Test the internal _parse_image_meta methode + Feed it an 'docker' image as we get it from from imgadm list -j + ''' + ret = {'description': 'Docker image imported from ' + 'busybox42/zimbra-docker-centos:latest on ' + '2019-03-23T01:32:25.320Z.', + 'name': 'busybox42/zimbra-docker-centos:latest', + 'os': 'linux', + 'published': '2019-03-23T01:32:25.320Z', + 'source': 'https://docker.io', + 'version': '62487cf6a7f6'} + self.assertEqual(_parse_image_meta(image_docker, True), ret) diff --git a/tests/unit/modules/test_smf_service.py b/tests/unit/modules/test_smf_service.py index 115a4a6049f5..ceb41efe3d65 100644 --- a/tests/unit/modules/test_smf_service.py +++ b/tests/unit/modules/test_smf_service.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.modules.smf_service as smf -@skipIf(NO_MOCK, NO_MOCK_REASON) class SmfTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.smf diff --git a/tests/unit/modules/test_smtp.py b/tests/unit/modules/test_smtp.py index 473d9ed94c72..e49b0cb06202 100644 --- a/tests/unit/modules/test_smtp.py +++ b/tests/unit/modules/test_smtp.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -221,7 +219,6 @@ def SMTP(self, server): return MockSMTP('server') -@skipIf(NO_MOCK, NO_MOCK_REASON) class SmtpTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.smtp diff --git a/tests/unit/modules/test_snapper.py b/tests/unit/modules/test_snapper.py index 67fa8dca3ae6..5bbe62f576ea 100644 --- a/tests/unit/modules/test_snapper.py +++ b/tests/unit/modules/test_snapper.py @@ -14,8 +14,6 @@ from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch, mock_open, @@ -142,7 +140,6 @@ @skipIf(sys.platform.startswith('win'), 'Snapper not available on Windows') -@skipIf(NO_MOCK, NO_MOCK_REASON) class SnapperTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_solarisipspkg.py b/tests/unit/modules/test_solarisipspkg.py index c80ca953d951..5bb6d6016615 100644 --- a/tests/unit/modules/test_solarisipspkg.py +++ b/tests/unit/modules/test_solarisipspkg.py @@ -10,8 +10,6 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt libs @@ -20,14 +18,13 @@ import salt.utils.data -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(sys.platform != 'solaris', 'Skip when not running on Solaris') class IpsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.solarisips ''' def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( opts, whitelist=['pkg', 'path', 'platform']) diff --git a/tests/unit/modules/test_solr.py b/tests/unit/modules/test_solr.py index 0f43700a9936..003cdd976383 100644 --- a/tests/unit/modules/test_solr.py +++ b/tests/unit/modules/test_solr.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -19,7 +17,6 @@ import salt.modules.solr as solr -@skipIf(NO_MOCK, NO_MOCK_REASON) class SolrTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.solr diff --git a/tests/unit/modules/test_sqlite3.py b/tests/unit/modules/test_sqlite3.py index 3bd3118c6473..c9743e7d539a 100644 --- a/tests/unit/modules/test_sqlite3.py +++ b/tests/unit/modules/test_sqlite3.py @@ -8,11 +8,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON -) +from tests.support.unit import TestCase # Import Salt Libs import salt.modules.sqlite3 as sqlite3 @@ -59,7 +55,6 @@ def fetchall(): return True -@skipIf(NO_MOCK, NO_MOCK_REASON) class Sqlite3TestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.sqlite3 diff --git a/tests/unit/modules/test_ssh.py b/tests/unit/modules/test_ssh.py index 6cd022f0ea07..1bbe7f6d6087 100644 --- a/tests/unit/modules/test_ssh.py +++ b/tests/unit/modules/test_ssh.py @@ -6,11 +6,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -21,7 +19,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class SSHAuthKeyTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.ssh diff --git a/tests/unit/modules/test_state.py b/tests/unit/modules/test_state.py index 0d15458be0a9..e717a5a0a71e 100644 --- a/tests/unit/modules/test_state.py +++ b/tests/unit/modules/test_state.py @@ -13,16 +13,14 @@ import time # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP, TMP_CONF_DIR -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( Mock, MagicMock, patch, mock_open, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -336,7 +334,6 @@ def close(): return True -@skipIf(NO_MOCK, NO_MOCK_REASON) class StateTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.modules.state @@ -344,7 +341,7 @@ class StateTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): utils = salt.loader.utils( - salt.config.DEFAULT_MINION_OPTS, + salt.config.DEFAULT_MINION_OPTS.copy(), whitelist=['state', 'args', 'systemd', 'path', 'platform'] ) utils.keys() @@ -1321,7 +1318,7 @@ def setup_loader_modules(self): return { state: { '__opts__': salt.config.minion_config( - os.path.join(TMP_CONF_DIR, 'minion') + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion') ), '__salt__': { 'saltutil.is_running': MagicMock(return_value=[]), @@ -1330,8 +1327,8 @@ def setup_loader_modules(self): } def setUp(self): - self.cachedir = tempfile.mkdtemp(dir=TMP) - self.fileserver_root = tempfile.mkdtemp(dir=TMP) + self.cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + self.fileserver_root = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.cachedir, ignore_errors=True) self.addCleanup(shutil.rmtree, self.fileserver_root, ignore_errors=True) diff --git a/tests/unit/modules/test_supervisord.py b/tests/unit/modules/test_supervisord.py index 4e61307ee9ba..05a1bdc5048f 100644 --- a/tests/unit/modules/test_supervisord.py +++ b/tests/unit/modules/test_supervisord.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class SupervisordTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.supervisord diff --git a/tests/unit/modules/test_svn.py b/tests/unit/modules/test_svn.py index 5f94507b85f7..a9c1796f81d5 100644 --- a/tests/unit/modules/test_svn.py +++ b/tests/unit/modules/test_svn.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.modules.svn as svn -@skipIf(NO_MOCK, NO_MOCK_REASON) class SvnTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.svn diff --git a/tests/unit/modules/test_swift.py b/tests/unit/modules/test_swift.py index d8e78e061a46..10b944555ea1 100644 --- a/tests/unit/modules/test_swift.py +++ b/tests/unit/modules/test_swift.py @@ -7,19 +7,16 @@ from __future__ import absolute_import, unicode_literals, print_function # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.swift as swift -@skipIf(NO_MOCK, NO_MOCK_REASON) class SwiftTestCase(TestCase): ''' Test cases for salt.modules.swift diff --git a/tests/unit/modules/test_sysbench.py b/tests/unit/modules/test_sysbench.py index d0a82636aea0..8d6184de886c 100644 --- a/tests/unit/modules/test_sysbench.py +++ b/tests/unit/modules/test_sysbench.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.modules.sysbench as sysbench -@skipIf(NO_MOCK, NO_MOCK_REASON) class SysbenchTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases to salt.modules.sysbench diff --git a/tests/unit/modules/test_syslog_ng.py b/tests/unit/modules/test_syslog_ng.py index e277ecb25f49..a68a32546b76 100644 --- a/tests/unit/modules/test_syslog_ng.py +++ b/tests/unit/modules/test_syslog_ng.py @@ -11,7 +11,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch # Import Salt libs import salt.utils.platform @@ -56,7 +56,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): # pylint: disable=blacklisted-function diff --git a/tests/unit/modules/test_sysmod.py b/tests/unit/modules/test_sysmod.py index 4fd86bcef4d4..0b02ca490726 100644 --- a/tests/unit/modules/test_sysmod.py +++ b/tests/unit/modules/test_sysmod.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -80,7 +78,6 @@ def render(self, opts, lst): return sysmod.__salt__ # renderers do not have '.'s; but whatever. This is for convenience -@skipIf(NO_MOCK, NO_MOCK_REASON) class SysmodTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.sysmod diff --git a/tests/unit/modules/test_system.py b/tests/unit/modules/test_system.py index af32b3cae874..3c21c125a59d 100644 --- a/tests/unit/modules/test_system.py +++ b/tests/unit/modules/test_system.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.modules.system as system -@skipIf(NO_MOCK, NO_MOCK_REASON) class SystemTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.system @@ -72,6 +69,46 @@ def test_shutdown(self): ''' Test to shutdown a running system ''' - with patch.dict(system.__salt__, - {'cmd.run': MagicMock(return_value='A')}): + cmd_mock = MagicMock(return_value='A') + with patch.dict(system.__salt__, {'cmd.run': cmd_mock}), \ + patch('salt.utils.platform.is_freebsd', MagicMock(return_value=False)), \ + patch('salt.utils.platform.is_netbsd', MagicMock(return_value=False)), \ + patch('salt.utils.platform.is_openbsd', MagicMock(return_value=False)): + self.assertEqual(system.shutdown(), 'A') + cmd_mock.assert_called_with(['shutdown', '-h', 'now'], python_shell=False) + + def test_shutdown_freebsd(self): + ''' + Test to shutdown a running FreeBSD system + ''' + cmd_mock = MagicMock(return_value='A') + with patch.dict(system.__salt__, {'cmd.run': cmd_mock}), \ + patch('salt.utils.platform.is_freebsd', MagicMock(return_value=True)), \ + patch('salt.utils.platform.is_netbsd', MagicMock(return_value=False)), \ + patch('salt.utils.platform.is_openbsd', MagicMock(return_value=False)): + self.assertEqual(system.shutdown(), 'A') + cmd_mock.assert_called_with(['shutdown', '-p', 'now'], python_shell=False) + + def test_shutdown_netbsd(self): + ''' + Test to shutdown a running NetBSD system + ''' + cmd_mock = MagicMock(return_value='A') + with patch.dict(system.__salt__, {'cmd.run': cmd_mock}), \ + patch('salt.utils.platform.is_freebsd', MagicMock(return_value=False)), \ + patch('salt.utils.platform.is_netbsd', MagicMock(return_value=True)), \ + patch('salt.utils.platform.is_openbsd', MagicMock(return_value=False)): + self.assertEqual(system.shutdown(), 'A') + cmd_mock.assert_called_with(['shutdown', '-p', 'now'], python_shell=False) + + def test_shutdown_openbsd(self): + ''' + Test to shutdown a running OpenBSD system + ''' + cmd_mock = MagicMock(return_value='A') + with patch.dict(system.__salt__, {'cmd.run': cmd_mock}), \ + patch('salt.utils.platform.is_freebsd', MagicMock(return_value=False)), \ + patch('salt.utils.platform.is_netbsd', MagicMock(return_value=False)), \ + patch('salt.utils.platform.is_openbsd', MagicMock(return_value=True)): self.assertEqual(system.shutdown(), 'A') + cmd_mock.assert_called_with(['shutdown', '-p', 'now'], python_shell=False) diff --git a/tests/unit/modules/test_systemd_service.py b/tests/unit/modules/test_systemd_service.py index 1d3a760c1315..8b1c492f9dd4 100644 --- a/tests/unit/modules/test_systemd_service.py +++ b/tests/unit/modules/test_systemd_service.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -63,7 +61,6 @@ timer3.timer static''' -@skipIf(NO_MOCK, NO_MOCK_REASON) class SystemdTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.modules.systemd @@ -110,7 +107,7 @@ def test_get_enabled(self): 'README' ) ) - sysv_enabled_mock = MagicMock(side_effect=lambda x: x == 'baz') + sysv_enabled_mock = MagicMock(side_effect=lambda x, _: x == 'baz') with patch.dict(systemd.__salt__, {'cmd.run': cmd_mock}): with patch.object(os, 'listdir', listdir_mock): @@ -146,7 +143,7 @@ def test_get_disabled(self): 'README' ) ) - sysv_enabled_mock = MagicMock(side_effect=lambda x: x == 'baz') + sysv_enabled_mock = MagicMock(side_effect=lambda x, _: x == 'baz') with patch.dict(systemd.__salt__, {'cmd.run': cmd_mock}): with patch.object(os, 'listdir', listdir_mock): @@ -261,7 +258,6 @@ def test_execs(self): self.assertDictEqual(systemd.execs(), {'a': 'c', 'b': 'c'}) -@skipIf(NO_MOCK, NO_MOCK_REASON) class SystemdScopeTestCase(TestCase, LoaderModuleMockMixin): ''' Test case for salt.modules.systemd, for functions which use systemd diff --git a/tests/unit/modules/test_telegram.py b/tests/unit/modules/test_telegram.py index f45a7e339be2..3a7eca9483cd 100644 --- a/tests/unit/modules/test_telegram.py +++ b/tests/unit/modules/test_telegram.py @@ -12,12 +12,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( Mock, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -65,7 +63,6 @@ def json(self): return {'_id': 4321} -@skipIf(NO_MOCK, NO_MOCK_REASON) class TelegramModuleTest(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.telegram. diff --git a/tests/unit/modules/test_timezone.py b/tests/unit/modules/test_timezone.py index 37d19ff1083b..1aa2a766c83a 100644 --- a/tests/unit/modules/test_timezone.py +++ b/tests/unit/modules/test_timezone.py @@ -10,8 +10,6 @@ from tests.support.unit import TestCase, skipIf from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch, mock_open ) @@ -27,7 +25,6 @@ GET_LOCALTIME_PATH = 'salt.modules.timezone._get_localtime_path' -@skipIf(NO_MOCK, NO_MOCK_REASON) class TimezoneTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -86,7 +83,6 @@ def create_tempfile_with_contents(self, contents): return temp -@skipIf(NO_MOCK, NO_MOCK_REASON) class TimezoneModuleTestCase(TestCase, LoaderModuleMockMixin): ''' Timezone test case diff --git a/tests/unit/modules/test_tls.py b/tests/unit/modules/test_tls.py index 1765b99402bc..a78ca7d7c4ef 100644 --- a/tests/unit/modules/test_tls.py +++ b/tests/unit/modules/test_tls.py @@ -24,8 +24,6 @@ mock_open, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -113,7 +111,6 @@ # Skip this test case if we don't have access to mock or PyOpenSSL. -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(NO_PYOPENSSL, 'PyOpenSSL must be installed to run these tests.') class TLSAddTestCase(TestCase, LoaderModuleMockMixin): ''' diff --git a/tests/unit/modules/test_tuned.py b/tests/unit/modules/test_tuned.py index 0ddd41f2c53f..73d2b802e7b4 100644 --- a/tests/unit/modules/test_tuned.py +++ b/tests/unit/modules/test_tuned.py @@ -4,16 +4,13 @@ from salt.modules import tuned from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch, ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class TunedListTestCase(TestCase, LoaderModuleMockMixin): ''' Test the tuned.list_() method for different versions of tuned-adm diff --git a/tests/unit/modules/test_twilio_notify.py b/tests/unit/modules/test_twilio_notify.py index d1cbfceca1f9..91d8f41ce537 100644 --- a/tests/unit/modules/test_twilio_notify.py +++ b/tests/unit/modules/test_twilio_notify.py @@ -12,8 +12,6 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -95,7 +93,6 @@ def __init__(self): @skipIf(not HAS_LIBS, 'twilio.rest is not available') -@skipIf(NO_MOCK, NO_MOCK_REASON) class TwilioNotifyTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.twilio_notify diff --git a/tests/unit/modules/test_udev.py b/tests/unit/modules/test_udev.py index 45b17f2c3476..c643ce6c6c3d 100644 --- a/tests/unit/modules/test_udev.py +++ b/tests/unit/modules/test_udev.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) class UdevTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.udev diff --git a/tests/unit/modules/test_uptime.py b/tests/unit/modules/test_uptime.py index daced37315e6..a58982efff80 100644 --- a/tests/unit/modules/test_uptime.py +++ b/tests/unit/modules/test_uptime.py @@ -5,8 +5,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, Mock +from tests.support.unit import TestCase +from tests.support.mock import Mock # Import salt libs from salt.exceptions import CommandExecutionError @@ -50,7 +50,6 @@ def json(self): REQUEST_MOCK = RequestMock() -@skipIf(NO_MOCK, NO_MOCK_REASON) class UptimeTestCase(TestCase, LoaderModuleMockMixin): ''' UptimeTestCase diff --git a/tests/unit/modules/test_useradd.py b/tests/unit/modules/test_useradd.py index 18da8d8ce863..814146e699d4 100644 --- a/tests/unit/modules/test_useradd.py +++ b/tests/unit/modules/test_useradd.py @@ -17,8 +17,6 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -26,7 +24,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class UserAddTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.useradd @@ -415,14 +412,15 @@ def test_rename(self): mock = MagicMock(return_value=None) with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'name': ''}, False, + mock = MagicMock(side_effect=[False, {'name': ''}, {'name': 'salt'}]) with patch.object(useradd, 'info', mock): self.assertTrue(useradd.rename('name', 'salt')) mock = MagicMock(return_value=None) with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'name': ''}, False, {'name': ''}]) + mock = MagicMock(side_effect=[False, {'name': ''}, + {'name': ''}]) with patch.object(useradd, 'info', mock): self.assertFalse(useradd.rename('salt', 'salt')) diff --git a/tests/unit/modules/test_uwsgi.py b/tests/unit/modules/test_uwsgi.py index 809c58d8b849..2eb3cef3c14d 100644 --- a/tests/unit/modules/test_uwsgi.py +++ b/tests/unit/modules/test_uwsgi.py @@ -5,14 +5,13 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, Mock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, Mock, patch # Import salt libs import salt.modules.uwsgi as uwsgi -@skipIf(NO_MOCK, NO_MOCK_REASON) class UwsgiTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/modules/test_vagrant.py b/tests/unit/modules/test_vagrant.py index 7c636053dc3c..d91fdb6bef86 100644 --- a/tests/unit/modules/test_vagrant.py +++ b/tests/unit/modules/test_vagrant.py @@ -6,8 +6,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.modules.vagrant as vagrant @@ -17,7 +17,6 @@ TEMP_DATABASE_FILE = '/tmp/salt-tests-tmpdir/test_vagrant.sqlite' -@skipIf(NO_MOCK, NO_MOCK_REASON) class VagrantTestCase(TestCase, LoaderModuleMockMixin): ''' Unit TestCase for the salt.modules.vagrant module. diff --git a/tests/unit/modules/test_varnish.py b/tests/unit/modules/test_varnish.py index 6ce6c8b2df97..df4d9f9033ff 100644 --- a/tests/unit/modules/test_varnish.py +++ b/tests/unit/modules/test_varnish.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.modules.varnish as varnish -@skipIf(NO_MOCK, NO_MOCK_REASON) class VarnishTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.varnish diff --git a/tests/unit/modules/test_virt.py b/tests/unit/modules/test_virt.py index 6546a0467c8b..2c01fa1c954c 100644 --- a/tests/unit/modules/test_virt.py +++ b/tests/unit/modules/test_virt.py @@ -13,8 +13,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.utils.yaml @@ -38,6 +38,10 @@ class LibvirtMock(MagicMock): # pylint: disable=too-many-ancestors ''' Libvirt library mock ''' + class virDomain(MagicMock): + ''' + virDomain mock + ''' class libvirtError(Exception): ''' @@ -45,7 +49,6 @@ class libvirtError(Exception): ''' -@skipIf(NO_MOCK, NO_MOCK_REASON) class VirtTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.module.virt @@ -76,7 +79,7 @@ def set_mock_vm(self, name, xml): Define VM to use in tests ''' self.mock_conn.listDefinedDomains.return_value = [name] # pylint: disable=no-member - mock_domain = MagicMock() + mock_domain = self.mock_libvirt.virDomain() self.mock_conn.lookupByName.return_value = mock_domain # pylint: disable=no-member mock_domain.XMLDesc.return_value = xml # pylint: disable=no-member @@ -1437,6 +1440,23 @@ def test_get_nics(self): self.assertEqual('bridge', nic['type']) self.assertEqual('ac:de:48:b6:8b:59', nic['mac']) + def test_get_xml(self): + ''' + Test virt.get_xml() + ''' + xml = ''' + test-vm + + + + + + + ''' + domain = self.set_mock_vm("test-vm", xml) + self.assertEqual(xml, virt.get_xml('test-vm')) + self.assertEqual(xml, virt.get_xml(domain)) + def test_parse_qemu_img_info(self): ''' Make sure that qemu-img info output is properly parsed @@ -2642,3 +2662,34 @@ def test_pool_list_volumes(self): self.mock_conn.storagePoolLookupByName.return_value = mock_pool # pylint: enable=no-member self.assertEqual(names, virt.pool_list_volumes('default')) + + @patch('salt.modules.virt._is_kvm_hyper', return_value=True) + @patch('salt.modules.virt._is_xen_hyper', return_value=False) + def test_get_hypervisor(self, isxen_mock, iskvm_mock): + ''' + test the virt.get_hypervisor() function + ''' + self.assertEqual('kvm', virt.get_hypervisor()) + + iskvm_mock.return_value = False + self.assertIsNone(virt.get_hypervisor()) + + isxen_mock.return_value = True + self.assertEqual('xen', virt.get_hypervisor()) + + def test_pool_delete(self): + ''' + Test virt.pool_delete function + ''' + mock_pool = MagicMock() + mock_pool.delete = MagicMock(return_value=0) + self.mock_conn.storagePoolLookupByName = MagicMock(return_value=mock_pool) + + res = virt.pool_delete('test-pool') + self.assertTrue(res) + + self.mock_conn.storagePoolLookupByName.assert_called_once_with('test-pool') + + # Shouldn't be called with another parameter so far since those are not implemented + # and thus throwing exceptions. + mock_pool.delete.assert_called_once_with(self.mock_libvirt.VIR_STORAGE_POOL_DELETE_NORMAL) diff --git a/tests/unit/modules/test_virtualenv_mod.py b/tests/unit/modules/test_virtualenv_mod.py index 1c982e91c6d9..3acc1cbde578 100644 --- a/tests/unit/modules/test_virtualenv_mod.py +++ b/tests/unit/modules/test_virtualenv_mod.py @@ -13,16 +13,15 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.helpers import TestsLoggingHandler, ForceImportErrorOn -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.helpers import TstSuiteLoggingHandler, ForceImportErrorOn +from tests.support.mock import MagicMock, patch # Import salt libs import salt.modules.virtualenv_mod as virtualenv_mod from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class VirtualenvTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -54,7 +53,7 @@ def test_issue_6029_deprecated_distribute(self): python_shell=False ) - with TestsLoggingHandler() as handler: + with TstSuiteLoggingHandler() as handler: # Let's fake a higher virtualenv version virtualenv_mock = MagicMock() virtualenv_mock.__version__ = '1.10rc1' @@ -92,7 +91,7 @@ def test_issue_6030_deprecated_never_download(self): python_shell=False ) - with TestsLoggingHandler() as handler: + with TstSuiteLoggingHandler() as handler: mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) # Let's fake a higher virtualenv version virtualenv_mock = MagicMock() diff --git a/tests/unit/modules/test_vsphere.py b/tests/unit/modules/test_vsphere.py index 5b61aa0257d2..1d30e4eb904a 100644 --- a/tests/unit/modules/test_vsphere.py +++ b/tests/unit/modules/test_vsphere.py @@ -23,20 +23,30 @@ from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf from tests.support.mock import ( + Mock, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, call ) # Import Third Party Libs try: from pyVmomi import vim, vmodl # pylint: disable=unused-import + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False +try: + # pylint: disable=unused-import + from com.vmware.vapi.std_client import DynamicID + HAS_VSPHERE_SDK = True +except ImportError: + HAS_VSPHERE_SDK = False + +import salt.utils.args +import salt.utils.vmware + # Globals HOST = '1.2.3.4' USER = 'root' @@ -44,11 +54,11 @@ ERROR = 'Some Testing Error Message' -@skipIf(NO_MOCK, NO_MOCK_REASON) class VsphereTestCase(TestCase, LoaderModuleMockMixin): ''' Unit TestCase for the salt.modules.vsphere module. ''' + def setup_loader_modules(self): return {vsphere: {'__virtual__': MagicMock(return_value='vsphere')}} @@ -566,11 +576,11 @@ def test_set_syslog_config_helper_success(self): vsphere._set_syslog_config_helper(HOST, USER, PASSWORD, config, 'foo')) -@skipIf(NO_MOCK, NO_MOCK_REASON) class GetProxyTypeTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.get_proxy_type ''' + def setup_loader_modules(self): return {vsphere: {'__virtual__': MagicMock(return_value='vsphere')}} @@ -581,11 +591,11 @@ def test_output(self): self.assertEqual('fake_proxy_type', ret) -@skipIf(NO_MOCK, NO_MOCK_REASON) class SupportsProxiesTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.supports_proxies decorator ''' + def setup_loader_modules(self): return {vsphere: {'__virtual__': MagicMock(return_value='vsphere')}} @@ -613,11 +623,11 @@ def mock_function(): excinfo.exception.strerror) -@skipIf(NO_MOCK, NO_MOCK_REASON) class _GetProxyConnectionDetailsTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere._get_proxy_connection_details ''' + def setup_loader_modules(self): return {vsphere: {'__virtual__': MagicMock(return_value='vsphere')}} @@ -679,7 +689,7 @@ def test_esxi_proxy_host_details(self): MagicMock(return_value='esxi')): with patch.dict(vsphere.__salt__, {'esxi.get_details': - MagicMock(return_value=self.esxi_host_details)}): + MagicMock(return_value=self.esxi_host_details)}): ret = vsphere._get_proxy_connection_details() self.assertEqual(('fake_host', 'fake_username', 'fake_password', 'fake_protocol', 'fake_port', 'fake_mechanism', @@ -690,7 +700,7 @@ def test_esxdatacenter_proxy_details(self): MagicMock(return_value='esxdatacenter')): with patch.dict(vsphere.__salt__, {'esxdatacenter.get_details': MagicMock( - return_value=self.esxdatacenter_details)}): + return_value=self.esxdatacenter_details)}): ret = vsphere._get_proxy_connection_details() self.assertEqual(('fake_vcenter', 'fake_username', 'fake_password', 'fake_protocol', 'fake_port', 'fake_mechanism', @@ -701,7 +711,7 @@ def test_esxcluster_proxy_details(self): MagicMock(return_value='esxcluster')): with patch.dict(vsphere.__salt__, {'esxcluster.get_details': MagicMock( - return_value=self.esxcluster_details)}): + return_value=self.esxcluster_details)}): ret = vsphere._get_proxy_connection_details() self.assertEqual(('fake_vcenter', 'fake_username', 'fake_password', 'fake_protocol', 'fake_port', 'fake_mechanism', @@ -712,8 +722,8 @@ def test_esxi_proxy_vcenter_details(self): MagicMock(return_value='esxi')): with patch.dict(vsphere.__salt__, {'esxi.get_details': - MagicMock( - return_value=self.esxi_vcenter_details)}): + MagicMock( + return_value=self.esxi_vcenter_details)}): ret = vsphere._get_proxy_connection_details() self.assertEqual(('fake_vcenter', 'fake_username', 'fake_password', 'fake_protocol', 'fake_port', 'fake_mechanism', @@ -724,7 +734,7 @@ def test_vcenter_proxy_details(self): MagicMock(return_value='vcenter')): with patch.dict(vsphere.__salt__, {'vcenter.get_details': MagicMock( - return_value=self.vcenter_details)}): + return_value=self.vcenter_details)}): ret = vsphere._get_proxy_connection_details() self.assertEqual(('fake_vcenter', 'fake_username', 'fake_password', 'fake_protocol', 'fake_port', 'fake_mechanism', @@ -739,12 +749,12 @@ def test_unsupported_proxy_details(self): excinfo.exception.strerror) -@skipIf(NO_MOCK, NO_MOCK_REASON) class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.gets_service_instance_via_proxy decorator ''' + def setup_loader_modules(self): patcher = patch('salt.utils.vmware.get_service_instance', MagicMock()) patcher.start() @@ -911,11 +921,11 @@ def mock_function(**kwargs): self.assertEqual(ret, self.mock_si) -@skipIf(NO_MOCK, NO_MOCK_REASON) class GetServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.get_service_instance_via_proxy ''' + def setup_loader_modules(self): patcher = patch('salt.utils.vmware.get_service_instance', MagicMock()) patcher.start() @@ -954,11 +964,11 @@ def test_output(self): self.assertEqual(res, mock_si) -@skipIf(NO_MOCK, NO_MOCK_REASON) class DisconnectTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.disconnect ''' + def setup_loader_modules(self): self.mock_si = MagicMock() self.addCleanup(delattr, self, 'mock_si') @@ -991,11 +1001,11 @@ def test_output(self): self.assertEqual(res, True) -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestVcenterConnectionTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.test_vcenter_connection ''' + def setup_loader_modules(self): self.mock_si = MagicMock() self.addCleanup(delattr, self, 'mock_si') @@ -1066,12 +1076,12 @@ def test_output_false(self): self.assertEqual(res, False) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class ListDatacentersViaProxyTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.list_datacenters_via_proxy ''' + def setup_loader_modules(self): self.mock_si = MagicMock() self.addCleanup(delattr, self, 'mock_si') @@ -1152,12 +1162,12 @@ def test_returned_array(self): self.assertEqual(res, [{'name': 'fake_dc1'}, {'name': 'fake_dc2'}]) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class CreateDatacenterTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.create_datacenter ''' + def setup_loader_modules(self): self.mock_si = MagicMock() self.addCleanup(delattr, self, 'mock_si') @@ -1203,12 +1213,12 @@ def test_returned_value(self): self.assertEqual(res, {'create_datacenter': True}) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class EraseDiskPartitionsTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.erase_disk_partitions ''' + def setup_loader_modules(self): return { vsphere: { @@ -1292,12 +1302,12 @@ def test_erase_disk_partitions(self): self.mock_si, self.mock_host, 'fake_disk_id', hostname='fake_host') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class RemoveDatastoreTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.remove_datastore ''' + def setup_loader_modules(self): return { vsphere: { @@ -1382,12 +1392,12 @@ def test_success_output(self): self.assertTrue(res) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class RemoveDiskgroupTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.remove_diskgroup ''' + def setup_loader_modules(self): return { vsphere: { @@ -1477,13 +1487,13 @@ def test_success_output(self): self.assertTrue(res) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not vsphere.HAS_JSONSCHEMA, 'The \'jsonschema\' library is missing') class RemoveCapacityFromDiskgroupTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.remove_capacity_from_diskgroup ''' + def setup_loader_modules(self): return { vsphere: { @@ -1646,12 +1656,12 @@ def test_success_output(self): self.assertTrue(res) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class ListClusterTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.list_cluster ''' + def setup_loader_modules(self): return { vsphere: { @@ -1690,7 +1700,7 @@ def setUp(self): # Patch __salt__ dunder patcher = patch.dict(vsphere.__salt__, {'esxcluster.get_details': - MagicMock(return_value={'cluster': 'cl'})}) + MagicMock(return_value={'cluster': 'cl'})}) patcher.start() self.addCleanup(patcher.stop) @@ -1738,12 +1748,12 @@ def test__get_cluster_dict_call(self): self.mock__get_cluster_dict.assert_called_once_with('cl', self.mock_cl) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class RenameDatastoreTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere.rename_datastore ''' + def setup_loader_modules(self): return { vsphere: { @@ -1822,11 +1832,11 @@ def test_rename_datastore_call(self): self.mock_ds_ref, 'new_ds_name') -@skipIf(NO_MOCK, NO_MOCK_REASON) class _GetProxyTargetTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.modules.vsphere._get_proxy_target ''' + def setup_loader_modules(self): return { vsphere: { @@ -1918,3 +1928,728 @@ def test_vcenter_proxy_return(self): ret = vsphere._get_proxy_target(self.mock_si) self.mock_get_root_folder.assert_called_once_with(self.mock_si) self.assertEqual(ret, self.mock_root) + + +@skipIf(not HAS_VSPHERE_SDK, 'The \'vsphere-automation-sdk\' library is missing') +class TestVSphereTagging(TestCase, LoaderModuleMockMixin): + ''' + Tests for: + - salt.modules.vsphere.create_tag_category + - salt.modules.vsphere.create_tag + - salt.modules.vsphere.delete_tag_category + - salt.modules.vsphere.delete_tag + - salt.modules.vsphere.list_tag_categories + - salt.modules.vsphere.list_tags + - salt.modules.vsphere.attach_tags + - salt.modules.vsphere.list_attached_tags + ''' + + def setup_loader_modules(self): + return { + vsphere: { + '__virtual__': MagicMock(return_value='vsphere'), + '_get_proxy_connection_details': MagicMock(), + 'get_proxy_type': MagicMock(return_value='vcenter') + } + } + + # Grains for expected __salt__ calls + details = { + key: None for key in [ + 'vcenter', 'username', 'password'] + } + + # Function attributes + func_attrs = { + key: None for key in [ + 'category_id', 'object_id', 'tag_id', 'name', + 'description', 'cardinality'] + } + + # Expected returns + create_tag_category = { + 'Category created': + 'urn:vmomi:InventoryServiceTag:' + 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + } + + create_tag = { + 'Tag created': + 'urn:vmomi:InventoryServiceTag:' + 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + } + + delete_tag_category = { + 'Category deleted': + 'urn:vmomi:InventoryServiceTag:' + 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + } + + delete_tag = { + 'Tag deleted': + 'urn:vmomi:InventoryServiceTag:' + 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + } + + list_tag_categories_return = [ + 'urn:vmomi:InventoryServiceCategory:' + 'b13f4959-a3f3-48d0-8080-15bb586b4355:GLOBAL', + 'urn:vmomi:InventoryServiceCategory:' + 'f4d41f02-c317-422d-9013-dcbebfcd54ad:GLOBAL', + 'urn:vmomi:InventoryServiceCategory:' + '2db5b00b-f211-4bba-ba42-e2658ebbb283:GLOBAL', + 'urn:vmomi:InventoryServiceCategory:' + 'cd847c3c-687c-4bd9-8e5a-0eb536f0a01d:GLOBAL', + 'urn:vmomi:InventoryServiceCategory:' + 'd51c24f9-cffb-4ce0-af56-7f18b6e649af:GLOBAL' + ] + + list_tags_return = [ + 'urn:vmomi:InventoryServiceTag:' + 'a584a83b-3015-45ad-8057-a3630613052f:GLOBAL', + 'urn:vmomi:InventoryServiceTag:' + 'db08019c-15de-4bbf-be46-d81aaf8d25c0:GLOBAL', + 'urn:vmomi:InventoryServiceTag:' + 'b55ecc77-f4a5-49f8-ab52-38865467cfbe:GLOBAL', + 'urn:vmomi:InventoryServiceTag:' + 'f009ab1b-e1b5-4c40-b8f7-951d9d716b39:GLOBAL', + 'urn:vmomi:InventoryServiceTag:' + '102bb4c5-9b76-4d6c-882a-76a91ee3edcc:GLOBAL', + 'urn:vmomi:InventoryServiceTag:' + 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL', + 'urn:vmomi:InventoryServiceTag:' + '71d30f2d-bb23-48e1-995f-630adfb0dc89:GLOBAL' + ] + + list_attached_tags_return = [ + 'urn:vmomi:InventoryServiceTag:' + 'b55ecc77-f4a5-49f8-ab52-38865467cfbe:GLOBAL', + 'urn:vmomi:InventoryServiceTag:' + 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + ] + + list_create_category_return = [ + 'urn:vmomi:InventoryServiceCategory:' + '0af54c2d-e8cd-4248-931e-2f5807d8c477:GLOBAL' + ] + + list_create_tag_return = [ + 'urn:vmomi:InventoryServiceCategory:' + '0af54c2d-e8cd-4248-931e-2f5807d8c477:GLOBAL' + ] + + attach_tags_return = { + 'Tag attached': + 'urn:vmomi:InventoryServiceTag:' + 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + } + + def test_create_tag_category_client_none(self): + get_details = MagicMock(return_value=self.details) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=None + ) as get_vsphere_client: + ret = vsphere.create_tag_category( + self.func_attrs['name'], + self.func_attrs['description'], + self.func_attrs['cardinality']) + # Check function calls and return data + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, {'Category created': None}) + + def test_create_tag_category_client(self): + get_details = MagicMock(return_value=self.details) + + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + Category=Mock( + CreateSpec=Mock(return_value=Mock()), + create=Mock( + return_value=self.create_tag_category[ + 'Category created'])))) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=mock_client + ) as get_vsphere_client: + ret = vsphere.create_tag_category( + self.func_attrs['name'], + self.func_attrs['description'], + self.func_attrs['cardinality']) + + # Check function calls and return data + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, self.create_tag_category) + + def test_create_tag_client_none(self): + get_details = MagicMock(return_value=self.details) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + )as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=None + ) as get_vsphere_client: + ret = vsphere.create_tag( + self.func_attrs['name'], + self.func_attrs['description'], + self.func_attrs['cardinality']) + # Check function calls and return data + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, {'Tag created': None}) + + def test_create_tag_client(self): + get_details = MagicMock(return_value=self.details) + + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + Tag=Mock( + CreateSpec=Mock(return_value=Mock()), + create=Mock(return_value=self.create_tag[ + 'Tag created'])))) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + )as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=mock_client + ) as get_vsphere_client: + ret = vsphere.create_tag( + self.func_attrs['name'], + self.func_attrs['description'], + self.func_attrs['cardinality']) + # Check function calls and return data + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, self.create_tag) + + def test_delete_tag_category_client_none(self): + get_details = MagicMock(return_value=self.details) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=None + ) as get_vsphere_client: + ret = vsphere.delete_tag_category( + self.func_attrs['category_id']) + # Check function calls and return data + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, {'Category deleted': None}) + + def test_delete_tag_category_client(self): + get_details = MagicMock(return_value=self.details) + + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + Category=Mock( + delete=Mock(return_value=self.delete_tag_category[ + 'Category deleted'])))) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=mock_client + ) as get_vsphere_client: + ret = vsphere.delete_tag_category( + self.func_attrs['category_id']) + + # Check function calls and return data + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, self.delete_tag_category) + + def test_delete_tag_client_none(self): + get_details = MagicMock(return_value=self.details) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[]) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=None + ) as get_vsphere_client: + ret = vsphere.delete_tag( + self.func_attrs['tag_id']) + # Check function calls and return data + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, {'Tag deleted': None}) + + def test_delete_tag_client(self): + get_details = MagicMock(return_value=self.details) + + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + Tag=Mock( + delete=Mock(return_value=self.delete_tag[ + 'Tag deleted'])))) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=mock_client + ) as get_vsphere_client: + ret = vsphere.delete_tag( + self.func_attrs['tag_id']) + + # Check function calls and return data + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, self.delete_tag) + + def test_list_tag_categories_client_none(self): + get_details = MagicMock(return_value=self.details) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=None + ) as get_vsphere_client: + ret = vsphere.list_tag_categories() + # Check function calls and return data + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, {'Categories': None}) + + def test_list_tag_categories_client(self): + get_details = MagicMock(return_value=self.details) + + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + Category=Mock( + list=Mock(return_value=self.list_tag_categories_return + )))) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=mock_client + ) as get_vsphere_client: + ret = vsphere.list_tag_categories() + + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual( + ret, {'Categories': + self.list_tag_categories_return}) + + def test_list_tags_client_none(self): + get_details = MagicMock(return_value=self.details) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=None + ) as get_vsphere_client: + # Check function calls and return + ret = vsphere.list_tags() + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, {'Tags': None}) + + def test_list_tags_client(self): + get_details = MagicMock(return_value=self.details) + + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + Tag=Mock( + list=Mock(return_value=self.list_tags_return + )))) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=mock_client + ) as get_vsphere_client: + # Check function calls and return + ret = vsphere.list_tags() + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, + {'Tags': self.list_tags_return}) + + def test_list_attached_tags_client_none(self): + get_details = MagicMock(return_value=self.details) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=None + ) as get_vsphere_client: + with patch.object(vsphere, + 'DynamicID' + ) as dynamic_id: + # Check function calls and return + ret = vsphere.list_attached_tags('object_id') + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, {'Attached tags': None}) + + def test_list_attached_tags_client(self): + get_details = MagicMock(return_value=self.details) + + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + TagAssociation=Mock( + list_attached_tags=Mock( + return_value=self.list_attached_tags_return + )))) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=mock_client + ) as get_vsphere_client: + with patch.object(vsphere, + 'DynamicID' + ) as dynamic_id: + # Check function calls and return + ret = vsphere.list_attached_tags( + self.func_attrs['object_id']) + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual( + ret, {'Attached tags': + self.list_attached_tags_return}) + + def test_attach_tags_client_none(self): + get_details = MagicMock(return_value=self.details) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=None + ) as get_vsphere_client: + # Check function calls and return + ret = vsphere.attach_tag( + object_id=self.func_attrs['object_id'], + tag_id=self.func_attrs['tag_id']) + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual(ret, {'Tag attached': None}) + + def test_attach_tags_client(self): + get_details = MagicMock(return_value=self.details) + + # Mock CreateSpec object and create objects + mock_client = Mock( + tagging=Mock( + TagAssociation=Mock( + attach=Mock(return_value=self.list_attached_tags_return + )))) + + # Start patching each external API return with Mock Objects + with patch.object(vsphere, + 'get_proxy_type', + return_value='vcenter' + ) as get_proxy_type: + with patch.object(vsphere, + '_get_proxy_connection_details', + return_value=[] + ) as get_proxy_connection: + with patch.object(salt.utils.vmware, + 'get_service_instance', + return_value=None + ) as get_service_instance: + with patch.dict(vsphere.__salt__, + {'vcenter.get_details': get_details}, + clear=True + ) as get_vcenter_details: + with patch.object(salt.utils.vmware, + 'get_vsphere_client', + return_value=mock_client + ) as get_vsphere_client: + with patch.object(vsphere, + 'DynamicID' + ) as dynamic_id: + # Check function calls and return + ret = vsphere.attach_tag( + object_id=self.func_attrs['object_id'], + tag_id=self.func_attrs['tag_id']) + get_proxy_type.assert_called_once() + get_proxy_connection.assert_called_once() + get_service_instance.assert_called_once() + get_vsphere_client.assert_called_once() + self.assertEqual( + ret, {'Tag attached': + self.list_attached_tags_return}) diff --git a/tests/unit/modules/test_webutil.py b/tests/unit/modules/test_webutil.py index 4e40328cdf7c..eac01140075c 100644 --- a/tests/unit/modules/test_webutil.py +++ b/tests/unit/modules/test_webutil.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.webutil as htpasswd -@skipIf(NO_MOCK, NO_MOCK_REASON) class HtpasswdTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.webutil diff --git a/tests/unit/modules/test_win_disk.py b/tests/unit/modules/test_win_disk.py index 7bcc27f68f6b..95d1ec476451 100644 --- a/tests/unit/modules/test_win_disk.py +++ b/tests/unit/modules/test_win_disk.py @@ -8,11 +8,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON -) +from tests.support.unit import TestCase # Import Salt Libs import salt.modules.win_disk as win_disk @@ -49,7 +45,6 @@ def __init__(self): self.windll = MockWindll() -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinDiskTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_disk diff --git a/tests/unit/modules/test_win_dns_client.py b/tests/unit/modules/test_win_dns_client.py index c417120f670b..698967b75aed 100644 --- a/tests/unit/modules/test_win_dns_client.py +++ b/tests/unit/modules/test_win_dns_client.py @@ -14,8 +14,6 @@ MagicMock, patch, Mock, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -65,7 +63,6 @@ def Com(): return True -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_WMI, 'WMI only available on Windows') class WinDnsClientTestCase(TestCase, LoaderModuleMockMixin): ''' diff --git a/tests/unit/modules/test_win_file.py b/tests/unit/modules/test_win_file.py index 6ad4c6560fe1..55957a9aaf65 100644 --- a/tests/unit/modules/test_win_file.py +++ b/tests/unit/modules/test_win_file.py @@ -9,15 +9,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import MagicMock, NO_MOCK, NO_MOCK_REASON, patch -from tests.support.paths import TMP +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase, skipIf +from tests.support.runtests import RUNTIME_VARS # Import Salt Libs import salt.modules.temp as temp import salt.modules.win_file as win_file import salt.utils.platform import salt.utils.win_dacl as win_dacl +import salt.utils.win_functions import salt.modules.cmdmod as cmdmod from salt.exceptions import CommandExecutionError @@ -40,7 +41,6 @@ class DummyStat(object): st_ctime = 1552661253 -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinFileTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_file @@ -84,7 +84,7 @@ def test_issue_52002_check_file_remove_symlink(self): ''' Make sure that directories including symlinks or symlinks can be removed ''' - base = temp.dir(prefix='base-', parent=TMP) + base = temp.dir(prefix='base-', parent=RUNTIME_VARS.TMP) target = os.path.join(base, 'child 1', 'target\\') symlink = os.path.join(base, 'child 2', 'link') try: @@ -104,7 +104,6 @@ def test_issue_52002_check_file_remove_symlink(self): win_file.remove(base) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not salt.utils.platform.is_windows(), 'Skip on Non-Windows systems') class WinFileCheckPermsTestCase(TestCase, LoaderModuleMockMixin): ''' @@ -126,13 +125,13 @@ def setup_loader_modules(self): } def setUp(self): - self.temp_file = temp.file(parent=TMP) - salt.utils.win_dacl.set_owner(obj_name=self.temp_file, + self.temp_file = temp.file(parent=RUNTIME_VARS.TMP) + win_dacl.set_owner(obj_name=self.temp_file, principal=self.current_user) - salt.utils.win_dacl.set_inheritance(obj_name=self.temp_file, + win_dacl.set_inheritance(obj_name=self.temp_file, enabled=True) self.assertEqual( - salt.utils.win_dacl.get_owner(obj_name=self.temp_file), + win_dacl.get_owner(obj_name=self.temp_file), self.current_user) def tearDown(self): @@ -185,7 +184,8 @@ def test_check_perms_deny(self): Test setting deny perms on a file ''' expected = {'comment': '', - 'changes': {'perms': {'Users': {'deny': 'read_execute'}}}, + 'changes': { + 'perms': {'Users': {'deny': 'read_execute'}}}, 'name': self.temp_file, 'result': True} ret = win_file.check_perms( @@ -214,7 +214,8 @@ def test_check_perms_grant(self): Test setting grant perms on a file ''' expected = {'comment': '', - 'changes': {'perms': {'Users': {'grant': 'read_execute'}}}, + 'changes': { + 'perms': {'Users': {'grant': 'read_execute'}}}, 'name': self.temp_file, 'result': True} ret = win_file.check_perms( @@ -265,11 +266,11 @@ def test_check_perms_reset_test_true(self): Test resetting perms with test=True. This shows minimal changes ''' # Turn off inheritance - salt.utils.win_dacl.set_inheritance(obj_name=self.temp_file, + win_dacl.set_inheritance(obj_name=self.temp_file, enabled=False, clear=True) # Set some permissions - salt.utils.win_dacl.set_permissions(obj_name=self.temp_file, + win_dacl.set_permissions(obj_name=self.temp_file, principal='Administrator', permissions='full_control') expected = {'comment': '', @@ -297,11 +298,11 @@ def test_check_perms_reset(self): Test resetting perms on a File ''' # Turn off inheritance - salt.utils.win_dacl.set_inheritance(obj_name=self.temp_file, + win_dacl.set_inheritance(obj_name=self.temp_file, enabled=False, clear=True) # Set some permissions - salt.utils.win_dacl.set_permissions(obj_name=self.temp_file, + win_dacl.set_permissions(obj_name=self.temp_file, principal='Administrator', permissions='full_control') expected = {'comment': '', diff --git a/tests/unit/modules/test_win_groupadd.py b/tests/unit/modules/test_win_groupadd.py index 048112d893f0..8624153af62c 100644 --- a/tests/unit/modules/test_win_groupadd.py +++ b/tests/unit/modules/test_win_groupadd.py @@ -13,8 +13,6 @@ MagicMock, Mock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -63,12 +61,10 @@ def Remove(self, name): pass -if not NO_MOCK: - sam_mock = MagicMock(side_effect=lambda x: 'HOST\\' + x) +sam_mock = MagicMock(side_effect=lambda x: 'HOST\\' + x) @skipIf(not HAS_WIN_LIBS, 'win_groupadd unit tests can only be run if win32com, pythoncom, and pywintypes are installed') -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinGroupTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_groupadd diff --git a/tests/unit/modules/test_win_iis.py b/tests/unit/modules/test_win_iis.py index 21072452069d..ee7135b1ed8b 100644 --- a/tests/unit/modules/test_win_iis.py +++ b/tests/unit/modules/test_win_iis.py @@ -16,12 +16,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, ) APP_LIST = { @@ -116,7 +114,6 @@ CERT_BINDING_INFO = '*:443:mytestsite.local' -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinIisTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_iis @@ -131,9 +128,9 @@ def test_create_apppool(self): ''' with patch('salt.modules.win_iis._srvmgr', MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_apppools', - MagicMock(return_value=dict())), \ - patch.dict(win_iis.__salt__): + patch('salt.modules.win_iis.list_apppools', + MagicMock(return_value=dict())), \ + patch.dict(win_iis.__salt__): self.assertTrue(win_iis.create_apppool('MyTestPool')) def test_list_apppools(self): @@ -141,8 +138,8 @@ def test_list_apppools(self): Test - List all configured IIS application pools. ''' with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value=LIST_APPPOOLS_SRVMGR)): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value=LIST_APPPOOLS_SRVMGR)): self.assertEqual(win_iis.list_apppools(), APPPOOL_LIST) def test_remove_apppool(self): @@ -150,12 +147,12 @@ def test_remove_apppool(self): Test - Remove an IIS application pool. ''' with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_apppools', - MagicMock(return_value={'MyTestPool': { - 'applications': list(), - 'state': 'Started'}})): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_apppools', + MagicMock(return_value={'MyTestPool': { + 'applications': list(), + 'state': 'Started'}})): self.assertTrue(win_iis.remove_apppool('MyTestPool')) def test_restart_apppool(self): @@ -163,8 +160,8 @@ def test_restart_apppool(self): Test - Restart an IIS application pool. ''' with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})): self.assertTrue(win_iis.restart_apppool('MyTestPool')) def test_create_site(self): @@ -175,12 +172,12 @@ def test_create_site(self): 'apppool': 'MyTestPool', 'hostheader': 'mytestsite.local', 'ipaddress': '*', 'port': 80, 'protocol': 'http'} with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_sites', - MagicMock(return_value=dict())), \ - patch('salt.modules.win_iis.list_apppools', - MagicMock(return_value=dict())): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_sites', + MagicMock(return_value=dict())), \ + patch('salt.modules.win_iis.list_apppools', + MagicMock(return_value=dict())): self.assertTrue(win_iis.create_site(**kwargs)) def test_create_site_failed(self): @@ -191,12 +188,12 @@ def test_create_site_failed(self): 'apppool': 'MyTestPool', 'hostheader': 'mytestsite.local', 'ipaddress': '*', 'port': 80, 'protocol': 'invalid-protocol-name'} with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_sites', - MagicMock(return_value=dict())), \ - patch('salt.modules.win_iis.list_apppools', - MagicMock(return_value=dict())): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_sites', + MagicMock(return_value=dict())), \ + patch('salt.modules.win_iis.list_apppools', + MagicMock(return_value=dict())): self.assertRaises(SaltInvocationError, win_iis.create_site, **kwargs) def test_remove_site(self): @@ -204,10 +201,10 @@ def test_remove_site(self): Test - Delete a website from IIS. ''' with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_sites', - MagicMock(return_value=SITE_LIST)): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_sites', + MagicMock(return_value=SITE_LIST)): self.assertTrue(win_iis.remove_site('MyTestSite')) def test_create_app(self): @@ -217,11 +214,11 @@ def test_create_app(self): kwargs = {'name': 'testApp', 'site': 'MyTestSite', 'sourcepath': r'C:\inetpub\apps\testApp', 'apppool': 'MyTestPool'} with patch.dict(win_iis.__salt__), \ - patch('os.path.isdir', MagicMock(return_value=True)), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_apps', - MagicMock(return_value=APP_LIST)): + patch('os.path.isdir', MagicMock(return_value=True)), \ + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_apps', + MagicMock(return_value=APP_LIST)): self.assertTrue(win_iis.create_app(**kwargs)) def test_list_apps(self): @@ -229,8 +226,8 @@ def test_list_apps(self): Test - Get all configured IIS applications for the specified site. ''' with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value=LIST_APPS_SRVMGR)): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value=LIST_APPS_SRVMGR)): self.assertEqual(win_iis.list_apps('MyTestSite'), APP_LIST) def test_remove_app(self): @@ -239,10 +236,10 @@ def test_remove_app(self): ''' kwargs = {'name': 'otherApp', 'site': 'MyTestSite'} with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_apps', - MagicMock(return_value=APP_LIST)): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_apps', + MagicMock(return_value=APP_LIST)): self.assertTrue(win_iis.remove_app(**kwargs)) def test_create_binding(self): @@ -252,10 +249,10 @@ def test_create_binding(self): kwargs = {'site': 'MyTestSite', 'hostheader': '', 'ipaddress': '*', 'port': 80, 'protocol': 'http', 'sslflags': 0} with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_bindings', - MagicMock(return_value=BINDING_LIST)): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_bindings', + MagicMock(return_value=BINDING_LIST)): self.assertTrue(win_iis.create_binding(**kwargs)) def test_create_binding_failed(self): @@ -265,10 +262,10 @@ def test_create_binding_failed(self): kwargs = {'site': 'MyTestSite', 'hostheader': '', 'ipaddress': '*', 'port': 80, 'protocol': 'invalid-protocol-name', 'sslflags': 999} with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_bindings', - MagicMock(return_value=BINDING_LIST)): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_bindings', + MagicMock(return_value=BINDING_LIST)): self.assertRaises(SaltInvocationError, win_iis.create_binding, **kwargs) def test_list_bindings(self): @@ -276,8 +273,8 @@ def test_list_bindings(self): Test - Get all configured IIS bindings for the specified site. ''' with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis.list_sites', - MagicMock(return_value=SITE_LIST)): + patch('salt.modules.win_iis.list_sites', + MagicMock(return_value=SITE_LIST)): self.assertEqual(win_iis.list_bindings('MyTestSite'), BINDING_LIST) def test_remove_binding(self): @@ -287,10 +284,10 @@ def test_remove_binding(self): kwargs = {'site': 'MyTestSite', 'hostheader': 'myothertestsite.local', 'ipaddress': '*', 'port': 443} with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_bindings', - MagicMock(return_value=BINDING_LIST)): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_bindings', + MagicMock(return_value=BINDING_LIST)): self.assertTrue(win_iis.remove_binding(**kwargs)) def test_create_vdir(self): @@ -300,12 +297,12 @@ def test_create_vdir(self): kwargs = {'name': 'TestVdir', 'site': 'MyTestSite', 'sourcepath': r'C:\inetpub\vdirs\TestVdir'} with patch.dict(win_iis.__salt__), \ - patch('os.path.isdir', - MagicMock(return_value=True)), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_vdirs', - MagicMock(return_value=VDIR_LIST)): + patch('os.path.isdir', + MagicMock(return_value=True)), \ + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_vdirs', + MagicMock(return_value=VDIR_LIST)): self.assertTrue(win_iis.create_vdir(**kwargs)) def test_list_vdirs(self): @@ -318,8 +315,8 @@ def test_list_vdirs(self): } } with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value=LIST_VDIRS_SRVMGR)): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value=LIST_VDIRS_SRVMGR)): self.assertEqual(win_iis.list_vdirs('MyTestSite'), vdirs) def test_remove_vdir(self): @@ -328,10 +325,10 @@ def test_remove_vdir(self): ''' kwargs = {'name': 'TestOtherVdir', 'site': 'MyTestSite'} with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_vdirs', - MagicMock(return_value=VDIR_LIST)): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_vdirs', + MagicMock(return_value=VDIR_LIST)): self.assertTrue(win_iis.remove_vdir(**kwargs)) def test_create_cert_binding(self): @@ -342,15 +339,15 @@ def test_create_cert_binding(self): 'site': 'MyTestSite', 'hostheader': 'mytestsite.local', 'ipaddress': '*', 'port': 443} with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._list_certs', - MagicMock(return_value={'9988776655443322111000AAABBBCCCDDDEEEFFF': None})), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0, 'stdout': 10})), \ - patch('salt.utils.json.loads', MagicMock(return_value=[{'MajorVersion': 10, 'MinorVersion': 0}])), \ - patch('salt.modules.win_iis.list_bindings', - MagicMock(return_value=BINDING_LIST)), \ - patch('salt.modules.win_iis.list_cert_bindings', - MagicMock(return_value={CERT_BINDING_INFO: BINDING_LIST[CERT_BINDING_INFO]})): + patch('salt.modules.win_iis._list_certs', + MagicMock(return_value={'9988776655443322111000AAABBBCCCDDDEEEFFF': None})), \ + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0, 'stdout': 10})), \ + patch('salt.utils.json.loads', MagicMock(return_value=[{'MajorVersion': 10, 'MinorVersion': 0}])), \ + patch('salt.modules.win_iis.list_bindings', + MagicMock(return_value=BINDING_LIST)), \ + patch('salt.modules.win_iis.list_cert_bindings', + MagicMock(return_value={CERT_BINDING_INFO: BINDING_LIST[CERT_BINDING_INFO]})): self.assertTrue(win_iis.create_cert_binding(**kwargs)) def test_list_cert_bindings(self): @@ -359,8 +356,8 @@ def test_list_cert_bindings(self): ''' key = '*:443:mytestsite.local' with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis.list_sites', - MagicMock(return_value=SITE_LIST)): + patch('salt.modules.win_iis.list_sites', + MagicMock(return_value=SITE_LIST)): self.assertEqual(win_iis.list_cert_bindings('MyTestSite'), {key: BINDING_LIST[key]}) @@ -372,10 +369,10 @@ def test_remove_cert_binding(self): 'site': 'MyOtherTestSite', 'hostheader': 'myothertestsite.local', 'ipaddress': '*', 'port': 443} with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_cert_bindings', - MagicMock(return_value={CERT_BINDING_INFO: BINDING_LIST[CERT_BINDING_INFO]})): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.list_cert_bindings', + MagicMock(return_value={CERT_BINDING_INFO: BINDING_LIST[CERT_BINDING_INFO]})): self.assertTrue(win_iis.remove_cert_binding(**kwargs)) def test_get_container_setting(self): @@ -385,8 +382,8 @@ def test_get_container_setting(self): kwargs = {'name': 'MyTestSite', 'container': 'AppPools', 'settings': ['managedPipelineMode']} with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value=CONTAINER_SETTING)): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value=CONTAINER_SETTING)): self.assertEqual(win_iis.get_container_setting(**kwargs), {'managedPipelineMode': 'Integrated'}) @@ -397,8 +394,159 @@ def test_set_container_setting(self): kwargs = {'name': 'MyTestSite', 'container': 'AppPools', 'settings': {'managedPipelineMode': 'Integrated'}} with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.get_container_setting', - MagicMock(return_value={'managedPipelineMode': 'Integrated'})): + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0})), \ + patch('salt.modules.win_iis.get_container_setting', + MagicMock(return_value={'managedPipelineMode': 'Integrated'})): self.assertTrue(win_iis.set_container_setting(**kwargs)) + + def test__collection_match_to_index(self): + bad_match = {'key_0': 'value'} + first_match = {'key_1': 'value'} + second_match = {'key_2': 'value'} + collection = [first_match, second_match] + settings = [{'name': 'enabled', 'value': collection}] + with patch.dict(win_iis.__salt__), \ + patch('salt.modules.win_iis.get_webconfiguration_settings', + MagicMock(return_value=settings)): + ret = win_iis._collection_match_to_index('pspath', 'colfilter', 'name', bad_match) + self.assertEqual(ret, -1) + ret = win_iis._collection_match_to_index('pspath', 'colfilter', 'name', first_match) + self.assertEqual(ret, 0) + ret = win_iis._collection_match_to_index('pspath', 'colfilter', 'name', second_match) + self.assertEqual(ret, 1) + + def test__prepare_settings(self): + simple_setting = {'name': 'value', 'filter': 'value'} + collection_setting = {'name': 'Collection[{yaml:\n\tdata}]', 'filter': 'value'} + with patch.dict(win_iis.__salt__), \ + patch('salt.modules.win_iis._collection_match_to_index', + MagicMock(return_value=0)): + ret = win_iis._prepare_settings('pspath', [ + simple_setting, collection_setting, {'invalid': 'setting'}, {'name': 'filter-less_setting'} + ]) + self.assertEqual(ret, [simple_setting, collection_setting]) + + @patch('salt.modules.win_iis.log') + def test_get_webconfiguration_settings_empty(self, mock_log): + ret = win_iis.get_webconfiguration_settings('name', settings=[]) + mock_log.warning.assert_called_once_with('No settings provided') + self.assertEqual(ret, {}) + + def test_get_webconfiguration_settings(self): + # Setup + name = 'IIS' + collection_setting = {'name': 'Collection[{yaml:\n\tdata}]', 'filter': 'value'} + filter_setting = {'name': 'enabled', + 'filter': 'system.webServer / security / authentication / anonymousAuthentication'} + settings = [collection_setting, filter_setting] + + ps_cmd = ['$Settings = New-Object System.Collections.ArrayList;', ] + for setting in settings: + ps_cmd.extend([ + "$Property = Get-WebConfigurationProperty -PSPath '{}'".format(name), + "-Name '{name}' -Filter '{filter}' -ErrorAction Stop;".format( + filter=setting['filter'], name=setting['name']), + 'if (([String]::IsNullOrEmpty($Property) -eq $False) -and', + "($Property.GetType()).Name -eq 'ConfigurationAttribute') {", + '$Property = $Property | Select-Object', + '-ExpandProperty Value };', + "$Settings.add(@{{filter='{filter}';name='{name}';value=[String] $Property}})| Out-Null;".format( + filter=setting['filter'], name=setting['name']), + '$Property = $Null;', + ]) + ps_cmd.append('$Settings') + + # Execute + with patch.dict(win_iis.__salt__), \ + patch('salt.modules.win_iis._prepare_settings', + MagicMock(return_value=settings)), \ + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0, 'stdout': '{}'})): + ret = win_iis.get_webconfiguration_settings(name, settings=settings) + + # Verify + win_iis._srvmgr.assert_called_with(cmd=ps_cmd, return_json=True) + self.assertEqual(ret, {}) + + @patch('salt.modules.win_iis.log') + def test_set_webconfiguration_settings_empty(self, mock_log): + ret = win_iis.set_webconfiguration_settings('name', settings=[]) + mock_log.warning.assert_called_once_with('No settings provided') + self.assertEqual(ret, False) + + @patch('salt.modules.win_iis.log') + def test_set_webconfiguration_settings_no_changes(self, mock_log): + # Setup + name = 'IIS' + setting = { + 'name': 'Collection[{yaml:\n\tdata}]', + 'filter': 'system.webServer / security / authentication / anonymousAuthentication', + 'value': [] + } + settings = [setting] + + # Execute + with patch.dict(win_iis.__salt__), \ + patch('salt.modules.win_iis._prepare_settings', + MagicMock(return_value=settings)), \ + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0, 'stdout': '{}'})), \ + patch('salt.modules.win_iis.get_webconfiguration_settings', + MagicMock(return_value=settings)): + ret = win_iis.set_webconfiguration_settings(name, settings=settings) + + # Verify + mock_log.debug.assert_called_with('Settings already contain the provided values.') + self.assertEqual(ret, True) + + @patch('salt.modules.win_iis.log') + def test_set_webconfiguration_settings_failed(self, mock_log): + # Setup + name = 'IIS' + setting = { + 'name': 'Collection[{yaml:\n\tdata}]', + 'filter': 'system.webServer / security / authentication / anonymousAuthentication', + 'value': [] + } + settings = [setting] + + # Execute + with patch.dict(win_iis.__salt__), \ + patch('salt.modules.win_iis._prepare_settings', + MagicMock(return_value=settings)), \ + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0, 'stdout': '{}'})), \ + patch('salt.modules.win_iis.get_webconfiguration_settings', + MagicMock(side_effect=[[], [{'value': 'unexpected_change!'}]])): + + ret = win_iis.set_webconfiguration_settings(name, settings=settings) + + # Verify + self.assertEqual(ret, False) + mock_log.error.assert_called_with('Failed to change settings: %s', settings) + + @patch('salt.modules.win_iis.log') + def test_set_webconfiguration_settings(self, mock_log): + # Setup + name = 'IIS' + setting = { + 'name': 'Collection[{yaml:\n\tdata}]', + 'filter': 'system.webServer / security / authentication / anonymousAuthentication', + 'value': [] + } + settings = [setting] + + # Execute + with patch.dict(win_iis.__salt__), \ + patch('salt.modules.win_iis._prepare_settings', + MagicMock(return_value=settings)), \ + patch('salt.modules.win_iis._srvmgr', + MagicMock(return_value={'retcode': 0, 'stdout': '{}'})), \ + patch('salt.modules.win_iis.get_webconfiguration_settings', + MagicMock(side_effect=[[], settings])): + ret = win_iis.set_webconfiguration_settings(name, settings=settings) + + # Verify + self.assertEqual(ret, True) + mock_log.debug.assert_called_with('Settings configured successfully: %s', settings) diff --git a/tests/unit/modules/test_win_ip.py b/tests/unit/modules/test_win_ip.py index c86966830873..9b135475fda0 100644 --- a/tests/unit/modules/test_win_ip.py +++ b/tests/unit/modules/test_win_ip.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, call ) @@ -38,7 +36,6 @@ 'Connect state: Connected') -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinShadowTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_ip diff --git a/tests/unit/modules/test_win_lgpo.py b/tests/unit/modules/test_win_lgpo.py index b666c99e04b9..7ed71a3ffdc6 100644 --- a/tests/unit/modules/test_win_lgpo.py +++ b/tests/unit/modules/test_win_lgpo.py @@ -7,10 +7,13 @@ from __future__ import absolute_import, unicode_literals, print_function # Import Salt Testing Libs -from tests.support.unit import TestCase +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase, skipIf +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.modules.win_lgpo as win_lgpo +import salt.utils.platform class WinSystemTestCase(TestCase): @@ -53,3 +56,207 @@ def test__encode_string_none(self): ''' value = win_lgpo._encode_string(None) self.assertEqual(value, self.encoded_null) + + +@skipIf(not salt.utils.platform.is_windows(), 'Not a Windows system') +class WinLgpoNetShTestCase(TestCase, LoaderModuleMockMixin): + ''' + NetSH test cases + ''' + + def setup_loader_modules(self): + return {win_lgpo: { + '__context__': {} + }} + + def test__set_netsh_value_firewall(self): + ''' + Test setting the firewall inbound policy + ''' + context = { + 'lgpo.netsh_data': { + 'Private': { + 'Inbound': 'Block'}}} + expected = { + 'lgpo.netsh_data': { + 'Private': { + 'Inbound': 'Allow'}}} + with patch('salt.utils.win_lgpo_netsh.set_firewall_settings', + MagicMock(return_value=True)),\ + patch.dict(win_lgpo.__context__, context): + result = win_lgpo._set_netsh_value(profile='Private', + section='firewallpolicy', + option='Inbound', + value='Allow') + self.assertTrue(result) + self.assertEqual(win_lgpo.__context__, expected) + + def test__set_netsh_value_settings(self): + ''' + Test setting firewall settings + ''' + context = { + 'lgpo.netsh_data': { + 'private': { + 'localfirewallrules': 'disable'}}} + expected = { + 'lgpo.netsh_data': { + 'private': { + 'localfirewallrules': 'enable'}}} + with patch('salt.utils.win_lgpo_netsh.set_settings', + MagicMock(return_value=True)), \ + patch.dict(win_lgpo.__context__, context): + result = win_lgpo._set_netsh_value(profile='private', + section='settings', + option='localfirewallrules', + value='enable') + self.assertTrue(result) + self.assertEqual(win_lgpo.__context__, expected) + + def test__set_netsh_value_state(self): + ''' + Test setting the firewall state + ''' + context = { + 'lgpo.netsh_data': { + 'private': { + 'State': 'notconfigured'}}} + expected = { + 'lgpo.netsh_data': { + 'private': { + 'State': 'on'}}} + with patch('salt.utils.win_lgpo_netsh.set_state', + MagicMock(return_value=True)), \ + patch.dict(win_lgpo.__context__, context): + result = win_lgpo._set_netsh_value(profile='private', + section='state', + option='unused', + value='on') + self.assertTrue(result) + self.assertEqual(win_lgpo.__context__, expected) + + def test__set_netsh_value_logging(self): + ''' + Test setting firewall logging + ''' + context = { + 'lgpo.netsh_data': { + 'private': { + 'allowedconnections': 'notconfigured'}}} + expected = { + 'lgpo.netsh_data': { + 'private': { + 'allowedconnections': 'enable'}}} + with patch('salt.utils.win_lgpo_netsh.set_logging_settings', + MagicMock(return_value=True)), \ + patch.dict(win_lgpo.__context__, context): + result = win_lgpo._set_netsh_value(profile='private', + section='logging', + option='allowedconnections', + value='enable') + self.assertTrue(result) + self.assertEqual(win_lgpo.__context__, expected) + + +class WinLgpoSeceditTestCase(TestCase, LoaderModuleMockMixin): + ''' + Secedit test cases + ''' + + @classmethod + def setUpClass(cls): + cls.secedit_data = [ + '[Unicode]', + 'Unicode=yes', + '[System Access]', + 'MinimumPasswordAge = 0', + 'MaximumPasswordAge = 42', + '[Event Audit]', + 'AuditSystemEvents = 0', + 'AuditLogonEvents = 0', + '[Registry Values]', + r'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel=4,0', + r'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SetCommand=4,0', + '[Privilege Rights]', + 'SeNetworkLogonRight = *S-1-1-0,*S-1-5-32-544,*S-1-5-32-545,*S-1-5-32-551', + 'SeBackupPrivilege = *S-1-5-32-544,*S-1-5-32-551', + '[Version]', + 'signature="$CHICAGO$"', + 'Revision=1'] + + @classmethod + def tearDownClass(cls): + del cls.secedit_data + + def setup_loader_modules(self): + return {win_lgpo: { + '__context__': {}, + '__opts__': {'cachedir': 'C:\\cachedir'}, + '__salt__': {} + }} + + def test__get_secedit_data(self): + ''' + Test getting secedit data and loading it into __context__ + ''' + expected = { + 'AuditLogonEvents': '0', + 'AuditSystemEvents': '0', + r'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SecurityLevel': '4,0', + r'MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Setup\RecoveryConsole\SetCommand': '4,0', + 'MaximumPasswordAge': '42', + 'MinimumPasswordAge': '0', + 'Revision': '1', + 'SeBackupPrivilege': '*S-1-5-32-544,*S-1-5-32-551', + 'SeNetworkLogonRight': '*S-1-1-0,*S-1-5-32-544,*S-1-5-32-545,*S-1-5-32-551', + 'Unicode': 'yes', + 'signature': '"$CHICAGO$"'} + with patch.object(win_lgpo, '_load_secedit_data', + MagicMock(return_value=self.secedit_data)): + result = win_lgpo._get_secedit_data() + self.assertDictEqual(result, expected) + self.assertDictEqual(win_lgpo.__context__['lgpo.secedit_data'], + expected) + + def test__get_secedit_value(self): + ''' + Test getting a specific secedit value + ''' + with patch.object(win_lgpo, '_load_secedit_data', + MagicMock(return_value=self.secedit_data)): + result = win_lgpo._get_secedit_value('AuditSystemEvents') + self.assertEqual(result, '0') + + def test__get_secedit_value_not_defined(self): + ''' + Test getting a secedit value that is undefined + ''' + with patch.object(win_lgpo, '_load_secedit_data', + MagicMock(return_value=self.secedit_data)): + result = win_lgpo._get_secedit_value('UndefinedKey') + self.assertEqual(result, 'Not Defined') + + def test__write_secedit_data(self): + ''' + Test writing secedit data and updating the __context__ + ''' + mock_true = MagicMock(return_value=True) + mock_false = MagicMock(return_value=False) + mock_retcode = MagicMock(return_value=0) + new_secedit_data = {'System Access': ['MaximumPasswordAge=100']} + with patch.object(win_lgpo, '_load_secedit_data', + MagicMock(return_value=self.secedit_data)),\ + patch.dict(win_lgpo.__salt__, {'file.write': mock_true, + 'file.file_exists': mock_false, + 'cmd.retcode': mock_retcode}): + # Populate __context__['lgpo.secedit_data'] + # It will have been run before this function is called + win_lgpo._get_secedit_data() + self.assertEqual( + win_lgpo.__context__['lgpo.secedit_data']['MaximumPasswordAge'], + '42') + result = win_lgpo._write_secedit_data(new_secedit_data) + self.assertTrue(result) + self.assertEqual( + win_lgpo.__context__['lgpo.secedit_data']['MaximumPasswordAge'], + '100') diff --git a/tests/unit/modules/test_win_network.py b/tests/unit/modules/test_win_network.py index 411b5f9cf8a3..a527c0ddf62b 100644 --- a/tests/unit/modules/test_win_network.py +++ b/tests/unit/modules/test_win_network.py @@ -13,8 +13,6 @@ MagicMock, patch, Mock, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -63,7 +61,6 @@ def __exit__(self, *exc_info): return False -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinNetworkTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_network diff --git a/tests/unit/modules/test_win_path.py b/tests/unit/modules/test_win_path.py index 891a13149797..da117fb24f2d 100644 --- a/tests/unit/modules/test_win_path.py +++ b/tests/unit/modules/test_win_path.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -22,7 +20,6 @@ import salt.utils.stringutils -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinPathTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_path diff --git a/tests/unit/modules/test_win_pkg.py b/tests/unit/modules/test_win_pkg.py index 50881a634845..50cd3a8e1106 100644 --- a/tests/unit/modules/test_win_pkg.py +++ b/tests/unit/modules/test_win_pkg.py @@ -9,13 +9,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.mock import MagicMock, patch -from tests.support.unit import TestCase +from tests.support.unit import TestCase, skipIf # Import Salt Libs import salt.modules.pkg_resource as pkg_resource import salt.modules.win_pkg as win_pkg +import salt.utils.data +import salt.utils.platform +@skipIf(not salt.utils.platform.is_windows(), "Must be on Windows!") class WinPkgInstallTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_pkg @@ -151,3 +154,67 @@ def test_pkg_install_existing_with_version(self): expected = {} result = win_pkg.install(name='nsis', version='3.03') self.assertDictEqual(expected, result) + + def test_pkg_install_name(self): + ''' + test pkg.install name extra_install_flags + ''' + + ret__get_package_info = {'3.03': {'uninstaller': '%program.exe', 'reboot': False, + 'msiexec': False, + 'installer': 'runme.exe', + 'uninstall_flags': '/S', 'locale': 'en_US', 'install_flags': '/s', + 'full_name': 'Firebox 3.03 (x86 en-US)'}} + + mock_cmd_run_all = MagicMock(return_value={'retcode': 0}) + with patch.object(salt.utils.data, 'is_true', MagicMock(return_value=True)),\ + patch.object(win_pkg, '_get_package_info', MagicMock(return_value=ret__get_package_info)),\ + patch.dict(win_pkg.__salt__, {'pkg_resource.parse_targets': + MagicMock(return_value=[{'firebox': '3.03'}, None]), + 'cp.is_cached': + MagicMock(return_value='C:\\fake\\path.exe'), + 'cmd.run_all': mock_cmd_run_all}): + ret = win_pkg.install(name='firebox', version='3.03', extra_install_flags='-e True -test_flag True') + self.assertTrue('-e True -test_flag True' in str(mock_cmd_run_all.call_args[0])) + + def test_pkg_install_single_pkg(self): + ''' + test pkg.install pkg with extra_install_flags + ''' + ret__get_package_info = {'3.03': {'uninstaller': '%program.exe', 'reboot': False, + 'msiexec': False, + 'installer': 'runme.exe', + 'uninstall_flags': '/S', 'locale': 'en_US', 'install_flags': '/s', + 'full_name': 'Firebox 3.03 (x86 en-US)'}} + + mock_cmd_run_all = MagicMock(return_value={'retcode': 0}) + with patch.object(salt.utils.data, 'is_true', MagicMock(return_value=True)), \ + patch.object(win_pkg, '_get_package_info', MagicMock(return_value=ret__get_package_info)), \ + patch.dict(win_pkg.__salt__, {'pkg_resource.parse_targets': + MagicMock(return_value=[{'firebox': '3.03'}, None]), + 'cp.is_cached': + MagicMock(return_value='C:\\fake\\path.exe'), + 'cmd.run_all': mock_cmd_run_all}): + ret = win_pkg.install(pkgs=['firebox'], version='3.03', extra_install_flags='-e True -test_flag True') + self.assertTrue('-e True -test_flag True' in str(mock_cmd_run_all.call_args[0])) + + def test_pkg_install_multiple_pkgs(self): + ''' + test pkg.install pkg with extra_install_flags + ''' + ret__get_package_info = {'3.03': {'uninstaller': '%program.exe', 'reboot': False, + 'msiexec': False, + 'installer': 'runme.exe', + 'uninstall_flags': '/S', 'locale': 'en_US', 'install_flags': '/s', + 'full_name': 'Firebox 3.03 (x86 en-US)'}} + + mock_cmd_run_all = MagicMock(return_value={'retcode': 0}) + with patch.object(salt.utils.data, 'is_true', MagicMock(return_value=True)), \ + patch.object(win_pkg, '_get_package_info', MagicMock(return_value=ret__get_package_info)), \ + patch.dict(win_pkg.__salt__, {'pkg_resource.parse_targets': + MagicMock(return_value=[{'firebox': '3.03', 'got': '3.03'}, None]), + 'cp.is_cached': + MagicMock(return_value='C:\\fake\\path.exe'), + 'cmd.run_all': mock_cmd_run_all}): + ret = win_pkg.install(pkgs=['firebox', 'got'], extra_install_flags='-e True -test_flag True') + self.assertFalse('-e True -test_flag True' in str(mock_cmd_run_all.call_args[0])) diff --git a/tests/unit/modules/test_win_pki.py b/tests/unit/modules/test_win_pki.py index a37c98630656..e42e06558a4f 100644 --- a/tests/unit/modules/test_win_pki.py +++ b/tests/unit/modules/test_win_pki.py @@ -14,12 +14,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, ) CERT_PATH = r'C:\certs\testdomain.local.cer' @@ -87,7 +85,6 @@ }] -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinPkiTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_pki diff --git a/tests/unit/modules/test_win_powercfg.py b/tests/unit/modules/test_win_powercfg.py index 25b21bffd6e8..38434cb2486b 100644 --- a/tests/unit/modules/test_win_powercfg.py +++ b/tests/unit/modules/test_win_powercfg.py @@ -8,17 +8,14 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch, call ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PowerCfgTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the powercfg state diff --git a/tests/unit/modules/test_win_psget.py b/tests/unit/modules/test_win_psget.py new file mode 100644 index 000000000000..51c0c4d235f5 --- /dev/null +++ b/tests/unit/modules/test_win_psget.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +# Import Python Libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Libs +import salt.modules.win_psget as win_psget + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +BOOTSTRAP_PS_STR = ''' + + + NuGet + + 2 + 8 + 5 + 208 + + C:\\Program Files\\PackageManagement\\ProviderAssemblies\\nuget\\2.8.5 +.208\\Microsoft.PackageManagement.NuGetProvider.dll + +''' + +AVAIL_MODULES_PS_STR = ''' + + + ActOnCmdlets + CData Cmdlets for Act-On + + + FinancialEdgeNXTCmdlets + CData Cmdlets for Blackbaud Financial Edge NXT + + + GoogleCMCmdlets + CData Cmdlets for Google Campaign Manager + + + DHCPMigration + A module to copy various DHCP information from 1 server to another. + +''' + + +class WinPsgetCase(TestCase, LoaderModuleMockMixin): + ''' + Test cases for salt.modules.win_psget + ''' + def setup_loader_modules(self): + return {win_psget: {}} + + def test_bootstrap(self): + mock_read_ok = MagicMock(return_value={'pid': 78, + 'retcode': 0, + 'stderr': '', + 'stdout': BOOTSTRAP_PS_STR}) + + with patch.dict(win_psget.__salt__, {'cmd.run_all': mock_read_ok}): + self.assertTrue('NuGet' in win_psget.bootstrap()) + + def test_avail_modules(self): + mock_read_ok = MagicMock(return_value={'pid': 78, + 'retcode': 0, + 'stderr': '', + 'stdout': AVAIL_MODULES_PS_STR}) + + with patch.dict(win_psget.__salt__, {'cmd.run_all': mock_read_ok}): + self.assertTrue('DHCPMigration' in win_psget.avail_modules(False)) + self.assertTrue('DHCPMigration' in win_psget.avail_modules(True)) diff --git a/tests/unit/modules/test_win_service.py b/tests/unit/modules/test_win_service.py index 61a0d14a97f1..faf8ef881119 100644 --- a/tests/unit/modules/test_win_service.py +++ b/tests/unit/modules/test_win_service.py @@ -12,8 +12,6 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -29,7 +27,6 @@ WINAPI = False -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinServiceTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_service diff --git a/tests/unit/modules/test_win_snmp.py b/tests/unit/modules/test_win_snmp.py index f60a0515db3b..dfdfe942ab8d 100644 --- a/tests/unit/modules/test_win_snmp.py +++ b/tests/unit/modules/test_win_snmp.py @@ -15,18 +15,15 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, ) COMMUNITY_NAMES = {'TestCommunity': 'Read Create'} -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinSnmpTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_snmp diff --git a/tests/unit/modules/test_win_status.py b/tests/unit/modules/test_win_status.py index 314467023e44..f52c3fd7558a 100644 --- a/tests/unit/modules/test_win_status.py +++ b/tests/unit/modules/test_win_status.py @@ -9,7 +9,7 @@ # Import Salt Testing libs from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, Mock, patch, ANY +from tests.support.mock import Mock, patch, ANY try: import wmi @@ -20,7 +20,6 @@ import salt.modules.win_status as status -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(status.HAS_WMI is False, 'This test requires Windows') class TestProcsBase(TestCase): def __init__(self, *args, **kwargs): diff --git a/tests/unit/modules/test_win_system.py b/tests/unit/modules/test_win_system.py index 45a7663a495b..13c62919e354 100644 --- a/tests/unit/modules/test_win_system.py +++ b/tests/unit/modules/test_win_system.py @@ -14,8 +14,6 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -23,7 +21,6 @@ import salt.utils.platform -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') class WinSystemTestCase(TestCase, LoaderModuleMockMixin): ''' diff --git a/tests/unit/modules/test_win_wusa.py b/tests/unit/modules/test_win_wusa.py index ee696063ebce..8396fb802fde 100644 --- a/tests/unit/modules/test_win_wusa.py +++ b/tests/unit/modules/test_win_wusa.py @@ -8,7 +8,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.mock import patch, MagicMock from tests.support.unit import TestCase, skipIf # Import Salt Libs @@ -17,7 +17,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') class WinWusaTestCase(TestCase, LoaderModuleMockMixin): ''' diff --git a/tests/unit/modules/test_x509.py b/tests/unit/modules/test_x509.py index 7030f96484a3..624a927becd5 100644 --- a/tests/unit/modules/test_x509.py +++ b/tests/unit/modules/test_x509.py @@ -30,8 +30,6 @@ from tests.support.mock import ( patch, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) from salt.modules import x509 @@ -45,7 +43,6 @@ HAS_M2CRYPTO = False -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not bool(pytest), False) class X509TestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/modules/test_xapi_virt.py b/tests/unit/modules/test_xapi_virt.py index 38db76187bb8..279dd6bf203e 100644 --- a/tests/unit/modules/test_xapi_virt.py +++ b/tests/unit/modules/test_xapi_virt.py @@ -8,13 +8,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( mock_open, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -64,7 +62,6 @@ def logout(): return Mockxapi() -@skipIf(NO_MOCK, NO_MOCK_REASON) class XapiTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.xapi diff --git a/tests/unit/modules/test_yumpkg.py b/tests/unit/modules/test_yumpkg.py index 6113d3a4b131..fe9df4a47f69 100644 --- a/tests/unit/modules/test_yumpkg.py +++ b/tests/unit/modules/test_yumpkg.py @@ -11,8 +11,6 @@ Mock, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt libs @@ -60,7 +58,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class YumTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.yumpkg diff --git a/tests/unit/modules/test_zabbix.py b/tests/unit/modules/test_zabbix.py index 6f120d56fe65..d88732d8e746 100644 --- a/tests/unit/modules/test_zabbix.py +++ b/tests/unit/modules/test_zabbix.py @@ -10,12 +10,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) from salt.exceptions import SaltException @@ -96,7 +94,6 @@ 'empty_list': [{'dict_key': 'dic_val'}]}} -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZabbixTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.zabbix diff --git a/tests/unit/modules/test_zcbuildout.py b/tests/unit/modules/test_zcbuildout.py index aa678a97913b..cb853edf01c0 100644 --- a/tests/unit/modules/test_zcbuildout.py +++ b/tests/unit/modules/test_zcbuildout.py @@ -2,12 +2,14 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import tempfile import logging +import os import shutil +import subprocess +import tempfile # Import 3rd-party libs + # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext import six from salt.ext.six.moves.urllib.error import URLError @@ -15,18 +17,18 @@ # pylint: enable=import-error,no-name-in-module,redefined-builtin # Import Salt Testing libs +from tests.support.helpers import requires_network, patched_environ from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import FILES, TMP +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase, skipIf -from tests.support.helpers import requires_network, skip_if_binaries_missing # Import Salt libs import salt.utils.files import salt.utils.path +import salt.utils.platform import salt.modules.zcbuildout as buildout import salt.modules.cmdmod as cmd - -ROOT = os.path.join(FILES, 'file', 'base', 'buildout') +from salt.ext import six KNOWN_VIRTUALENV_BINARY_NAMES = ( 'virtualenv', @@ -35,6 +37,13 @@ 'virtualenv-2.7' ) +# temp workaround since virtualenv pip wheel package does not include +# backports.ssl_match_hostname on windows python2.7 +if salt.utils.platform.is_windows() and six.PY2: + KNOWN_VIRTUALENV_BINARY_NAMES = ( + 'c:\\Python27\\Scripts\\virtualenv.EXE', + ) + BOOT_INIT = { 1: [ 'var/ver/1/bootstrap/bootstrap.py', @@ -48,11 +57,10 @@ def download_to(url, dest): - with salt.utils.files.fopen(dest, 'w') as fic: + with salt.utils.files.fopen(dest, 'wb') as fic: fic.write(urlopen(url, timeout=10).read()) -@skipIf(True, 'These tests are not running reliably') class Base(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -68,9 +76,11 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - if not os.path.isdir(TMP): - os.makedirs(TMP) - cls.rdir = tempfile.mkdtemp(dir=TMP) + if not os.path.isdir(RUNTIME_VARS.TMP): + os.makedirs(RUNTIME_VARS.TMP) + + cls.root = os.path.join(RUNTIME_VARS.BASE_FILES, 'buildout') + cls.rdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) cls.tdir = os.path.join(cls.rdir, 'test') for idx, url in six.iteritems(buildout._URL_VERSIONS): log.debug('Downloading bootstrap from %s', url) @@ -83,26 +93,49 @@ def setUpClass(cls): log.debug('Failed to download %s', url) # creating a new setuptools install cls.ppy_st = os.path.join(cls.rdir, 'psetuptools') - cls.py_st = os.path.join(cls.ppy_st, 'bin', 'python') - ret1 = buildout._Popen(( - '{0} --no-site-packages {1};' - '{1}/bin/pip install -U setuptools; ' - '{1}/bin/easy_install -U distribute;').format( - salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), - cls.ppy_st - ) - ) - assert ret1['retcode'] == 0 - - @classmethod - def tearDownClass(cls): - if os.path.isdir(cls.rdir): - shutil.rmtree(cls.rdir) + if salt.utils.platform.is_windows(): + cls.bin_st = os.path.join(cls.ppy_st, 'Scripts') + cls.py_st = os.path.join(cls.bin_st, 'python') + else: + cls.bin_st = os.path.join(cls.ppy_st, 'bin') + cls.py_st = os.path.join(cls.bin_st, 'python') + # `--no-site-packages` has been deprecated + # https://virtualenv.pypa.io/en/stable/reference/#cmdoption-no-site-packages + subprocess.check_call([ + salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), + cls.ppy_st + ]) + subprocess.check_call([ + os.path.join(cls.bin_st, 'pip'), + 'install', + '-U', + 'setuptools', + ]) + # distribute has been merged back in to setuptools as of v0.7. So, no + # need to upgrade distribute, but this seems to be the only way to get + # the binary in the right place + # https://packaging.python.org/key_projects/#setuptools + # Additionally, this part may fail if the certificate store is outdated + # on Windows, as it would be in a fresh installation for example. The + # following commands will fix that. This should be part of the golden + # images. (https://github.com/saltstack/salt-jenkins/pull/1479) + # certutil -generateSSTFromWU roots.sst + # powershell "(Get-ChildItem -Path .\roots.sst) | Import-Certificate -CertStoreLocation Cert:\LocalMachine\Root" + subprocess.check_call([ + os.path.join(cls.bin_st, 'easy_install'), + '-U', + 'distribute', + ]) def setUp(self): + if salt.utils.platform.is_darwin and six.PY3: + self.patched_environ = patched_environ(__cleanup__=['__PYVENV_LAUNCHER__']) + self.patched_environ.__enter__() + self.addCleanup(self.patched_environ.__exit__) + super(Base, self).setUp() self._remove_dir() - shutil.copytree(ROOT, self.tdir) + shutil.copytree(self.root, self.tdir) for idx in BOOT_INIT: path = os.path.join( @@ -120,19 +153,17 @@ def _remove_dir(self): shutil.rmtree(self.tdir) -@skipIf(True, 'These tests are not running reliably') @skipIf(salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES) is None, "The 'virtualenv' packaged needs to be installed") -@skip_if_binaries_missing(['tar']) class BuildoutTestCase(Base): @requires_network() def test_onlyif_unless(self): b_dir = os.path.join(self.tdir, 'b') - ret = buildout.buildout(b_dir, onlyif='/bin/false') + ret = buildout.buildout(b_dir, onlyif=RUNTIME_VARS.SHELL_FALSE_PATH) self.assertTrue(ret['comment'] == 'onlyif condition is false') self.assertTrue(ret['status'] is True) - ret = buildout.buildout(b_dir, unless='/bin/true') + ret = buildout.buildout(b_dir, unless=RUNTIME_VARS.SHELL_TRUE_PATH) self.assertTrue(ret['comment'] == 'unless condition is true') self.assertTrue(ret['status'] is True) @@ -232,16 +263,16 @@ def test_get_bootstrap_content(self): self.assertEqual( '', buildout._get_bootstrap_content( - os.path.join(self.tdir, '/non/existing')) + os.path.join(self.tdir, 'non', 'existing')) ) self.assertEqual( '', buildout._get_bootstrap_content( - os.path.join(self.tdir, 'var/tb/1'))) + os.path.join(self.tdir, 'var', 'tb', '1'))) self.assertEqual( - 'foo\n', + 'foo{0}'.format(os.linesep), buildout._get_bootstrap_content( - os.path.join(self.tdir, 'var/tb/2'))) + os.path.join(self.tdir, 'var', 'tb', '2'))) @requires_network() def test_logger_clean(self): @@ -277,16 +308,16 @@ def test_logger_loggers(self): @requires_network() def test__find_cfgs(self): result = sorted( - [a.replace(ROOT, '') for a in buildout._find_cfgs(ROOT)]) + [a.replace(self.root, '') for a in buildout._find_cfgs(self.root)]) assertlist = sorted( - ['/buildout.cfg', - '/c/buildout.cfg', - '/etc/buildout.cfg', - '/e/buildout.cfg', - '/b/buildout.cfg', - '/b/bdistribute/buildout.cfg', - '/b/b2/buildout.cfg', - '/foo/buildout.cfg']) + [os.path.join(os.sep, 'buildout.cfg'), + os.path.join(os.sep, 'c', 'buildout.cfg'), + os.path.join(os.sep, 'etc', 'buildout.cfg'), + os.path.join(os.sep, 'e', 'buildout.cfg'), + os.path.join(os.sep, 'b', 'buildout.cfg'), + os.path.join(os.sep, 'b', 'bdistribute', 'buildout.cfg'), + os.path.join(os.sep, 'b', 'b2', 'buildout.cfg'), + os.path.join(os.sep, 'foo', 'buildout.cfg')]) self.assertEqual(result, assertlist) @requires_network() @@ -315,63 +346,70 @@ def skip_test_upgrade_bootstrap(self): @skipIf(salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES) is None, 'The \'virtualenv\' packaged needs to be installed') -@skipIf(True, 'These tests are not running reliably') class BuildoutOnlineTestCase(Base): @classmethod def setUpClass(cls): super(BuildoutOnlineTestCase, cls).setUpClass() - cls.ppy_dis = os.path.join(cls.rdir, 'pdistibute') + cls.ppy_dis = os.path.join(cls.rdir, 'pdistribute') cls.ppy_blank = os.path.join(cls.rdir, 'pblank') cls.py_dis = os.path.join(cls.ppy_dis, 'bin', 'python') cls.py_blank = os.path.join(cls.ppy_blank, 'bin', 'python') # creating a distribute based install try: - ret20 = buildout._Popen(( - '{0} --no-site-packages --no-setuptools --no-pip {1}'.format( - salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), - cls.ppy_dis - ) - )) - except buildout._BuildoutError: - ret20 = buildout._Popen(( - '{0} --no-site-packages {1}'.format( - salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), - cls.ppy_dis - )) + # `--no-site-packages` has been deprecated + # https://virtualenv.pypa.io/en/stable/reference/#cmdoption-no-site-packages + subprocess.check_call([ + salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), + '--no-setuptools', + '--no-pip', + cls.ppy_dis, + ]) + except subprocess.CalledProcessError: + subprocess.check_call([ + salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), + cls.ppy.dis, + ]) + + url = ( + 'https://pypi.python.org/packages/source' + '/d/distribute/distribute-0.6.43.tar.gz' + ) + download_to( + url, + os.path.join(cls.ppy_dis, 'distribute-0.6.43.tar.gz'), ) - assert ret20['retcode'] == 0 - download_to('https://pypi.python.org/packages/source' - '/d/distribute/distribute-0.6.43.tar.gz', - os.path.join(cls.ppy_dis, 'distribute-0.6.43.tar.gz')) + subprocess.check_call([ + 'tar', + '-C', + cls.ppy_dis, + '-xzvf', + '{0}/distribute-0.6.43.tar.gz'.format(cls.ppy_dis), + ]) - ret2 = buildout._Popen(( - 'cd {0} &&' - ' tar xzvf distribute-0.6.43.tar.gz && cd distribute-0.6.43 &&' - ' {0}/bin/python setup.py install' - ).format(cls.ppy_dis)) - assert ret2['retcode'] == 0 + subprocess.check_call([ + '{0}/bin/python'.format(cls.ppy_dis), + '{0}/distribute-0.6.43/setup.py'.format(cls.ppy_dis), + 'install', + ]) # creating a blank based install try: - ret3 = buildout._Popen(( - '{0} --no-site-packages --no-setuptools --no-pip {1}'.format( - salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), - cls.ppy_blank - ) - )) - except buildout._BuildoutError: - ret3 = buildout._Popen(( - '{0} --no-site-packages {1}'.format( - salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), - cls.ppy_blank - ) - )) - - assert ret3['retcode'] == 0 + subprocess.check_call([ + salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), + '--no-setuptools', + '--no-pip', + cls.ppy_blank, + ]) + except subprocess.CalledProcessError: + subprocess.check_call([ + salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), + cls.ppy_blank, + ]) @requires_network() + @skipIf(True, 'TODO this test should probably be fixed') def test_buildout_bootstrap(self): b_dir = os.path.join(self.tdir, 'b') bd_dir = os.path.join(self.tdir, 'b', 'bdistribute') @@ -447,7 +485,7 @@ def test_buildout(self): self.assertTrue(ret['status']) self.assertTrue('Creating directory' in out) self.assertTrue('Installing a.' in out) - self.assertTrue('psetuptools/bin/python bootstrap.py' in comment) + self.assertTrue('{0} bootstrap.py'.format(self.py_st) in comment) self.assertTrue('buildout -c buildout.cfg' in comment) ret = buildout.buildout(b_dir, parts=['a', 'b', 'c'], @@ -472,7 +510,6 @@ def test_buildout(self): # TODO: Is this test even still needed? -@skipIf(True, 'These tests are not running reliably') class BuildoutAPITestCase(TestCase): def test_merge(self): diff --git a/tests/unit/modules/test_zfs.py b/tests/unit/modules/test_zfs.py index 9e858e2fcb93..94573499bf04 100644 --- a/tests/unit/modules/test_zfs.py +++ b/tests/unit/modules/test_zfs.py @@ -13,18 +13,14 @@ from __future__ import absolute_import, unicode_literals, print_function # Import Salt Testing libs +from tests.support.zfs import ZFSMockData from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, ) -# Import test data from salt.utils.zfs test -from tests.unit.utils.test_zfs import utils_patch - # Import Salt Execution module to test import salt.utils.zfs import salt.modules.zfs as zfs @@ -36,13 +32,16 @@ # Skip this test case if we don't have access to mock! -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZfsTestCase(TestCase, LoaderModuleMockMixin): ''' This class contains a set of functions that test salt.modules.zfs module ''' def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() + self.utils_patch = ZFSMockData().get_patched_utils() + for key in ('opts', 'utils_patch'): + self.addCleanup(delattr, self, key) + utils = salt.loader.utils( opts, whitelist=['zfs', 'args', 'systemd', 'path', 'platform']) @@ -65,7 +64,7 @@ def test_exists_success(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertTrue(zfs.exists('myzpool/mydataset')) def test_exists_failure_not_exists(self): @@ -78,7 +77,7 @@ def test_exists_failure_not_exists(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertFalse(zfs.exists('myzpool/mydataset')) def test_exists_failure_invalid_name(self): @@ -91,7 +90,7 @@ def test_exists_failure_invalid_name(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertFalse(zfs.exists('myzpool/')) def test_create_success(self): @@ -105,7 +104,7 @@ def test_create_success(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.create('myzpool/mydataset')) def test_create_success_with_create_parent(self): @@ -119,7 +118,7 @@ def test_create_success_with_create_parent(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.create('myzpool/mydataset/mysubdataset', create_parent=True)) def test_create_success_with_properties(self): @@ -133,7 +132,7 @@ def test_create_success_with_properties(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual( res, zfs.create( @@ -159,7 +158,7 @@ def test_create_error_missing_dataset(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.create('myzpool')) def test_create_error_trailing_slash(self): @@ -176,7 +175,7 @@ def test_create_error_trailing_slash(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.create('myzpool/')) def test_create_error_no_such_pool(self): @@ -193,7 +192,7 @@ def test_create_error_no_such_pool(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.create('myzpool/mydataset')) def test_create_error_missing_parent(self): @@ -210,7 +209,7 @@ def test_create_error_missing_parent(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.create('myzpool/mydataset/mysubdataset')) def test_destroy_success(self): @@ -224,7 +223,7 @@ def test_destroy_success(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.destroy('myzpool/mydataset')) def test_destroy_error_not_exists(self): @@ -241,7 +240,7 @@ def test_destroy_error_not_exists(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.destroy('myzpool/mydataset')) def test_destroy_error_has_children(self): @@ -266,7 +265,7 @@ def test_destroy_error_has_children(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.destroy('myzpool/mydataset')) def test_rename_success(self): @@ -280,7 +279,7 @@ def test_rename_success(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.rename('myzpool/mydataset', 'myzpool/newdataset')) def test_rename_error_not_exists(self): @@ -297,7 +296,7 @@ def test_rename_error_not_exists(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.rename('myzpool/mydataset', 'myzpool/newdataset')) def test_list_success(self): @@ -318,7 +317,7 @@ def test_list_success(self): ret['stderr'] = '' mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.list_('myzpool')) def test_list_parsable_success(self): @@ -339,7 +338,7 @@ def test_list_parsable_success(self): ret['stderr'] = '' mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.list_('myzpool', parsable=False)) def test_list_custom_success(self): @@ -360,7 +359,7 @@ def test_list_custom_success(self): ret['stderr'] = '' mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.list_('myzpool', properties='canmount,used,avail,compression')) def test_list_custom_parsable_success(self): @@ -381,7 +380,7 @@ def test_list_custom_parsable_success(self): ret['stderr'] = '' mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.list_('myzpool', properties='canmount,used,avail,compression', parsable=False)) def test_list_error_no_dataset(self): @@ -395,7 +394,7 @@ def test_list_error_no_dataset(self): ret['stderr'] = '' mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.list_('myzpool')) def test_list_mount_success(self): @@ -415,7 +414,7 @@ def test_list_mount_success(self): ret['stderr'] = '' mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.list_mount()) def test_mount_success(self): @@ -429,7 +428,7 @@ def test_mount_success(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.mount('myzpool/mydataset')) def test_mount_failure(self): @@ -443,7 +442,7 @@ def test_mount_failure(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.mount('myzpool/mydataset')) def test_unmount_success(self): @@ -457,7 +456,7 @@ def test_unmount_success(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.unmount('myzpool/mydataset')) def test_unmount_failure(self): @@ -474,7 +473,7 @@ def test_unmount_failure(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.unmount('myzpool/mydataset')) def test_inherit_success(self): @@ -485,7 +484,7 @@ def test_inherit_success(self): ret = {'pid': 45193, 'retcode': 0, 'stderr': '', 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.inherit('compression', 'myzpool/mydataset')) def test_inherit_failure(self): @@ -499,7 +498,7 @@ def test_inherit_failure(self): ret = {'pid': 43898, 'retcode': 1, 'stderr': "'canmount' property cannot be inherited", 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.inherit('canmount', 'myzpool/mydataset')) def test_diff(self): @@ -521,7 +520,7 @@ def test_diff(self): ret['stderr'] = '' mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.diff('myzpool/mydataset@yesterday', 'myzpool/mydataset')) def test_diff_parsed_time(self): @@ -545,7 +544,7 @@ def test_diff_parsed_time(self): ret['stderr'] = '' mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.diff('myzpool/data@yesterday', 'myzpool/data', parsable=False)) def test_rollback_success(self): @@ -556,7 +555,7 @@ def test_rollback_success(self): ret = {'pid': 56502, 'retcode': 0, 'stderr': '', 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.rollback('myzpool/mydataset@yesterday')) def test_rollback_failure(self): @@ -582,7 +581,7 @@ def test_rollback_failure(self): } mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.rollback('myzpool/mydataset@yesterday')) def test_clone_success(self): @@ -593,7 +592,7 @@ def test_clone_success(self): ret = {'pid': 64532, 'retcode': 0, 'stderr': '', 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.clone('myzpool/mydataset@yesterday', 'myzpool/yesterday')) def test_clone_failure(self): @@ -607,7 +606,7 @@ def test_clone_failure(self): ret = {'pid': 64864, 'retcode': 1, 'stderr': "cannot create 'myzpool/archive/yesterday': parent does not exist", 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.clone('myzpool/mydataset@yesterday', 'myzpool/archive/yesterday')) def test_promote_success(self): @@ -618,7 +617,7 @@ def test_promote_success(self): ret = {'pid': 69075, 'retcode': 0, 'stderr': '', 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.promote('myzpool/yesterday')) def test_promote_failure(self): @@ -632,7 +631,7 @@ def test_promote_failure(self): ret = {'pid': 69209, 'retcode': 1, 'stderr': "cannot promote 'myzpool/yesterday': not a cloned filesystem", 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.promote('myzpool/yesterday')) def test_bookmark_success(self): @@ -644,7 +643,7 @@ def test_bookmark_success(self): ret = {'pid': 20990, 'retcode': 0, 'stderr': '', 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.bookmark('myzpool/mydataset@yesterday', 'myzpool/mydataset#important')) def test_holds_success(self): @@ -658,7 +657,7 @@ def test_holds_success(self): ret = {'pid': 40216, 'retcode': 0, 'stderr': '', 'stdout': 'myzpool/mydataset@baseline\timportant \tWed Dec 23 21:06 2015\nmyzpool/mydataset@baseline\trelease-1.0\tWed Dec 23 21:08 2015'} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.holds('myzpool/mydataset@baseline')) def test_holds_failure(self): @@ -671,7 +670,7 @@ def test_holds_failure(self): ret = {'pid': 40993, 'retcode': 1, 'stderr': "cannot open 'myzpool/mydataset@baseline': dataset does not exist", 'stdout': 'no datasets available'} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.holds('myzpool/mydataset@baseline')) def test_hold_success(self): @@ -682,7 +681,7 @@ def test_hold_success(self): ret = {'pid': 50876, 'retcode': 0, 'stderr': '', 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.hold('important', 'myzpool/mydataset@baseline', 'myzpool/mydataset@release-1.0')) def test_hold_failure(self): @@ -696,7 +695,7 @@ def test_hold_failure(self): ret = {'pid': 51006, 'retcode': 1, 'stderr': "cannot hold snapshot 'myzpool/mydataset@baseline': tag already exists on this dataset", 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.hold('important', 'myzpool/mydataset@baseline')) def test_release_success(self): @@ -707,7 +706,7 @@ def test_release_success(self): ret = {'pid': 50876, 'retcode': 0, 'stderr': '', 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.release('important', 'myzpool/mydataset@baseline', 'myzpool/mydataset@release-1.0')) def test_release_failure(self): @@ -721,7 +720,7 @@ def test_release_failure(self): ret = {'pid': 51006, 'retcode': 1, 'stderr': "cannot release hold from snapshot 'myzpool/mydataset@baseline': no such tag on this dataset", 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.release('important', 'myzpool/mydataset@baseline')) def test_snapshot_success(self): @@ -732,7 +731,7 @@ def test_snapshot_success(self): ret = {'pid': 69125, 'retcode': 0, 'stderr': '', 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.snapshot('myzpool/mydataset@baseline')) def test_snapshot_failure(self): @@ -746,7 +745,7 @@ def test_snapshot_failure(self): ret = {'pid': 68526, 'retcode': 1, 'stderr': "cannot create snapshot 'myzpool/mydataset@baseline': dataset already exists", 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.snapshot('myzpool/mydataset@baseline')) def test_snapshot_failure2(self): @@ -760,7 +759,7 @@ def test_snapshot_failure2(self): ret = {'pid': 69256, 'retcode': 2, 'stderr': "cannot open 'myzpool/mydataset': dataset does not exist\nusage:\n\tsnapshot [-r] [-o property=value] ... @ ...\n\nFor the property list, run: zfs set|get\n\nFor the delegated permission list, run: zfs allow|unallow", 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.snapshot('myzpool/mydataset@baseline')) def test_set_success(self): @@ -771,7 +770,7 @@ def test_set_success(self): ret = {'pid': 79736, 'retcode': 0, 'stderr': '', 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.set('myzpool/mydataset', compression='lz4')) def test_set_failure(self): @@ -785,7 +784,7 @@ def test_set_failure(self): ret = {'pid': 79887, 'retcode': 1, 'stderr': "cannot set property for 'myzpool/mydataset': 'canmount' must be one of 'on | off | noauto'", 'stdout': ''} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.set('myzpool/mydataset', canmount='lz4')) def test_get_success(self): @@ -802,7 +801,7 @@ def test_get_success(self): ret = {'pid': 562, 'retcode': 0, 'stderr': '', 'stdout': 'myzpool\tused\t906238099456'} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.get('myzpool', properties='used', fields='value')) def test_get_parsable_success(self): @@ -819,5 +818,5 @@ def test_get_parsable_success(self): ret = {'pid': 562, 'retcode': 0, 'stderr': '', 'stdout': 'myzpool\tused\t906238099456'} mock_cmd = MagicMock(return_value=ret) with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(res, zfs.get('myzpool', properties='used', fields='value', parsable=False)) diff --git a/tests/unit/modules/test_znc.py b/tests/unit/modules/test_znc.py index 16178b5c5e2e..5cfe82d909b1 100644 --- a/tests/unit/modules/test_znc.py +++ b/tests/unit/modules/test_znc.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.modules.znc as znc -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZncTestCase(TestCase, LoaderModuleMockMixin): ''' TestCase for salt.modules.znc diff --git a/tests/unit/modules/test_zpool.py b/tests/unit/modules/test_zpool.py index 5ac302eb5ac5..8ebb96008c41 100644 --- a/tests/unit/modules/test_zpool.py +++ b/tests/unit/modules/test_zpool.py @@ -13,17 +13,14 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs +from tests.support.zfs import ZFSMockData from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, ) -# Import test data from salt.utils.zfs test -from tests.unit.utils.test_zfs import utils_patch # Import Salt Execution module to test import salt.utils.zfs @@ -37,13 +34,15 @@ # Skip this test case if we don't have access to mock! -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZpoolTestCase(TestCase, LoaderModuleMockMixin): ''' This class contains a set of functions that test salt.modules.zpool module ''' def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() + self.utils_patch = ZFSMockData().get_patched_utils() + for key in ('opts', 'utils_patch'): + self.addCleanup(delattr, self, key) utils = salt.loader.utils( opts, whitelist=['zfs', 'args', 'systemd', 'path', 'platform']) @@ -67,7 +66,7 @@ def test_exists_success(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertTrue(zpool.exists('myzpool')) def test_exists_failure(self): @@ -81,7 +80,7 @@ def test_exists_failure(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertFalse(zpool.exists('myzpool')) def test_healthy(self): @@ -95,7 +94,7 @@ def test_healthy(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertTrue(zpool.healthy()) def test_status(self): @@ -121,7 +120,7 @@ def test_status(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.status() self.assertEqual('ONLINE', ret['mypool']['state']) @@ -144,7 +143,7 @@ def test_iostat(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.iostat('mypool', parsable=False) self.assertEqual('46.7G', ret['mypool']['capacity-alloc']) @@ -172,7 +171,7 @@ def test_iostat_parsable(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.iostat('mypool', parsable=True) self.assertEqual(50143743180, ret['mypool']['capacity-alloc']) @@ -186,7 +185,7 @@ def test_list(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.list_(parsable=False) res = OrderedDict([('mypool', OrderedDict([ ('size', '1.81T'), @@ -208,7 +207,7 @@ def test_list_parsable(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.list_(parsable=True) res = OrderedDict([('mypool', OrderedDict([ ('size', 1990116046274), @@ -230,7 +229,7 @@ def test_get(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.get('mypool', 'size', parsable=False) res = OrderedDict(OrderedDict([('size', '1.81T')])) self.assertEqual(ret, res) @@ -245,7 +244,7 @@ def test_get_parsable(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.get('mypool', 'size', parsable=True) res = OrderedDict(OrderedDict([('size', 1990116046274)])) self.assertEqual(ret, res) @@ -260,7 +259,7 @@ def test_get_whitespace(self): ret['retcode'] = 0 mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.get('mypool', 'comment') res = OrderedDict(OrderedDict([('comment', "my testing pool")])) self.assertEqual(ret, res) @@ -278,7 +277,7 @@ def test_scrub_start(self): with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.scrub('mypool') res = OrderedDict(OrderedDict([('scrubbing', True)])) self.assertEqual(ret, res) @@ -296,7 +295,7 @@ def test_scrub_pause(self): with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.scrub('mypool', pause=True) res = OrderedDict(OrderedDict([('scrubbing', False)])) self.assertEqual(ret, res) @@ -314,7 +313,7 @@ def test_scrub_stop(self): with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.scrub('mypool', stop=True) res = OrderedDict(OrderedDict([('scrubbing', False)])) self.assertEqual(ret, res) @@ -330,7 +329,7 @@ def test_split_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.split('datapool', 'backuppool') res = OrderedDict([('split', True)]) self.assertEqual(ret, res) @@ -346,7 +345,7 @@ def test_split_exist_new(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.split('datapool', 'backuppool') res = OrderedDict([('split', False), ('error', 'Unable to split datapool: pool already exists')]) self.assertEqual(ret, res) @@ -362,7 +361,7 @@ def test_split_missing_pool(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.split('datapool', 'backuppool') res = OrderedDict([('split', False), ('error', "cannot open 'datapool': no such pool")]) self.assertEqual(ret, res) @@ -378,7 +377,7 @@ def test_split_not_mirror(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.split('datapool', 'backuppool') res = OrderedDict([('split', False), ('error', 'Unable to split datapool: Source pool must be composed only of mirrors')]) self.assertEqual(ret, res) @@ -394,7 +393,7 @@ def test_labelclear_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.labelclear('/dev/rdsk/c0t0d0', force=False) res = OrderedDict([('labelcleared', True)]) self.assertEqual(ret, res) @@ -410,7 +409,7 @@ def test_labelclear_nodevice(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.labelclear('/dev/rdsk/c0t0d0', force=False) res = OrderedDict([ ('labelcleared', False), @@ -429,7 +428,7 @@ def test_labelclear_cleared(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.labelclear('/dev/rdsk/c0t0d0', force=False) res = OrderedDict([ ('labelcleared', False), @@ -450,7 +449,7 @@ def test_labelclear_exported(self): ret['retcode'] = 1 mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.labelclear('/dev/rdsk/c0t0d0', force=False) res = OrderedDict([ ('labelcleared', False), @@ -470,7 +469,7 @@ def test_create_file_vdev_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.create_file_vdev('64M', '/vdisks/disk0') res = OrderedDict([ ('/vdisks/disk0', 'created'), @@ -489,7 +488,7 @@ def test_create_file_vdev_nospace(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.create_file_vdev('64M', '/vdisks/disk0') res = OrderedDict([ ('/vdisks/disk0', 'failed'), @@ -510,7 +509,7 @@ def test_export_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.export('mypool') res = OrderedDict([('exported', True)]) self.assertEqual(ret, res) @@ -526,7 +525,7 @@ def test_export_nopool(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.export('mypool') res = OrderedDict([('exported', False), ('error', "cannot open 'mypool': no such pool")]) self.assertEqual(ret, res) @@ -542,7 +541,7 @@ def test_import_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.import_('mypool') res = OrderedDict([('imported', True)]) self.assertEqual(ret, res) @@ -561,7 +560,7 @@ def test_import_duplicate(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.import_('mypool') res = OrderedDict([ ('imported', False), @@ -580,7 +579,7 @@ def test_import_nopool(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.import_('mypool') res = OrderedDict([ ('imported', False), @@ -599,7 +598,7 @@ def test_online_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.online('mypool', '/dev/rdsk/c0t0d0') res = OrderedDict([('onlined', True)]) self.assertEqual(ret, res) @@ -615,7 +614,7 @@ def test_online_nodevice(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.online('mypool', '/dev/rdsk/c0t0d1') res = OrderedDict([ ('onlined', False), @@ -634,7 +633,7 @@ def test_offline_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.offline('mypool', '/dev/rdsk/c0t0d0') res = OrderedDict([('offlined', True)]) self.assertEqual(ret, res) @@ -650,7 +649,7 @@ def test_offline_nodevice(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.offline('mypool', '/dev/rdsk/c0t0d1') res = OrderedDict([ ('offlined', False), @@ -669,7 +668,7 @@ def test_offline_noreplica(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.offline('mypool', '/dev/rdsk/c0t0d1') res = OrderedDict([ ('offlined', False), @@ -688,7 +687,7 @@ def test_reguid_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.reguid('mypool') res = OrderedDict([('reguided', True)]) self.assertEqual(ret, res) @@ -704,7 +703,7 @@ def test_reguid_nopool(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.reguid('mypool') res = OrderedDict([ ('reguided', False), @@ -723,7 +722,7 @@ def test_reopen_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.reopen('mypool') res = OrderedDict([('reopened', True)]) self.assertEqual(ret, res) @@ -739,7 +738,7 @@ def test_reopen_nopool(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.reopen('mypool') res = OrderedDict([ ('reopened', False), @@ -758,7 +757,7 @@ def test_upgrade_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.upgrade('mypool') res = OrderedDict([('upgraded', True)]) self.assertEqual(ret, res) @@ -774,7 +773,7 @@ def test_upgrade_nopool(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.upgrade('mypool') res = OrderedDict([ ('upgraded', False), @@ -797,7 +796,7 @@ def test_history_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.history('mypool') res = OrderedDict([ ('mypool', OrderedDict([ @@ -818,7 +817,7 @@ def test_history_nopool(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.history('mypool') res = OrderedDict([ ('error', "cannot open 'mypool': no such pool"), @@ -836,7 +835,7 @@ def test_clear_success(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.clear('mypool') res = OrderedDict([('cleared', True)]) self.assertEqual(ret, res) @@ -852,7 +851,7 @@ def test_clear_nopool(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.clear('mypool') res = OrderedDict([ ('cleared', False), @@ -870,7 +869,7 @@ def test_clear_nodevice(self): mock_cmd = MagicMock(return_value=ret) with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): ret = zpool.clear('mypool', '/dev/rdsk/c0t0d0') res = OrderedDict([ ('cleared', False), diff --git a/tests/unit/modules/test_zypperpkg.py b/tests/unit/modules/test_zypperpkg.py index 3259e1810da8..84d4bf590a4f 100644 --- a/tests/unit/modules/test_zypperpkg.py +++ b/tests/unit/modules/test_zypperpkg.py @@ -10,14 +10,12 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( Mock, MagicMock, call, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt libs @@ -54,7 +52,6 @@ def get_test_data(filename): return rfh.read() -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZypperTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.zypper diff --git a/tests/unit/netapi/test_rest_cherrypy.py b/tests/unit/netapi/test_rest_cherrypy.py index 4fbded4cb9ea..227699d3549f 100644 --- a/tests/unit/netapi/test_rest_cherrypy.py +++ b/tests/unit/netapi/test_rest_cherrypy.py @@ -87,3 +87,19 @@ def test_yaml_ctype(self): )) self.assertEqual(response.status, '200 OK') self.assertDictEqual(request.unserialized_data, data) + + +class TestCors(BaseToolsTest): + def __get_cp_config__(self): + return { + 'tools.cors_tool.on': True, + } + + def test_option_request(self): + request, response = self.request( + '/', method='OPTIONS', headers=( + ('Origin', 'https://domain.com'), + )) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.headers.get( + 'Access-Control-Allow-Origin'), 'https://domain.com') diff --git a/tests/unit/netapi/test_rest_tornado.py b/tests/unit/netapi/test_rest_tornado.py index 96a637b37b82..99e7ffe9cfab 100644 --- a/tests/unit/netapi/test_rest_tornado.py +++ b/tests/unit/netapi/test_rest_tornado.py @@ -4,11 +4,15 @@ from __future__ import absolute_import import os import copy +import shutil import hashlib # Import Salt Testing Libs -from tests.integration import AdaptedConfigurationTestCaseMixin +from tests.support.mixins import AdaptedConfigurationTestCaseMixin from tests.support.unit import TestCase, skipIf +from tests.support.helpers import patched_environ +from tests.support.runtests import RUNTIME_VARS +from tests.support.events import eventpublisher_process # Import Salt libs import salt.auth @@ -16,7 +20,6 @@ import salt.utils.json import salt.utils.yaml from salt.ext.six.moves import map, range # pylint: disable=import-error -from tests.unit.utils.test_event import eventpublisher_process, SOCK_DIR # pylint: disable=import-error try: HAS_TORNADO = True except ImportError: @@ -48,7 +51,7 @@ class AsyncHTTPTestCase(object): from salt.ext.six.moves.urllib.parse import urlencode, urlparse # pylint: disable=no-name-in-module # pylint: enable=import-error -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch @skipIf(not HAS_TORNADO, 'The tornado package needs to be installed') # pylint: disable=W0223 @@ -94,15 +97,12 @@ def token(self): def setUp(self): super(SaltnadoTestCase, self).setUp() - self.async_timeout_prev = os.environ.pop('ASYNC_TEST_TIMEOUT', None) - os.environ['ASYNC_TEST_TIMEOUT'] = str(30) + self.patched_environ = patched_environ(ASYNC_TEST_TIMEOUT='30') + self.patched_environ.__enter__() + self.addCleanup(self.patched_environ.__exit__) def tearDown(self): super(SaltnadoTestCase, self).tearDown() - if self.async_timeout_prev is None: - os.environ.pop('ASYNC_TEST_TIMEOUT', None) - else: - os.environ['ASYNC_TEST_TIMEOUT'] = self.async_timeout_prev if hasattr(self, 'http_server'): del self.http_server if hasattr(self, 'io_loop'): @@ -476,7 +476,6 @@ def test_cors_origin_url_with_arguments(self): self.assertEqual(headers["Access-Control-Allow-Origin"], "*") -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestWebhookSaltHandler(SaltnadoTestCase): def get_app(self): @@ -805,18 +804,20 @@ def test_any_future(self): @skipIf(not HAS_TORNADO, 'The tornado package needs to be installed') class TestEventListener(AsyncTestCase): def setUp(self): - if not os.path.exists(SOCK_DIR): - os.makedirs(SOCK_DIR) + self.sock_dir = os.path.join(RUNTIME_VARS.TMP, 'test-socks') + if not os.path.exists(self.sock_dir): + os.makedirs(self.sock_dir) + self.addCleanup(shutil.rmtree, self.sock_dir, ignore_errors=True) super(TestEventListener, self).setUp() def test_simple(self): ''' Test getting a few events ''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir) event_listener = saltnado.EventListener({}, # we don't use mod_opts, don't save? - {'sock_dir': SOCK_DIR, + {'sock_dir': self.sock_dir, 'transport': 'zeromq'}) self._finished = False # fit to event_listener's behavior event_future = event_listener.get_event(self, 'evt1', callback=self.stop) # get an event future @@ -833,10 +834,10 @@ def test_set_event_handler(self): ''' Test subscribing events using set_event_handler ''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir) event_listener = saltnado.EventListener({}, # we don't use mod_opts, don't save? - {'sock_dir': SOCK_DIR, + {'sock_dir': self.sock_dir, 'transport': 'zeromq'}) self._finished = False # fit to event_listener's behavior event_future = event_listener.get_event(self, @@ -854,9 +855,9 @@ def test_timeout(self): ''' Make sure timeouts work correctly ''' - with eventpublisher_process(): + with eventpublisher_process(self.sock_dir): event_listener = saltnado.EventListener({}, # we don't use mod_opts, don't save? - {'sock_dir': SOCK_DIR, + {'sock_dir': self.sock_dir, 'transport': 'zeromq'}) self._finished = False # fit to event_listener's behavior event_future = event_listener.get_event(self, @@ -900,10 +901,10 @@ def stop(): if cnt[0] == 2: self.stop() - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir) event_listener = saltnado.EventListener({}, # we don't use mod_opts, don't save? - {'sock_dir': SOCK_DIR, + {'sock_dir': self.sock_dir, 'transport': 'zeromq'}) self.assertEqual(0, len(event_listener.tag_map)) diff --git a/tests/unit/pillar/test_consul_pillar.py b/tests/unit/pillar/test_consul_pillar.py index a5243258970c..c4e73c1814c2 100644 --- a/tests/unit/pillar/test_consul_pillar.py +++ b/tests/unit/pillar/test_consul_pillar.py @@ -6,7 +6,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.pillar.consul_pillar as consul_pillar @@ -38,7 +38,6 @@ SIMPLE_DICT = {'key1': {'key2': 'val1'}} -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not consul_pillar.consul, 'python-consul module not installed') class ConsulPillarTestCase(TestCase, LoaderModuleMockMixin): ''' @@ -62,17 +61,23 @@ def test_pillar_data(self): with patch.dict(consul_pillar.__salt__, {'grains.get': MagicMock(return_value=({}))}): with patch.object(consul_pillar, 'consul_fetch', MagicMock(return_value=('2232', PILLAR_DATA))): pillar_data = consul_pillar.ext_pillar('testminion', {}, 'consul_config root=test-shared/') - consul_pillar.consul_fetch.assert_called_once_with('consul_connection', 'test-shared/') + consul_pillar.consul_fetch.assert_called_once_with('consul_connection', 'test-shared') assert sorted(pillar_data) == ['sites', 'user'] self.assertNotIn('blankvalue', pillar_data['user']) + def test_blank_root(self): + with patch.dict(consul_pillar.__salt__, {'grains.get': MagicMock(return_value=({}))}): + with patch.object(consul_pillar, 'consul_fetch', MagicMock(return_value=('2232', PILLAR_DATA))): + pillar_data = consul_pillar.ext_pillar('testminion', {}, 'consul_config') + consul_pillar.consul_fetch.assert_called_once_with('consul_connection', '') + assert sorted(pillar_data) == ['test-shared'] + def test_pillar_nest(self): with patch.dict(consul_pillar.__salt__, {'grains.get': MagicMock(return_value=({}))}): with patch.object(consul_pillar, 'consul_fetch', MagicMock(return_value=('2232', PILLAR_DATA))): pillar_data = consul_pillar.ext_pillar( - 'testminion', {}, 'consul_config root=test-shared/ pillar_root=nested-key/' + 'testminion', {}, 'consul_config pillar_root=nested-key/ root=test-shared/ ' ) - consul_pillar.consul_fetch.assert_called_once_with('consul_connection', 'test-shared/') assert sorted(pillar_data['nested-key']) == ['sites', 'user'] self.assertNotIn('blankvalue', pillar_data['nested-key']['user']) diff --git a/tests/unit/pillar/test_csvpillar.py b/tests/unit/pillar/test_csvpillar.py new file mode 100644 index 000000000000..a9ff4b3481f5 --- /dev/null +++ b/tests/unit/pillar/test_csvpillar.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +'''test for pillar csvpillar.py''' + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Testing libs +from tests.support.unit import TestCase +from tests.support.mock import patch, mock_open + +# Import Salt Libs +import salt.pillar.csvpillar as csvpillar + + +class CSVPillarTestCase(TestCase): + def test_001_load_utf8_csv(self): + fake_csv = "id,foo,bar\r\nminion1,foo1,bar1" + fake_dict = {'id': 'minion1', 'foo': 'foo1', 'bar': 'bar1'} + fopen_mock = mock_open(fake_csv) + with patch('salt.utils.files.fopen', fopen_mock): + result = csvpillar.ext_pillar(mid='minion1', pillar=None, + path="/fake/path/file.csv", idkey="id", namespace=None) + self.assertDictEqual(fake_dict, result) + + def test_002_load_utf8_csv_namespc(self): + fake_csv = "id,foo,bar\r\nminion1,foo1,bar1" + fake_dict = {'baz': {'id': 'minion1', 'foo': 'foo1', 'bar': 'bar1'}} + fopen_mock = mock_open(fake_csv) + with patch('salt.utils.files.fopen', fopen_mock): + result = csvpillar.ext_pillar(mid='minion1', pillar=None, + path="/fake/path/file.csv", idkey="id", namespace='baz') + self.assertDictEqual(fake_dict, result) diff --git a/tests/unit/pillar/test_extra_minion_data_in_pillar.py b/tests/unit/pillar/test_extra_minion_data_in_pillar.py index bf21f922cdb1..7488ed9ed746 100644 --- a/tests/unit/pillar/test_extra_minion_data_in_pillar.py +++ b/tests/unit/pillar/test_extra_minion_data_in_pillar.py @@ -5,14 +5,13 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import MagicMock # Import Salt Libs from salt.pillar import extra_minion_data_in_pillar -@skipIf(NO_MOCK, NO_MOCK_REASON) class ExtraMinionDataInPillarTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.pillar.extra_minion_data_in_pillar diff --git a/tests/unit/pillar/test_file_tree.py b/tests/unit/pillar/test_file_tree.py index 6d5626ef52cb..0521bff41393 100644 --- a/tests/unit/pillar/test_file_tree.py +++ b/tests/unit/pillar/test_file_tree.py @@ -11,11 +11,11 @@ import shutil # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock -from tests.support.paths import TMP -from tests.support.helpers import TestsLoggingHandler +from tests.support.unit import TestCase +from tests.support.mock import patch, MagicMock +from tests.support.helpers import TstSuiteLoggingHandler # Import Salt Libs import salt.utils.files @@ -46,13 +46,12 @@ _CHECK_MINIONS_RETURN = {'minions': [MINION_ID], 'missing': []} -@skipIf(NO_MOCK, NO_MOCK_REASON) class FileTreePillarTestCase(TestCase, LoaderModuleMockMixin): 'test file_tree pillar' maxDiff = None def setup_loader_modules(self): - self.tmpdir = tempfile.mkdtemp(dir=TMP) + self.tmpdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.tmpdir) cachedir = os.path.join(self.tmpdir, 'cachedir') os.makedirs(os.path.join(cachedir, 'file_tree')) @@ -127,7 +126,7 @@ def test_no_pillarenv(self): 'confirm that file_tree yells when pillarenv is missing for a relative path' with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_CHECK_MINIONS_RETURN)): with patch.dict(file_tree.__opts__, {'pillarenv': None}): - with TestsLoggingHandler() as handler: + with TstSuiteLoggingHandler() as handler: mypillar = file_tree.ext_pillar(MINION_ID, None, '.') self.assertEqual({}, mypillar) diff --git a/tests/unit/pillar/test_hg_pillar.py b/tests/unit/pillar/test_hg_pillar.py index 21e8a9a7e0ef..452d15cbcbe9 100644 --- a/tests/unit/pillar/test_hg_pillar.py +++ b/tests/unit/pillar/test_hg_pillar.py @@ -10,11 +10,9 @@ import subprocess # Import Salt Testing libs -from tests.integration import AdaptedConfigurationTestCaseMixin -from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mixins import AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON -from tests.support.paths import TMP +from tests.support.runtests import RUNTIME_VARS COMMIT_USER_NAME = 'test_user' @@ -32,14 +30,13 @@ HGLIB = hg_pillar.hglib -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HGLIB is None, 'python-hglib library not installed') class HgPillarTestCase(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin): 'test hg_pillar pillar' maxDiff = None def setup_loader_modules(self): - self.tmpdir = tempfile.mkdtemp(dir=TMP) + self.tmpdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.tmpdir) cachedir = os.path.join(self.tmpdir, 'cachedir') os.makedirs(os.path.join(cachedir, 'hg_pillar')) diff --git a/tests/unit/pillar/test_mysql.py b/tests/unit/pillar/test_mysql.py index f6a2d0a44b60..94ecde8c20d5 100644 --- a/tests/unit/pillar/test_mysql.py +++ b/tests/unit/pillar/test_mysql.py @@ -5,13 +5,11 @@ # Import Salt Testing libs from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON # Import Salt Libs import salt.pillar.mysql as mysql -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(mysql.MySQLdb is None, 'MySQL-python module not installed') class MysqlPillarTestCase(TestCase): maxDiff = None diff --git a/tests/unit/pillar/test_saltclass.py b/tests/unit/pillar/test_saltclass.py index 3df84804cf98..f8a4e1f7e78f 100644 --- a/tests/unit/pillar/test_saltclass.py +++ b/tests/unit/pillar/test_saltclass.py @@ -6,8 +6,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase # Import Salt Libs import salt.pillar.saltclass as saltclass @@ -24,7 +23,6 @@ fake_grains = {} -@skipIf(NO_MOCK, NO_MOCK_REASON) class SaltclassPillarTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for salt.pillar.saltclass @@ -49,7 +47,6 @@ def test_succeeds(self): self._runner(ret) -@skipIf(NO_MOCK, NO_MOCK_REASON) class SaltclassPillarTestCaseListExpansion(TestCase, LoaderModuleMockMixin): ''' Tests for salt.pillar.saltclass variable expansion in list diff --git a/tests/unit/pillar/test_sqlcipher.py b/tests/unit/pillar/test_sqlcipher.py index 69c6c1a50544..84382a90aacd 100644 --- a/tests/unit/pillar/test_sqlcipher.py +++ b/tests/unit/pillar/test_sqlcipher.py @@ -4,14 +4,12 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase # Import Salt Libs import salt.pillar.sqlcipher as sqlcipher -@skipIf(NO_MOCK, NO_MOCK_REASON) class SQLCipherPillarTestCase(TestCase): maxDiff = None diff --git a/tests/unit/pillar/test_sqlite3.py b/tests/unit/pillar/test_sqlite3.py index 11a1ce03e6a4..f22423b6e5fc 100644 --- a/tests/unit/pillar/test_sqlite3.py +++ b/tests/unit/pillar/test_sqlite3.py @@ -4,14 +4,12 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase # Import Salt Libs import salt.pillar.sqlite3 as sqlite3 -@skipIf(NO_MOCK, NO_MOCK_REASON) class SQLite3PillarTestCase(TestCase): maxDiff = None diff --git a/tests/unit/proxy/test_esxcluster.py b/tests/unit/proxy/test_esxcluster.py index 1a2bc5988b23..a7e79394c1b8 100644 --- a/tests/unit/proxy/test_esxcluster.py +++ b/tests/unit/proxy/test_esxcluster.py @@ -26,12 +26,9 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_JSONSCHEMA, 'jsonschema is required') class InitTestCase(TestCase, LoaderModuleMockMixin): '''Tests for salt.proxy.esxcluster.init''' diff --git a/tests/unit/proxy/test_esxdatacenter.py b/tests/unit/proxy/test_esxdatacenter.py index 98104eb14601..89f2e55881aa 100644 --- a/tests/unit/proxy/test_esxdatacenter.py +++ b/tests/unit/proxy/test_esxdatacenter.py @@ -26,12 +26,9 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_JSONSCHEMA, 'jsonschema is required') class InitTestCase(TestCase, LoaderModuleMockMixin): '''Tests for salt.proxy.esxdatacenter.init''' diff --git a/tests/unit/proxy/test_napalm.py b/tests/unit/proxy/test_napalm.py index 14a806d09655..a450fa3cd383 100644 --- a/tests/unit/proxy/test_napalm.py +++ b/tests/unit/proxy/test_napalm.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -35,7 +33,6 @@ def mock_get_device(opts, *args, **kwargs): } -@skipIf(NO_MOCK, NO_MOCK_REASON) @patch('salt.utils.napalm.get_device', mock_get_device) class NapalmProxyTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/renderers/test_aws_kms.py b/tests/unit/renderers/test_aws_kms.py index 5855859f740a..50305b2959d6 100644 --- a/tests/unit/renderers/test_aws_kms.py +++ b/tests/unit/renderers/test_aws_kms.py @@ -12,8 +12,6 @@ from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -45,7 +43,6 @@ REGION_NAME = 'us-test-1' -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(NO_BOTOCORE, 'Unable to import botocore libraries') class AWSKMSTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/renderers/test_gpg.py b/tests/unit/renderers/test_gpg.py index 9099b4262e02..20af25b678d3 100644 --- a/tests/unit/renderers/test_gpg.py +++ b/tests/unit/renderers/test_gpg.py @@ -7,10 +7,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -20,7 +18,6 @@ from salt.exceptions import SaltRenderError -@skipIf(NO_MOCK, NO_MOCK_REASON) class GPGTestCase(TestCase, LoaderModuleMockMixin): ''' unit test GPG renderer diff --git a/tests/unit/renderers/test_nacl.py b/tests/unit/renderers/test_nacl.py index de0e70297800..34e6227d03c5 100644 --- a/tests/unit/renderers/test_nacl.py +++ b/tests/unit/renderers/test_nacl.py @@ -5,10 +5,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -17,7 +15,6 @@ import salt.renderers.nacl as nacl -@skipIf(NO_MOCK, NO_MOCK_REASON) class NaclTestCase(TestCase, LoaderModuleMockMixin): ''' unit test NaCl renderer diff --git a/tests/unit/renderers/test_stateconf.py b/tests/unit/renderers/test_stateconf.py index 38798710d0e0..3acf11e17331 100644 --- a/tests/unit/renderers/test_stateconf.py +++ b/tests/unit/renderers/test_stateconf.py @@ -8,11 +8,11 @@ # Import Salt Testing libs from tests.support.unit import TestCase +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.loader import salt.config -import tests.integration as integration from salt.exceptions import SaltRenderError from salt.ext.six.moves import StringIO @@ -26,7 +26,7 @@ class StateConfigRendererTestCase(TestCase): def setUp(self): - self.root_dir = tempfile.mkdtemp(dir=integration.TMP) + self.root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.state_tree_dir = os.path.join(self.root_dir, 'state_tree') self.cache_dir = os.path.join(self.root_dir, 'cachedir') if not os.path.isdir(self.root_dir): diff --git a/tests/unit/returners/test_local_cache.py b/tests/unit/returners/test_local_cache.py index 9c987d9b83aa..267ad3173951 100644 --- a/tests/unit/returners/test_local_cache.py +++ b/tests/unit/returners/test_local_cache.py @@ -16,14 +16,11 @@ import time # Import Salt Testing libs -from tests.integration import AdaptedConfigurationTestCaseMixin -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import TestCase, skipIf +from tests.support.mixins import AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -37,17 +34,18 @@ log = logging.getLogger(__name__) -TMP_CACHE_DIR = os.path.join(TMP, 'salt_test_job_cache') -TMP_JID_DIR = os.path.join(TMP_CACHE_DIR, 'jobs') - -@skipIf(NO_MOCK, NO_MOCK_REASON) class LocalCacheCleanOldJobsTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for the local_cache.clean_old_jobs function. ''' + @classmethod + def setUpClass(cls): + cls.TMP_CACHE_DIR = os.path.join(RUNTIME_VARS.TMP, 'salt_test_job_cache') + cls.TMP_JID_DIR = os.path.join(cls.TMP_CACHE_DIR, 'jobs') + def setup_loader_modules(self): - return {local_cache: {'__opts__': {'cachedir': TMP_CACHE_DIR, 'keep_jobs': 1}}} + return {local_cache: {'__opts__': {'cachedir': self.TMP_CACHE_DIR, 'keep_jobs': 1}}} def tearDown(self): ''' @@ -56,8 +54,8 @@ def tearDown(self): Note that a setUp function is not used in this TestCase because the _make_tmp_jid_dirs replaces it. ''' - if os.path.exists(TMP_CACHE_DIR): - shutil.rmtree(TMP_CACHE_DIR) + if os.path.exists(self.TMP_CACHE_DIR): + shutil.rmtree(self.TMP_CACHE_DIR) def test_clean_old_jobs_no_jid_root(self): ''' @@ -91,7 +89,7 @@ def test_clean_old_jobs_empty_jid_dir_removed(self): local_cache.clean_old_jobs() # Assert that the JID dir was removed - self.assertEqual([], os.listdir(TMP_JID_DIR)) + self.assertEqual([], os.listdir(self.TMP_JID_DIR)) def test_clean_old_jobs_empty_jid_dir_remains(self): ''' @@ -114,7 +112,7 @@ def test_clean_old_jobs_empty_jid_dir_remains(self): jid_dir_name = jid_dir.rpartition('/')[2] # Assert the JID directory is still present to be cleaned after keep_jobs interval - self.assertEqual([jid_dir_name], os.listdir(TMP_JID_DIR)) + self.assertEqual([jid_dir_name], os.listdir(self.TMP_JID_DIR)) def test_clean_old_jobs_jid_file_corrupted(self): ''' @@ -135,7 +133,7 @@ def test_clean_old_jobs_jid_file_corrupted(self): local_cache.clean_old_jobs() # there should be only 1 dir in TMP_JID_DIR - self.assertEqual(1, len(os.listdir(TMP_JID_DIR))) + self.assertEqual(1, len(os.listdir(self.TMP_JID_DIR))) # top level dir should still be present self.assertEqual(True, os.path.exists(jid_dir)) self.assertEqual(True, os.path.isdir(jid_dir)) @@ -168,7 +166,7 @@ def test_clean_old_jobs_jid_file_is_cleaned(self): local_cache.clean_old_jobs() # there should be only 1 dir in TMP_JID_DIR - self.assertEqual(1, len(os.listdir(TMP_JID_DIR))) + self.assertEqual(1, len(os.listdir(self.TMP_JID_DIR))) # top level dir should still be present self.assertEqual(True, os.path.exists(jid_dir)) self.assertEqual(True, os.path.isdir(jid_dir)) @@ -182,7 +180,7 @@ def _make_tmp_jid_dirs(self, create_files=True): This emulates salt.utils.jid.jid_dir() by creating this structure: - TMP_JID_DIR dir/ + RUNTIME_VARS.TMP_JID_DIR dir/ random dir from tempfile.mkdtemp/ 'jid' directory/ 'jid' file @@ -191,11 +189,11 @@ def _make_tmp_jid_dirs(self, create_files=True): the jid_file_path will be None. ''' # First, create the /tmp/salt_test_job_cache/jobs/ directory to hold jid dirs - if not os.path.exists(TMP_JID_DIR): - os.makedirs(TMP_JID_DIR) + if not os.path.exists(self.TMP_JID_DIR): + os.makedirs(self.TMP_JID_DIR) # Then create a JID temp file in "/tmp/salt_test_job_cache/" - temp_dir = tempfile.mkdtemp(dir=TMP_JID_DIR) + temp_dir = tempfile.mkdtemp(dir=self.TMP_JID_DIR) jid_file_path = None if create_files: @@ -224,7 +222,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.TMP_CACHE_DIR = os.path.join(TMP, 'rootdir', 'cache') + cls.TMP_CACHE_DIR = os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'cache') cls.JOBS_DIR = os.path.join(cls.TMP_CACHE_DIR, 'jobs') cls.JID_DIR = os.path.join(cls.JOBS_DIR, '31', 'c56eed380a4e899ae12bc42563cfdfc53066fb4a6b53e2378a08ac49064539') cls.JID_FILE = os.path.join(cls.JID_DIR, 'jid') diff --git a/tests/unit/returners/test_pgjsonb.py b/tests/unit/returners/test_pgjsonb.py index 00cedc186aeb..831464a15c9c 100644 --- a/tests/unit/returners/test_pgjsonb.py +++ b/tests/unit/returners/test_pgjsonb.py @@ -12,11 +12,9 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -26,7 +24,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PGJsonbCleanOldJobsTestCase(TestCase, LoaderModuleMockMixin): ''' Tests for the local_cache.clean_old_jobs function. diff --git a/tests/unit/returners/test_slack_webhook_return.py b/tests/unit/returners/test_slack_webhook_return.py new file mode 100644 index 000000000000..cdea753011b1 --- /dev/null +++ b/tests/unit/returners/test_slack_webhook_return.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: :email:`Carlos D. Álvaro ` + + tests.unit.returners.test_slack_webhook_return + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Unit tests for the Slack Webhook Returner. +''' + +# Import Python libs +from __future__ import absolute_import + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase +from tests.support.mock import patch + +# Import Salt libs +import salt.returners.slack_webhook_return as slack_webhook + + +class SlackWebhookReturnerTestCase(TestCase, LoaderModuleMockMixin): + ''' + Test slack_webhook returner + ''' + _WEBHOOK = 'T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX' + _AUTHOR_ICON = 'https://platform.slack-edge.com/img/default_application_icon.png' + _SHOW_TASKS = True + _MINION_NAME = 'MacPro' + + _RET = { + 'fun_args': ['config.vim'], + 'jid': '20181227105933129338', + 'return': + {'file_|-vim files present_|-/Users/cdalvaro/_|-recurse': + {'comment': 'The directory /Users/cdalvaro/ is in the correct state', + 'pchanges': {}, + 'name': '/Users/cdalvaro/', + 'start_time': '10:59:52.252830', + 'result': True, + 'duration': 373.25, + '__run_num__': 3, + '__sls__': 'config.vim', + 'changes': {}, + '__id__': 'vim files present'}, + 'pkg_|-vim present_|-vim_|-installed': + {'comment': 'All specified packages are already installed', + 'name': 'vim', + 'start_time': '10:59:36.830591', + 'result': True, + 'duration': 1280.127, + '__run_num__': 0, + '__sls__': 'config.vim', + 'changes': {}, + '__id__': 'vim present'}, + 'git_|-salt vim plugin updated_|-https://github.com/saltstack/salt-vim.git_|-latest': + {'comment': 'https://github.com/saltstack/salt-vim.git cloned to /Users/cdalvaro/.vim/pack/git-plugins/start/salt', + 'name': 'https://github.com/saltstack/salt-vim.git', + 'start_time': '11:00:01.892757', + 'result': True, + 'duration': 11243.445, + '__run_num__': 6, + '__sls__': 'config.vim', + 'changes': + {'new': 'https://github.com/saltstack/salt-vim.git => /Users/cdalvaro/.vim/pack/git-plugins/start/salt', + 'revision': {'new': '6ca9e3500cc39dd417b411435d58a1b720b331cc', 'old': None}}, + '__id__': 'salt vim plugin updated'}, + 'pkg_|-macvim present_|-caskroom/cask/macvim_|-installed': + {'comment': 'The following packages failed to install/update: caskroom/cask/macvim', + 'name': 'caskroom/cask/macvim', + 'start_time': '10:59:38.111119', + 'result': False, + 'duration': 14135.45, + '__run_num__': 1, + '__sls__': 'config.vim', + 'changes': {}, + '__id__': 'macvim present'}}, + 'retcode': 2, + 'success': True, + 'fun': 'state.apply', + 'id': _MINION_NAME, + 'out': 'highstate' + } + + _EXPECTED_PAYLOAD = { + u'attachments': [ + {u'title': u'Success: False', + u'color': u'#272727', + u'text': u"Function: state.apply\nFunction Args: ['config.vim']\nJID: 20181227105933129338\nTotal: 4\nDuration: 27.03 secs", + u'author_link': u'{}'.format(_MINION_NAME), + u'author_name': u'{}'.format(_MINION_NAME), + u'fallback': u'{} | Failed'.format(_MINION_NAME), + u'author_icon': _AUTHOR_ICON}, + {u'color': u'good', + u'title': u'Unchanged: 2'}, + {u'color': u'warning', + u'fields': [ + {u'short': False, + u'value': u'config.vim.sls | salt vim plugin updated'} + ], + u'title': u'Changed: 1'}, + {u'color': u'danger', + u'fields': [ + {u'short': False, + u'value': u'config.vim.sls | macvim present'} + ], + u'title': u'Failed: 1'} + ] + } + + def setup_loader_modules(self): + return {slack_webhook: {'__opts__': { + 'slack_webhook.webhook': self._WEBHOOK, + 'slack_webhook.author_icon': self._AUTHOR_ICON, + 'slack_webhook.success_title': '{id} | Succeeded', + 'slack_webhook.failure_title': '{id} | Failed', + 'slack_webhook.show_tasks': self._SHOW_TASKS + }}} + + def test_no_webhook(self): + ''' + Test returner stops if no webhook is defined + ''' + with patch.dict(slack_webhook.__opts__, {'slack_webhook.webhook': ''}): + self.assertEqual(slack_webhook.returner(self._RET), None) + + def test_returner(self): + ''' + Test to see if the Slack Webhook returner sends a message + ''' + query_ret = {'body': 'ok', 'status': 200} + with patch('salt.utils.http.query', return_value=query_ret): + self.assertTrue(slack_webhook.returner(self._RET)) + + def test_generate_payload(self): + ''' + Test _generate_payload private method + ''' + test_title = '{} | Failed'.format(self._MINION_NAME) + test_report = slack_webhook._generate_report( + self._RET, self._SHOW_TASKS) + + custom_grains = slack_webhook.__grains__ + custom_grains['id'] = self._MINION_NAME + custom_grains['localhost'] = self._MINION_NAME + + with patch.dict(slack_webhook.__grains__, custom_grains): + test_payload = slack_webhook._generate_payload( + self._AUTHOR_ICON, test_title, test_report) + + self.assertDictEqual(test_payload, self._EXPECTED_PAYLOAD) diff --git a/tests/unit/returners/test_smtp_return.py b/tests/unit/returners/test_smtp_return.py index 726829691921..c000a29e6fef 100644 --- a/tests/unit/returners/test_smtp_return.py +++ b/tests/unit/returners/test_smtp_return.py @@ -12,8 +12,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.returners.smtp_return as smtp @@ -26,7 +26,6 @@ HAS_GNUPG = False -@skipIf(NO_MOCK, NO_MOCK_REASON) class SMTPReturnerTestCase(TestCase, LoaderModuleMockMixin): ''' Test SMTP returner diff --git a/tests/unit/returners/test_syslog_return.py b/tests/unit/returners/test_syslog_return.py index 4980c95368bd..a89ff8b056d1 100644 --- a/tests/unit/returners/test_syslog_return.py +++ b/tests/unit/returners/test_syslog_return.py @@ -13,13 +13,12 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch # Import salt libs import salt.returners.syslog_return as syslog -@skipIf(NO_MOCK, NO_MOCK_REASON) class SyslogReturnerTestCase(TestCase, LoaderModuleMockMixin): ''' Test Syslog returner diff --git a/tests/unit/returners/test_telegram_return.py b/tests/unit/returners/test_telegram_return.py index 417abcd88922..2a6ec6a66af3 100644 --- a/tests/unit/returners/test_telegram_return.py +++ b/tests/unit/returners/test_telegram_return.py @@ -11,14 +11,13 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.returners.telegram_return as telegram -@skipIf(NO_MOCK, NO_MOCK_REASON) class TelegramReturnerTestCase(TestCase, LoaderModuleMockMixin): ''' Test Telegram Returner diff --git a/tests/unit/roster/test_ansible.py b/tests/unit/roster/test_ansible.py index 147e9f0b49cc..a2ea996324ec 100644 --- a/tests/unit/roster/test_ansible.py +++ b/tests/unit/roster/test_ansible.py @@ -6,14 +6,11 @@ # Import Salt Testing Libs from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, patch ) from tests.support import mixins from tests.support.unit import skipIf, TestCase from tests.support.runtests import RUNTIME_VARS -from tests.support.paths import TESTS_DIR # Import Salt Libs import salt.config @@ -56,12 +53,11 @@ @skipIf(not salt.utils.path.which('ansible-inventory'), 'Skipping because ansible-inventory is not available') -@skipIf(NO_MOCK, NO_MOCK_REASON) class AnsibleRosterTestCase(TestCase, mixins.LoaderModuleMockMixin): @classmethod def setUpClass(cls): - cls.roster_dir = os.path.join(TESTS_DIR, 'unit/files/rosters/ansible/') + cls.roster_dir = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/rosters/ansible/') cls.opts = {'roster_defaults': {'passwd': 'test123'}} @classmethod diff --git a/tests/unit/roster/test_clustershell.py b/tests/unit/roster/test_clustershell.py index 4e9b32c7c2c9..43c7003832d6 100644 --- a/tests/unit/roster/test_clustershell.py +++ b/tests/unit/roster/test_clustershell.py @@ -8,8 +8,6 @@ # Import Salt Testing libraries from tests.support.unit import TestCase, skipIf from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -21,7 +19,6 @@ HAS_CLUSTERSHELL = False -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_CLUSTERSHELL is False, 'Install Python Clustershell bindings before running these tests.') class ClusterShellTestCase(TestCase): ''' diff --git a/tests/unit/roster/test_sshconfig.py b/tests/unit/roster/test_sshconfig.py index 77a6a06a50b0..a06fa39f7922 100644 --- a/tests/unit/roster/test_sshconfig.py +++ b/tests/unit/roster/test_sshconfig.py @@ -7,12 +7,10 @@ # Import Salt Testing Libs from tests.support.mock import ( mock_open, - NO_MOCK, - NO_MOCK_REASON, patch ) from tests.support import mixins -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase # Import Salt Libs import salt.roster.sshconfig as sshconfig @@ -67,7 +65,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class SSHConfigRosterTestCase(TestCase, mixins.LoaderModuleMockMixin): def setUp(self): diff --git a/tests/unit/roster/test_terraform.py b/tests/unit/roster/test_terraform.py index 8d1be9a1058e..bc9fefd6fa66 100644 --- a/tests/unit/roster/test_terraform.py +++ b/tests/unit/roster/test_terraform.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.runtests import RUNTIME_VARS from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -22,7 +20,6 @@ from salt.roster import terraform -@skipIf(NO_MOCK, NO_MOCK_REASON) class TerraformTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.roster.terraform diff --git a/tests/unit/runners/test_cache.py b/tests/unit/runners/test_cache.py index c493d643bec6..686c3c78e5b6 100644 --- a/tests/unit/runners/test_cache.py +++ b/tests/unit/runners/test_cache.py @@ -7,12 +7,10 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -21,13 +19,12 @@ import salt.utils.master -@skipIf(NO_MOCK, NO_MOCK_REASON) class CacheTest(TestCase, LoaderModuleMockMixin): ''' Validate the cache runner ''' def setup_loader_modules(self): - return {cache: {'__opts__': {'cache': 'localfs', 'pki_dir': TMP, 'key_cache': True}}} + return {cache: {'__opts__': {'cache': 'localfs', 'pki_dir': RUNTIME_VARS.TMP, 'key_cache': True}}} def test_grains(self): ''' diff --git a/tests/unit/runners/test_jobs.py b/tests/unit/runners/test_jobs.py index 0b0b66037dcf..f0a18fc5a732 100644 --- a/tests/unit/runners/test_jobs.py +++ b/tests/unit/runners/test_jobs.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -20,7 +18,6 @@ import salt.minion -@skipIf(NO_MOCK, NO_MOCK_REASON) class JobsTest(TestCase, LoaderModuleMockMixin): ''' Validate the jobs runner diff --git a/tests/unit/runners/test_queue.py b/tests/unit/runners/test_queue.py index b3a0d1977b27..3fd41dd3541f 100644 --- a/tests/unit/runners/test_queue.py +++ b/tests/unit/runners/test_queue.py @@ -8,12 +8,10 @@ import os # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -22,7 +20,6 @@ import salt.runners.queue as queue_mod -@skipIf(NO_MOCK, NO_MOCK_REASON) class QueueTest(TestCase, LoaderModuleMockMixin): ''' Validate the queue runner @@ -31,7 +28,7 @@ def setup_loader_modules(self): return { queue_mod: { '__opts__': { - 'sock_dir': os.path.join(TMP, 'queue-runner-sock-dir'), + 'sock_dir': os.path.join(RUNTIME_VARS.TMP, 'queue-runner-sock-dir'), 'transport': 'zeromq' } } diff --git a/tests/unit/runners/test_vault.py b/tests/unit/runners/test_vault.py index 25f06b78b4ad..c9ae7aa10a2b 100644 --- a/tests/unit/runners/test_vault.py +++ b/tests/unit/runners/test_vault.py @@ -9,13 +9,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, Mock, patch, - NO_MOCK, - NO_MOCK_REASON, ANY, call ) @@ -113,7 +111,6 @@ def test_get_policies_for_nonexisting_minions(self): log.debug('Difference:\n\t%s', diff) self.assertEqual(output, correct_output) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_get_policies(self): ''' Ensure _get_policies works as intended, including expansion of lists diff --git a/tests/unit/runners/test_winrepo.py b/tests/unit/runners/test_winrepo.py index d38c5419f5ae..f97b9df10a94 100644 --- a/tests/unit/runners/test_winrepo.py +++ b/tests/unit/runners/test_winrepo.py @@ -7,10 +7,9 @@ import tempfile # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase # Import salt libs import salt.utils.files @@ -74,15 +73,14 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinrepoTest(TestCase, LoaderModuleMockMixin): ''' Test the winrepo runner ''' def setup_loader_modules(self): - self.winrepo_dir = tempfile.mkdtemp(dir=TMP) + self.winrepo_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.winrepo_dir, ignore_errors=True) - self.extmods_dir = tempfile.mkdtemp(dir=TMP) + self.extmods_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.extmods_dir, ignore_errors=True) self.winrepo_sls_dir = os.path.join(self.winrepo_dir, 'repo_sls') os.mkdir(self.winrepo_sls_dir) diff --git a/tests/unit/sdb/test_yaml.py b/tests/unit/sdb/test_yaml.py index d7b2932ee91a..d21446415556 100644 --- a/tests/unit/sdb/test_yaml.py +++ b/tests/unit/sdb/test_yaml.py @@ -7,10 +7,8 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.sdb.yaml as sdb -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestYamlRenderer(TestCase): ''' Test case for the YAML SDB module diff --git a/tests/unit/states/test_acme.py b/tests/unit/states/test_acme.py new file mode 100644 index 000000000000..86644de73231 --- /dev/null +++ b/tests/unit/states/test_acme.py @@ -0,0 +1,127 @@ +# -*- coding: utf-8 -*- +''' +:codeauthor: Herbert Buurman +''' + +# Import future libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch + +# Import Salt Module +import salt.states.acme as acme + + +class AcmeTestCase(TestCase, LoaderModuleMockMixin): + ''' + Test cases for salt.modules.acme + ''' + + def setup_loader_modules(self): + return {acme: {'__opts__': {'test': False}}} + + def test_cert_no_changes_t(self): + ''' + Test cert state with no needed changes. (test=True) + ''' + # With test=True + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'acme.has': MagicMock(return_value=True), + 'acme.needs_renewal': MagicMock(return_value=False), + }), \ + patch.dict(acme.__opts__, {'test': True}): # pylint: disable=no-member + self.assertEqual(acme.cert('test'), { + 'name': 'test', + 'result': True, + 'comment': ['Certificate test exists and does not need renewal.'], + 'changes': {}, + }) + + def test_cert_no_changes(self): + ''' + Test cert state with no needed changes. (test=False) + ''' + # With test=False + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'acme.has': MagicMock(return_value=True), + 'acme.needs_renewal': MagicMock(return_value=False), + }): + self.assertEqual(acme.cert('test'), { + 'name': 'test', + 'result': True, + 'comment': ['Certificate test exists and does not need renewal.'], + 'changes': {}, + }) + + def test_cert_fresh_certificate_t(self): + ''' + Test cert state fetching a new certificate. (test=True) + ''' + # With test=True + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'acme.has': MagicMock(return_value=False), + # 'acme.cert': MagicMock(return_value={'result': True, 'comment': 'Mockery'}), + 'acme.info': MagicMock(return_value={'foo': 'bar'}), + }), \ + patch.dict(acme.__opts__, {'test': True}): # pylint: disable=no-member + self.assertEqual(acme.cert('test'), { + 'name': 'test', + 'result': None, + 'comment': ['Certificate test would have been obtained.'], + 'changes': {'old': 'current certificate', 'new': 'new certificate'}, + }) + + def test_cert_fresh_certificate(self): + ''' + Test cert state fetching a new certificate. (test=False) + ''' + # With test=False + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'acme.has': MagicMock(return_value=False), + 'acme.cert': MagicMock(return_value={'result': True, 'comment': 'Mockery'}), + 'acme.info': MagicMock(return_value={'foo': 'bar'}), + }): + self.assertEqual(acme.cert('test'), { + 'name': 'test', + 'result': True, + 'comment': ['Mockery'], + 'changes': {'new': {'foo': 'bar'}}, + }) + + def test_cert_renew_certificate_t(self): + ''' + Test cert state renewing a certificate. (test=True) + ''' + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'acme.has': MagicMock(return_value=True), + 'acme.needs_renewal': MagicMock(return_value=True), + 'acme.info': MagicMock(side_effect=[{'name': 'old cert'}, {'name': 'new cert'}]), + 'acme.cert': MagicMock(return_value={'result': True, 'comment': 'Mockery'}), + }), \ + patch.dict(acme.__opts__, {'test': True}): # pylint: disable=no-member + self.assertEqual(acme.cert('test'), { + 'name': 'test', + 'result': None, + 'comment': ['Certificate test would have been renewed.'], + 'changes': {'old': 'current certificate', 'new': 'new certificate'}, + }) + + def test_cert_renew_certificate(self): + ''' + Test cert state renewing a certificate. (test=False) + ''' + with patch.dict(acme.__salt__, { # pylint: disable=no-member + 'acme.has': MagicMock(return_value=True), + 'acme.needs_renewal': MagicMock(return_value=True), + 'acme.info': MagicMock(side_effect=[{'name': 'old cert'}, {'name': 'new cert'}]), + 'acme.cert': MagicMock(return_value={'result': True, 'comment': 'Mockery'}), + }): + self.assertEqual(acme.cert('test'), { + 'name': 'test', + 'result': True, + 'comment': ['Mockery'], + 'changes': {'old': {'name': 'old cert'}, 'new': {'name': 'new cert'}}, + }) diff --git a/tests/unit/states/test_alias.py b/tests/unit/states/test_alias.py index dc164bab01ff..059ef3bc57c9 100644 --- a/tests/unit/states/test_alias.py +++ b/tests/unit/states/test_alias.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -20,7 +18,6 @@ import salt.states.alias as alias -@skipIf(NO_MOCK, NO_MOCK_REASON) class AliasTest(TestCase, LoaderModuleMockMixin): ''' Validate the alias state diff --git a/tests/unit/states/test_alternatives.py b/tests/unit/states/test_alternatives.py index 7f9ec8a51691..af2c0d8fe0c9 100644 --- a/tests/unit/states/test_alternatives.py +++ b/tests/unit/states/test_alternatives.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.alternatives as alternatives -@skipIf(NO_MOCK, NO_MOCK_REASON) class AlternativesTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.alternatives diff --git a/tests/unit/states/test_apache.py b/tests/unit/states/test_apache.py index 798d0254d807..412a366edb0b 100644 --- a/tests/unit/states/test_apache.py +++ b/tests/unit/states/test_apache.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch, mock_open) @@ -20,7 +18,6 @@ import salt.utils.files -@skipIf(NO_MOCK, NO_MOCK_REASON) class ApacheTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.apache diff --git a/tests/unit/states/test_apache_conf.py b/tests/unit/states/test_apache_conf.py index c42a87580cb1..084c76f9d759 100644 --- a/tests/unit/states/test_apache_conf.py +++ b/tests/unit/states/test_apache_conf.py @@ -4,10 +4,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -16,7 +14,6 @@ import salt.states.apache_conf as apache_conf -@skipIf(NO_MOCK, NO_MOCK_REASON) class ApacheConfTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.apache_conf diff --git a/tests/unit/states/test_apache_module.py b/tests/unit/states/test_apache_module.py index 9421cbe1acb3..3d43deb0e439 100644 --- a/tests/unit/states/test_apache_module.py +++ b/tests/unit/states/test_apache_module.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.apache_module as apache_module -@skipIf(NO_MOCK, NO_MOCK_REASON) class ApacheModuleTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.apache_module diff --git a/tests/unit/states/test_apache_site.py b/tests/unit/states/test_apache_site.py index b0c2b8ec8389..f199c63260f2 100644 --- a/tests/unit/states/test_apache_site.py +++ b/tests/unit/states/test_apache_site.py @@ -4,10 +4,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -16,7 +14,6 @@ import salt.states.apache_site as apache_site -@skipIf(NO_MOCK, NO_MOCK_REASON) class ApacheSiteTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.apache_site diff --git a/tests/unit/states/test_aptpkg.py b/tests/unit/states/test_aptpkg.py index 96011cc46029..30bd6986b460 100644 --- a/tests/unit/states/test_aptpkg.py +++ b/tests/unit/states/test_aptpkg.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.aptpkg as aptpkg -@skipIf(NO_MOCK, NO_MOCK_REASON) class AptTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.aptpkg diff --git a/tests/unit/states/test_archive.py b/tests/unit/states/test_archive.py index 03d3c7d18573..849df5745e7b 100644 --- a/tests/unit/states/test_archive.py +++ b/tests/unit/states/test_archive.py @@ -9,10 +9,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -43,10 +41,9 @@ def _isfile_side_effect(path): '/tmp/test_extracted_tar': False, 'c:\\tmp\\test_extracted_tar': False, '/private/tmp/test_extracted_tar': False, - }[path] + }[path.lower() if salt.utils.platform.is_windows() else path] -@skipIf(NO_MOCK, NO_MOCK_REASON) class ArchiveTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -82,11 +79,11 @@ def test_extracted_tar(self): 'z -v -weird-long-opt arg', ] ret_tar_opts = [ - ['tar', 'x', '--no-anchored', 'foo', '-f'], + ['tar', 'xv', '--no-anchored', 'foo', '-f'], ['tar', 'xv', '-p', '--opt', '-f'], - ['tar', 'x', '-v', '-p', '-f'], - ['tar', 'x', '--long-opt', '-z', '-f'], - ['tar', 'xz', '-v', '-weird-long-opt', 'arg', '-f'], + ['tar', 'xv', '-p', '-f'], + ['tar', 'xv', '--long-opt', '-z', '-f'], + ['tar', 'xvz', '-weird-long-opt', 'arg', '-f'], ] mock_true = MagicMock(return_value=True) @@ -163,7 +160,7 @@ def test_tar_gnutar(self): options='xvzf', enforce_toplevel=False, keep=True) - self.assertEqual(ret['changes']['extracted_files'], 'stdout') + self.assertEqual(ret['changes']['extracted_files'], ['stdout']) def test_tar_bsdtar(self): ''' @@ -202,7 +199,7 @@ def test_tar_bsdtar(self): options='xvzf', enforce_toplevel=False, keep=True) - self.assertEqual(ret['changes']['extracted_files'], 'stderr') + self.assertEqual(ret['changes']['extracted_files'], ['stderr']) def test_extracted_when_if_missing_path_exists(self): ''' @@ -225,3 +222,47 @@ def test_extracted_when_if_missing_path_exists(self): ret['comment'], 'Path {0} exists'.format(if_missing) ) + + def test_clean_parent_conflict(self): + ''' + Tests the call of extraction with gnutar with both clean_parent plus clean set to True + ''' + gnutar = MagicMock(return_value='tar (GNU tar)') + source = '/tmp/foo.tar.gz' + ret_comment = "Only one of 'clean' and 'clean_parent' can be set to True" + mock_false = MagicMock(return_value=False) + mock_true = MagicMock(return_value=True) + state_single_mock = MagicMock(return_value={'local': {'result': True}}) + run_all = MagicMock(return_value={'retcode': 0, 'stdout': 'stdout', 'stderr': 'stderr'}) + mock_source_list = MagicMock(return_value=(source, None)) + list_mock = MagicMock(return_value={ + 'dirs': [], + 'files': ['stdout'], + 'links': [], + 'top_level_dirs': [], + 'top_level_files': ['stdout'], + 'top_level_links': [], + }) + isfile_mock = MagicMock(side_effect=_isfile_side_effect) + + with patch.dict(archive.__salt__, {'cmd.run': gnutar, + 'file.directory_exists': mock_false, + 'file.file_exists': mock_false, + 'state.single': state_single_mock, + 'file.makedirs': mock_true, + 'cmd.run_all': run_all, + 'archive.list': list_mock, + 'file.source_list': mock_source_list}),\ + patch.dict(archive.__states__, {'file.directory': mock_true}),\ + patch.object(os.path, 'isfile', isfile_mock),\ + patch('salt.utils.path.which', MagicMock(return_value=True)): + ret = archive.extracted(os.path.join(os.sep + 'tmp', 'out'), + source, + options='xvzf', + enforce_toplevel=False, + clean=True, + clean_parent=True, + keep=True) + self.assertEqual(ret['result'], False) + self.assertEqual(ret['changes'], {}) + self.assertEqual(ret['comment'], ret_comment) diff --git a/tests/unit/states/test_artifactory.py b/tests/unit/states/test_artifactory.py index 0b5c6ea49350..adf3ded728ec 100644 --- a/tests/unit/states/test_artifactory.py +++ b/tests/unit/states/test_artifactory.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.artifactory as artifactory -@skipIf(NO_MOCK, NO_MOCK_REASON) class ArtifactoryTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.artifactory diff --git a/tests/unit/states/test_at.py b/tests/unit/states/test_at.py index 4a4b75954c6d..4ceb29a78ee1 100644 --- a/tests/unit/states/test_at.py +++ b/tests/unit/states/test_at.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.at as at -@skipIf(NO_MOCK, NO_MOCK_REASON) class AtTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.at diff --git a/tests/unit/states/test_augeas.py b/tests/unit/states/test_augeas.py index 269cfb2cb0bb..4e74299bdc2b 100644 --- a/tests/unit/states/test_augeas.py +++ b/tests/unit/states/test_augeas.py @@ -9,11 +9,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( mock_open, - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -22,7 +20,6 @@ import salt.states.augeas as augeas -@skipIf(NO_MOCK, NO_MOCK_REASON) class AugeasTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.augeas diff --git a/tests/unit/states/test_aws_sqs.py b/tests/unit/states/test_aws_sqs.py index 63f2f61e9f9a..063e61bd7996 100644 --- a/tests/unit/states/test_aws_sqs.py +++ b/tests/unit/states/test_aws_sqs.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.aws_sqs as aws_sqs -@skipIf(NO_MOCK, NO_MOCK_REASON) class AwsSqsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.aws_sqs diff --git a/tests/unit/states/test_beacon.py b/tests/unit/states/test_beacon.py index fcea3297326c..93b5e93cb804 100644 --- a/tests/unit/states/test_beacon.py +++ b/tests/unit/states/test_beacon.py @@ -4,15 +4,11 @@ ''' # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -20,10 +16,7 @@ # Import Salt Libs import salt.states.beacon as beacon -SOCK_DIR = os.path.join(TMP, 'test-socks') - -@skipIf(NO_MOCK, NO_MOCK_REASON) class BeaconTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.beacon @@ -44,7 +37,6 @@ def test_present(self): 'result': False, 'comment': ''} - mock_dict = MagicMock(side_effect=[ret, []]) mock_mod = MagicMock(return_value=ret) mock_lst = MagicMock(side_effect=[{beacon_name: {}}, {beacon_name: {}}, diff --git a/tests/unit/states/test_blockdev.py b/tests/unit/states/test_blockdev.py index cdd13d9fb2b4..0f37afb0205c 100644 --- a/tests/unit/states/test_blockdev.py +++ b/tests/unit/states/test_blockdev.py @@ -8,10 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, + Mock, MagicMock, patch) @@ -20,7 +19,6 @@ import salt.utils.path -@skipIf(NO_MOCK, NO_MOCK_REASON) class BlockdevTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.blockdev @@ -107,3 +105,15 @@ def test_formatted(self): MagicMock(return_value=True)): with patch.dict(blockdev.__opts__, {'test': False}): self.assertDictEqual(blockdev.formatted(name), ret) + + def test__checkblk(self): + ''' + Confirm that we call cmd.run with ignore_retcode=True + ''' + cmd_mock = Mock() + with patch.dict(blockdev.__salt__, {'cmd.run': cmd_mock}): + blockdev._checkblk('/dev/foo') + + cmd_mock.assert_called_once_with( + ['blkid', '-o', 'value', '-s', 'TYPE', '/dev/foo'], + ignore_retcode=True) diff --git a/tests/unit/states/test_boto_apigateway.py b/tests/unit/states/test_boto_apigateway.py index d0fc2f317a20..8d5a034f0d31 100644 --- a/tests/unit/states/test_boto_apigateway.py +++ b/tests/unit/states/test_boto_apigateway.py @@ -11,7 +11,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch # Import Salt libs import salt.config @@ -397,7 +397,7 @@ class BotoApiGatewayStateTestCaseBase(TestCase, LoaderModuleMockMixin): @classmethod def setUpClass(cls): - cls.opts = salt.config.DEFAULT_MINION_OPTS + cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() cls.opts['grains'] = salt.loader.grains(cls.opts) @classmethod @@ -446,7 +446,6 @@ def setUp(self): @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoApiGatewayTestCase(BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin): ''' TestCase for salt.modules.boto_apigateway state.module @@ -1025,7 +1024,6 @@ def test_absent_when_nuke_api_and_other_stages_deployments_exist(self): @skipIf(_has_required_botocore() is False, 'The botocore module must be greater than' ' or equal to version {0}'.format(required_botocore_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoApiGatewayUsagePlanTestCase(BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin): ''' TestCase for salt.modules.boto_apigateway state.module, usage_plans portion @@ -1303,7 +1301,6 @@ def test_usage_plan_absent_if_IOError_is_raised(self, *args): @skipIf(_has_required_botocore() is False, 'The botocore module must be greater than' ' or equal to version {0}'.format(required_botocore_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoApiGatewayUsagePlanAssociationTestCase(BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin): ''' TestCase for salt.modules.boto_apigateway state.module, usage_plans_association portion diff --git a/tests/unit/states/test_boto_asg.py b/tests/unit/states/test_boto_asg.py index ab55b1bb0ecb..275ec234400d 100644 --- a/tests/unit/states/test_boto_asg.py +++ b/tests/unit/states/test_boto_asg.py @@ -7,14 +7,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.states.boto_asg as boto_asg -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoAsgTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_asg diff --git a/tests/unit/states/test_boto_cloudfront.py b/tests/unit/states/test_boto_cloudfront.py index 25f26d561136..5db1b2ad8479 100644 --- a/tests/unit/states/test_boto_cloudfront.py +++ b/tests/unit/states/test_boto_cloudfront.py @@ -9,8 +9,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.config @@ -18,7 +18,6 @@ import salt.states.boto_cloudfront as boto_cloudfront -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCloudfrontTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_cloudfront @@ -36,7 +35,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.opts = salt.config.DEFAULT_MINION_OPTS + cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() cls.name = 'my_distribution' cls.base_ret = {'name': cls.name, 'changes': {}} diff --git a/tests/unit/states/test_boto_cloudtrail.py b/tests/unit/states/test_boto_cloudtrail.py index 3edd5b259424..93c3367a5470 100644 --- a/tests/unit/states/test_boto_cloudtrail.py +++ b/tests/unit/states/test_boto_cloudtrail.py @@ -12,8 +12,6 @@ from tests.support.unit import skipIf, TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -102,7 +100,6 @@ def _has_required_boto(): @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCloudTrailStateTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None @@ -128,7 +125,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.opts = salt.config.DEFAULT_MINION_OPTS + cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() cls.opts['grains'] = salt.loader.grains(cls.opts) @classmethod diff --git a/tests/unit/states/test_boto_cloudwatch_alarm.py b/tests/unit/states/test_boto_cloudwatch_alarm.py index a9ffd13a8a0d..758166d347f8 100644 --- a/tests/unit/states/test_boto_cloudwatch_alarm.py +++ b/tests/unit/states/test_boto_cloudwatch_alarm.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.boto_cloudwatch_alarm as boto_cloudwatch_alarm -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCloudwatchAlarmTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_cloudwatch_alarm diff --git a/tests/unit/states/test_boto_cloudwatch_event.py b/tests/unit/states/test_boto_cloudwatch_event.py index 395a6b30593b..9d0008fb5a3b 100644 --- a/tests/unit/states/test_boto_cloudwatch_event.py +++ b/tests/unit/states/test_boto_cloudwatch_event.py @@ -9,7 +9,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.mock import patch, MagicMock # Import Salt libs import salt.config @@ -105,7 +105,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.opts = salt.config.DEFAULT_MINION_OPTS + cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() cls.opts['grains'] = salt.loader.grains(cls.opts) @classmethod @@ -133,7 +133,6 @@ def setUp(self): @skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCloudWatchEventTestCase(BotoCloudWatchEventStateTestCaseBase, BotoCloudWatchEventTestCaseMixin): def test_present_when_failing_to_describe_rule(self): ''' diff --git a/tests/unit/states/test_boto_cognitoidentity.py b/tests/unit/states/test_boto_cognitoidentity.py index f0834a3ce986..71439fc2de85 100644 --- a/tests/unit/states/test_boto_cognitoidentity.py +++ b/tests/unit/states/test_boto_cognitoidentity.py @@ -10,7 +10,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.mock import patch, MagicMock # Import Salt libs import salt.config @@ -150,7 +150,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.opts = salt.config.DEFAULT_MINION_OPTS + cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() cls.opts['grains'] = salt.loader.grains(cls.opts) @classmethod @@ -181,7 +181,6 @@ def setUp(self): @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCognitoIdentityTestCase(BotoCognitoIdentityStateTestCaseBase, BotoCognitoIdentityTestCaseMixin): ''' TestCase for salt.states.boto_cognitoidentity state.module diff --git a/tests/unit/states/test_boto_dynamodb.py b/tests/unit/states/test_boto_dynamodb.py index a4c02e67fd17..e94f0786bc5a 100644 --- a/tests/unit/states/test_boto_dynamodb.py +++ b/tests/unit/states/test_boto_dynamodb.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.boto_dynamodb as boto_dynamodb -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoDynamodbTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_dynamodb diff --git a/tests/unit/states/test_boto_ec2.py b/tests/unit/states/test_boto_ec2.py index 0f6408475ed9..20bc53d2eaed 100644 --- a/tests/unit/states/test_boto_ec2.py +++ b/tests/unit/states/test_boto_ec2.py @@ -7,14 +7,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.states.boto_ec2 as boto_ec2 -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoEc2TestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_ec2 diff --git a/tests/unit/states/test_boto_elasticache.py b/tests/unit/states/test_boto_elasticache.py index 5ee6145d9e9d..56c4c3c36f9f 100644 --- a/tests/unit/states/test_boto_elasticache.py +++ b/tests/unit/states/test_boto_elasticache.py @@ -7,14 +7,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.states.boto_elasticache as boto_elasticache -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoElasticacheTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_elasticache diff --git a/tests/unit/states/test_boto_elasticsearch_domain.py b/tests/unit/states/test_boto_elasticsearch_domain.py index e6664ab6674c..2484a2cccdec 100644 --- a/tests/unit/states/test_boto_elasticsearch_domain.py +++ b/tests/unit/states/test_boto_elasticsearch_domain.py @@ -9,7 +9,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, NO_MOCK, NO_MOCK_REASON, patch +from tests.support.mock import MagicMock, patch # Import Salt libs from salt.ext import six @@ -108,7 +108,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.opts = salt.config.DEFAULT_MINION_OPTS + cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() cls.opts['grains'] = salt.loader.grains(cls.opts) @classmethod @@ -140,7 +140,6 @@ def setUp(self): @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoElasticsearchDomainTestCase(BotoElasticsearchDomainStateTestCaseBase, BotoElasticsearchDomainTestCaseMixin): ''' TestCase for salt.modules.boto_elasticsearch_domain state.module diff --git a/tests/unit/states/test_boto_elb.py b/tests/unit/states/test_boto_elb.py index 87291ef180ea..69c49664ddd4 100644 --- a/tests/unit/states/test_boto_elb.py +++ b/tests/unit/states/test_boto_elb.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -19,7 +17,6 @@ import salt.states.boto_elb as boto_elb -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoElbTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_elb diff --git a/tests/unit/states/test_boto_iam_role.py b/tests/unit/states/test_boto_iam_role.py index b9789e3bf8f3..4792035d1ed7 100644 --- a/tests/unit/states/test_boto_iam_role.py +++ b/tests/unit/states/test_boto_iam_role.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.boto_iam_role as boto_iam_role -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoIAMRoleTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_iam_role diff --git a/tests/unit/states/test_boto_iot.py b/tests/unit/states/test_boto_iot.py index 530d88c46440..e03aa33a8173 100644 --- a/tests/unit/states/test_boto_iot.py +++ b/tests/unit/states/test_boto_iot.py @@ -9,7 +9,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, NO_MOCK, NO_MOCK_REASON, patch +from tests.support.mock import MagicMock, patch # Import Salt libs import salt.config @@ -133,7 +133,6 @@ def _has_required_boto(): @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoIoTStateTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None @@ -159,7 +158,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.opts = salt.config.DEFAULT_MINION_OPTS + cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() cls.opts['grains'] = salt.loader.grains(cls.opts) @classmethod @@ -400,7 +399,6 @@ def test_detached_with_failure(self): ' module must be greater than or equal to' ' version {1}.' .format(required_boto3_version, required_botocore_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoIoTTopicRuleTestCase(BotoIoTStateTestCaseBase, BotoIoTTestCaseMixin): ''' TestCase for salt.modules.boto_iot state.module rules diff --git a/tests/unit/states/test_boto_kinesis.py b/tests/unit/states/test_boto_kinesis.py index 3605e00467dd..4ef3d1ba5187 100644 --- a/tests/unit/states/test_boto_kinesis.py +++ b/tests/unit/states/test_boto_kinesis.py @@ -4,10 +4,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -15,7 +13,6 @@ import salt.states.boto_kinesis as boto_kinesis -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoKinesisTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_kinesis diff --git a/tests/unit/states/test_boto_lambda.py b/tests/unit/states/test_boto_lambda.py index fe92feeeafaa..99e9d64768c4 100644 --- a/tests/unit/states/test_boto_lambda.py +++ b/tests/unit/states/test_boto_lambda.py @@ -9,7 +9,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, NO_MOCK, NO_MOCK_REASON, patch +from tests.support.mock import MagicMock, patch # Import Salt libs import salt.config @@ -101,7 +101,6 @@ def _has_required_boto(): ('The boto3 module must be greater than or equal to version {0}, ' 'and botocore must be greater than or equal to {1}'.format( required_boto3_version, required_botocore_version))) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoLambdaStateTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None @@ -127,7 +126,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.opts = salt.config.DEFAULT_MINION_OPTS + cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() cls.opts['grains'] = salt.loader.grains(cls.opts) @classmethod @@ -307,7 +306,6 @@ def test_present_when_function_exists_and_permissions(self): ('The boto3 module must be greater than or equal to version {0}, ' 'and botocore must be greater than or equal to {1}'.format( required_boto3_version, required_botocore_version))) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoLambdaAliasTestCase(BotoLambdaStateTestCaseBase, BotoLambdaTestCaseMixin): ''' TestCase for salt.modules.boto_lambda state.module aliases @@ -393,7 +391,6 @@ def test_absent_with_failure(self): ('The boto3 module must be greater than or equal to version {0}, ' 'and botocore must be greater than or equal to {1}'.format( required_boto3_version, required_botocore_version))) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoLambdaEventSourceMappingTestCase(BotoLambdaStateTestCaseBase, BotoLambdaTestCaseMixin): ''' TestCase for salt.modules.boto_lambda state.module event_source_mappings diff --git a/tests/unit/states/test_boto_lc.py b/tests/unit/states/test_boto_lc.py index 61401b9b1428..62cc3f196ae9 100644 --- a/tests/unit/states/test_boto_lc.py +++ b/tests/unit/states/test_boto_lc.py @@ -7,15 +7,14 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.states.boto_lc as boto_lc from salt.exceptions import SaltInvocationError -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoLcTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_lc diff --git a/tests/unit/states/test_boto_route53.py b/tests/unit/states/test_boto_route53.py index 808b56970386..d0b66b2f3517 100644 --- a/tests/unit/states/test_boto_route53.py +++ b/tests/unit/states/test_boto_route53.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.boto_route53 as boto_route53 -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoRoute53TestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_route53 diff --git a/tests/unit/states/test_boto_s3_bucket.py b/tests/unit/states/test_boto_s3_bucket.py index b3f93c27c5f9..8c6c96b083c6 100644 --- a/tests/unit/states/test_boto_s3_bucket.py +++ b/tests/unit/states/test_boto_s3_bucket.py @@ -10,7 +10,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, NO_MOCK, NO_MOCK_REASON, patch +from tests.support.mock import MagicMock, patch # Import Salt libs from salt.ext import six @@ -264,7 +264,6 @@ def _has_required_boto(): @skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' ' or equal to version {0}' .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoS3BucketStateTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None @@ -290,7 +289,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.opts = salt.config.DEFAULT_MINION_OPTS + cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() cls.opts['grains'] = salt.loader.grains(cls.opts) @classmethod diff --git a/tests/unit/states/test_boto_sns.py b/tests/unit/states/test_boto_sns.py index 0d33ab22a4cd..70da60847292 100644 --- a/tests/unit/states/test_boto_sns.py +++ b/tests/unit/states/test_boto_sns.py @@ -7,14 +7,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.states.boto_sns as boto_sns -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoSnsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_sns diff --git a/tests/unit/states/test_boto_sqs.py b/tests/unit/states/test_boto_sqs.py index 2b8e46ac88cb..edecaf558e3f 100644 --- a/tests/unit/states/test_boto_sqs.py +++ b/tests/unit/states/test_boto_sqs.py @@ -8,8 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.config @@ -17,7 +17,6 @@ import salt.states.boto_sqs as boto_sqs -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoSqsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.boto_sqs @@ -35,7 +34,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.opts = salt.config.DEFAULT_MINION_OPTS + cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() @classmethod def tearDownClass(cls): diff --git a/tests/unit/states/test_boto_vpc.py b/tests/unit/states/test_boto_vpc.py index a2a4facb3f65..4e574055d410 100644 --- a/tests/unit/states/test_boto_vpc.py +++ b/tests/unit/states/test_boto_vpc.py @@ -10,8 +10,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch -from tests.support.paths import TESTS_DIR +from tests.support.mock import patch +from tests.support.runtests import RUNTIME_VARS # Import Salt libs @@ -28,7 +28,7 @@ from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin try: import boto - boto.ENDPOINTS_PATH = os.path.join(TESTS_DIR, 'unit/files/endpoints.json') + boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') import boto3 from boto.exception import BotoServerError @@ -110,7 +110,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.opts = salt.config.DEFAULT_MINION_OPTS + cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() cls.opts['grains'] = salt.loader.grains(cls.opts) @classmethod @@ -126,7 +126,6 @@ def setUp(self): conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -279,7 +278,6 @@ def test_absent_with_failure(self): self.assertTrue('Mocked error' in resource_absent_result['comment']) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -292,7 +290,6 @@ class BotoVpcSubnetsTestCase(BotoVpcStateTestCaseBase, BotoVpcResourceTestCaseMi extra_kwargs = {'cidr_block': cidr_block} -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' @@ -304,7 +301,6 @@ class BotoVpcInternetGatewayTestCase(BotoVpcStateTestCaseBase, BotoVpcResourceTe backend_delete = 'InternetGatewayBackend.delete_internet_gateway' -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(six.PY3, 'Disabled for Python 3 due to upstream bugs: ' 'https://github.com/spulec/moto/issues/548 and ' 'https://github.com/gabrielfalcao/HTTPretty/issues/325') @@ -321,8 +317,8 @@ class BotoVpcRouteTableTestCase(BotoVpcStateTestCaseBase, BotoVpcResourceTestCas @mock_ec2_deprecated def test_present_with_subnets(self): vpc = self._create_vpc(name='test') - subnet1 = self._create_subnet(vpc_id=vpc.id, name='test1') - subnet2 = self._create_subnet(vpc_id=vpc.id, name='test2') + subnet1 = self._create_subnet(vpc_id=vpc.id, cidr_block='10.0.0.0/25', name='test1') + subnet2 = self._create_subnet(vpc_id=vpc.id, cidr_block='10.0.0.128/25', name='test2') route_table_present_result = self.salt_states['boto_vpc.route_table_present']( name='test', vpc_name='test', subnet_names=['test1'], subnet_ids=[subnet2.id]) diff --git a/tests/unit/states/test_bower.py b/tests/unit/states/test_bower.py index d591643ae621..c0ee24a8b106 100644 --- a/tests/unit/states/test_bower.py +++ b/tests/unit/states/test_bower.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) class BowerTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.bower diff --git a/tests/unit/states/test_chef.py b/tests/unit/states/test_chef.py index 79861156ddbd..53bdf8db84d0 100644 --- a/tests/unit/states/test_chef.py +++ b/tests/unit/states/test_chef.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.chef as chef -@skipIf(NO_MOCK, NO_MOCK_REASON) class ChefTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.chef diff --git a/tests/unit/states/test_cloud.py b/tests/unit/states/test_cloud.py index 26bc9cb6341f..1f40d7bcba1a 100644 --- a/tests/unit/states/test_cloud.py +++ b/tests/unit/states/test_cloud.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -19,7 +17,6 @@ import salt.utils.cloud -@skipIf(NO_MOCK, NO_MOCK_REASON) class CloudTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.cloud diff --git a/tests/unit/states/test_cmd.py b/tests/unit/states/test_cmd.py index 5c5ddb680427..738fcdad6b47 100644 --- a/tests/unit/states/test_cmd.py +++ b/tests/unit/states/test_cmd.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -21,7 +19,6 @@ import salt.states.cmd as cmd -@skipIf(NO_MOCK, NO_MOCK_REASON) class CmdTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.cmd @@ -148,6 +145,29 @@ def test_run(self): 'skip_watch': True}) self.assertDictEqual(cmd.run(name, onlyif=''), ret) + def test_run_root(self): + ''' + Test to run a command with a different root + ''' + name = 'cmd.script' + + ret = {'name': name, + 'result': False, + 'changes': {}, + 'comment': ''} + + with patch.dict(cmd.__grains__, {'shell': 'shell'}): + with patch.dict(cmd.__opts__, {'test': False}): + mock = MagicMock(side_effect=[CommandExecutionError, + {'retcode': 1}]) + with patch.dict(cmd.__salt__, {'cmd.run_chroot': mock}): + ret.update({'comment': '', 'result': False}) + self.assertDictEqual(cmd.run(name, root='/mnt'), ret) + + ret.update({'comment': 'Command "cmd.script" run', + 'result': False, 'changes': {'retcode': 1}}) + self.assertDictEqual(cmd.run(name, root='/mnt'), ret) + # 'script' function tests: 1 def test_script(self): diff --git a/tests/unit/states/test_composer.py b/tests/unit/states/test_composer.py index 649194702e51..7a2984e8ad7f 100644 --- a/tests/unit/states/test_composer.py +++ b/tests/unit/states/test_composer.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -20,7 +18,6 @@ import salt.states.composer as composer -@skipIf(NO_MOCK, NO_MOCK_REASON) class ComposerTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.composer diff --git a/tests/unit/states/test_cron.py b/tests/unit/states/test_cron.py index d399d628e5fe..1d42e3a94b86 100644 --- a/tests/unit/states/test_cron.py +++ b/tests/unit/states/test_cron.py @@ -8,8 +8,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch from salt.ext.six.moves import StringIO import salt.modules.cron as cronmod @@ -53,7 +53,6 @@ def write_crontab(*args, **kw): } -@skipIf(NO_MOCK, NO_MOCK_REASON) class CronTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/states/test_cyg.py b/tests/unit/states/test_cyg.py index 2ea792a4db98..c01338cc1095 100644 --- a/tests/unit/states/test_cyg.py +++ b/tests/unit/states/test_cyg.py @@ -4,7 +4,7 @@ # # Import Salt Testing libs # from tests.support.unit import skipIf, TestCase -# from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +# from tests.support.mock import MagicMock, patch # # Late import so mock can do its job # import salt.states.cyg as cyg @@ -12,7 +12,6 @@ # cyg.__opts__ = {'test': False} -# @skipIf(NO_MOCK, NO_MOCK_REASON) # class TestGemState(TestCase): # def test_installed(self): diff --git a/tests/unit/states/test_ddns.py b/tests/unit/states/test_ddns.py index 7b3735035598..adb79a95cd01 100644 --- a/tests/unit/states/test_ddns.py +++ b/tests/unit/states/test_ddns.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.ddns as ddns -@skipIf(NO_MOCK, NO_MOCK_REASON) class DdnsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ddns diff --git a/tests/unit/states/test_debconfmod.py b/tests/unit/states/test_debconfmod.py index 3951ae54d7b4..b05afac255de 100644 --- a/tests/unit/states/test_debconfmod.py +++ b/tests/unit/states/test_debconfmod.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.debconfmod as debconfmod -@skipIf(NO_MOCK, NO_MOCK_REASON) class DebconfmodTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.debconfmod diff --git a/tests/unit/states/test_disk.py b/tests/unit/states/test_disk.py index 8c2e4aa9a619..64ac2a917f89 100644 --- a/tests/unit/states/test_disk.py +++ b/tests/unit/states/test_disk.py @@ -7,18 +7,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) +from os import path # Import Salt Libs import salt.states.disk as disk -@skipIf(NO_MOCK, NO_MOCK_REASON) class DiskTestCase(TestCase, LoaderModuleMockMixin): ''' Test disk state @@ -50,8 +48,12 @@ def setup_loader_modules(self): 'filesystem': 'tmpfs', 'used': '0'} } + + self.mock_data_path = {'/foo': {'available': '42', 'total': '100'}} + self.addCleanup(delattr, self, 'mock_data') - return {disk: {'__salt__': {'disk.usage': MagicMock(return_value=self.mock_data)}}} + self.addCleanup(delattr, self, 'mock_data_path') + return {disk: {'__salt__': {'disk.usage': MagicMock(return_value=self.mock_data), 'status.diskusage': MagicMock(return_value=self.mock_data_path)}}} def test_status_missing(self): ''' @@ -60,7 +62,7 @@ def test_status_missing(self): mock_fs = '/mnt/cheese' mock_ret = {'name': mock_fs, 'result': False, - 'comment': 'Named disk mount not present ', + 'comment': 'Disk mount /mnt/cheese not present. Directory /mnt/cheese does not exist or is not a directory', 'changes': {}, 'data': {}} @@ -203,27 +205,290 @@ def test_status(self): 'changes': {}, 'data': {}} - mock = MagicMock(side_effect=[[], [mock_fs], {mock_fs: {'capacity': '8 %', 'used': '8'}}, - {mock_fs: {'capacity': '22 %', 'used': '22'}}, - {mock_fs: {'capacity': '15 %', 'used': '15'}}]) + data_1 = {'capacity': '8 %', 'used': '8', 'available': '92'} + data_2 = {'capacity': '22 %', 'used': '22', 'available': '78'} + data_3 = {'capacity': '15 %', 'used': '15', 'available': '85'} + mock = MagicMock(side_effect=[[], {mock_fs: data_1}, {mock_fs: data_2}, {mock_fs: data_3}]) with patch.dict(disk.__salt__, {'disk.usage': mock}): - comt = ('Named disk mount not present ') - ret.update({'comment': comt}) - self.assertDictEqual(disk.status(mock_fs), ret) - - comt = ('minimum must be less than maximum ') - ret.update({'comment': comt}) - self.assertDictEqual(disk.status(mock_fs, '10', '20', absolute=True), ret) - - comt = ('Disk used space is below minimum of 10 KB at 8 KB') - ret.update({'comment': comt, 'data': {'capacity': '8 %', 'used': '8'}}) - self.assertDictEqual(disk.status(mock_fs, '20', '10', absolute=True), ret) - - comt = ('Disk used space is above maximum of 20 KB at 22 KB') - ret.update({'comment': comt, 'data': {'capacity': '22 %', 'used': '22'}}) - self.assertDictEqual(disk.status(mock_fs, '20', '10', absolute=True), ret) - - comt = ('Disk used space in acceptable range') - ret.update({'comment': comt, 'result': True, - 'data': {'capacity': '15 %', 'used': '15'}}) - self.assertDictEqual(disk.status(mock_fs, '20', '10', absolute=True), ret) + mock = MagicMock(return_value=False) + with patch.object(path, 'isdir', mock): + comt = 'Disk mount / not present. Directory / does not exist or is not a directory' + ret.update({'comment': comt}) + self.assertDictEqual(disk.status(mock_fs), ret) + + comt = 'minimum must be less than maximum ' + ret.update({'comment': comt}) + self.assertDictEqual(disk.status(mock_fs, '10', '20', absolute=True), ret) + + comt = 'Disk used space is below minimum of 10 KB at 8 KB' + ret.update({'comment': comt, 'data': data_1}) + self.assertDictEqual(disk.status(mock_fs, '20', '10', absolute=True), ret) + + comt = 'Disk used space is above maximum of 20 KB at 22 KB' + ret.update({'comment': comt, 'data': data_2}) + self.assertDictEqual(disk.status(mock_fs, '20', '10', absolute=True), ret) + + comt = 'Disk used space in acceptable range' + ret.update({'comment': comt, 'result': True, 'data': data_3}) + self.assertDictEqual(disk.status(mock_fs, '20', '10', absolute=True), ret) + + def test_path_missing(self): + mock_fs = '/bar' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk mount {0} not present. Directory {0} does not exist or is not a directory'.format( + mock_fs), + 'changes': {}, + 'data': {}} + mock = MagicMock(return_value=False) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '58', '55', absolute=True, free=False), mock_ret) + + # acceptable range + def test_path_used_absolute_acceptable(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': True, + 'comment': 'Disk used space in acceptable range', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '58', '55', absolute=True, free=False), mock_ret) + + def test_path_used_relative_acceptable(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': True, + 'comment': 'Disk used space in acceptable range', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '100%', '57%', absolute=False, free=False), mock_ret) + + def test_path_free_absolute_acceptable(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': True, + 'comment': 'Disk used space in acceptable range', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '100', '42', absolute=True, free=True), mock_ret) + + def test_path_free_relative_acceptable(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': True, + 'comment': 'Disk used space in acceptable range', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '42%', '41%', absolute=False, free=True), mock_ret) + + def test_mount_used_absolute_acceptable(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': True, + 'comment': 'Disk used space in acceptable range', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + self.assertDictEqual(disk.status(mock_fs, '2172881', '2172880', absolute=True, free=False), mock_ret) + + def test_mount_used_relative_acceptable(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': True, + 'comment': 'Disk used space in acceptable range', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + + self.assertDictEqual(disk.status(mock_fs, '7%', '6%', absolute=False, free=False), mock_ret) + + def test_mount_free_absolute_acceptable(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': True, + 'comment': 'Disk used space in acceptable range', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + self.assertDictEqual(disk.status(mock_fs, '37087976', '37087975', absolute=True, free=True), mock_ret) + + def test_mount_free_relative_acceptable(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': True, + 'comment': 'Disk used space in acceptable range', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + + self.assertDictEqual(disk.status(mock_fs, '100%', '94%', absolute=False, free=True), mock_ret) + + # below minimum + def test_path_used_absolute_below(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk used space is below minimum of 59 KB at 58 KB', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '60', '59', absolute=True, free=False), mock_ret) + + def test_path_used_relative_below(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk used space is below minimum of 59 % at 58.0 %', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '60%', '59%', absolute=False, free=False), mock_ret) + + def test_path_free_absolute_below(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk available space is below minimum of 43 KB at 42 KB', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '100', '43', absolute=True, free=True), mock_ret) + + def test_path_free_relative_below(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk available space is below minimum of 43 % at 42.0 %', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '100%', '43%', absolute=False, free=True), mock_ret) + + def test_mount_used_absolute_below(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk used space is below minimum of 2172881 KB at 2172880 KB', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + self.assertDictEqual(disk.status(mock_fs, '2172882', '2172881', absolute=True, free=False), mock_ret) + + def test_mount_used_relative_below(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk used space is below minimum of 7 % at 6 %', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + + self.assertDictEqual(disk.status(mock_fs, '8%', '7%', absolute=False, free=False), mock_ret) + + def test_mount_free_absolute_below(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk available space is below minimum of 37087977 KB at 37087976 KB', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + self.assertDictEqual(disk.status(mock_fs, '37087978', '37087977', absolute=True, free=True), mock_ret) + + def test_mount_free_relative_below(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk available space is below minimum of 95 % at 94 %', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + + self.assertDictEqual(disk.status(mock_fs, '100%', '95%', absolute=False, free=True), mock_ret) + + # above maximum + def test_path_used_absolute_above(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk used space is above maximum of 57 KB at 58 KB', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '57', '56', absolute=True, free=False), mock_ret) + + def test_path_used_relative_above(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk used space is above maximum of 57 % at 58.0 %', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '57%', '56%', absolute=False, free=False), mock_ret) + + def test_path_free_absolute_above(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk available space is above maximum of 41 KB at 42 KB', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '41', '40', absolute=True, free=True), mock_ret) + + def test_path_free_relative_above(self): + mock_fs = '/foo' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk available space is above maximum of 41 % at 42.0 %', + 'changes': {}, + 'data': self.mock_data_path} + mock = MagicMock(return_value=True) + with patch.object(path, 'isdir', mock): + self.assertDictEqual(disk.status(mock_fs, '41%', '40%', absolute=False, free=True), mock_ret) + + def test_mount_used_absolute_above(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk used space is above maximum of 2172879 KB at 2172880 KB', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + self.assertDictEqual(disk.status(mock_fs, '2172879', '2172878', absolute=True, free=False), mock_ret) + + def test_mount_used_relative_above(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk used space is above maximum of 5 % at 6 %', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + + self.assertDictEqual(disk.status(mock_fs, '5%', '4%', absolute=False, free=False), mock_ret) + + def test_mount_free_absolute_above(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk available space is above maximum of 37087975 KB at 37087976 KB', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + self.assertDictEqual(disk.status(mock_fs, '37087975', '37087974', absolute=True, free=True), mock_ret) + + def test_mount_free_relative_above(self): + mock_fs = '/' + mock_ret = {'name': mock_fs, + 'result': False, + 'comment': 'Disk available space is above maximum of 93 % at 94 %', + 'changes': {}, + 'data': self.mock_data[mock_fs]} + + self.assertDictEqual(disk.status(mock_fs, '93%', '92%', absolute=False, free=True), mock_ret) diff --git a/tests/unit/states/test_docker_image.py b/tests/unit/states/test_docker_image.py index df77541a568e..416267bf86b5 100644 --- a/tests/unit/states/test_docker_image.py +++ b/tests/unit/states/test_docker_image.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -21,7 +19,6 @@ import salt.states.docker_image as docker_state -@skipIf(NO_MOCK, NO_MOCK_REASON) class DockerImageTestCase(TestCase, LoaderModuleMockMixin): ''' Test docker_image states diff --git a/tests/unit/states/test_docker_volume.py b/tests/unit/states/test_docker_volume.py index 862acb9d739d..f62ea0a655d7 100644 --- a/tests/unit/states/test_docker_volume.py +++ b/tests/unit/states/test_docker_volume.py @@ -8,11 +8,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( Mock, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -21,7 +19,6 @@ import salt.states.docker_volume as docker_state -@skipIf(NO_MOCK, NO_MOCK_REASON) class DockerVolumeTestCase(TestCase, LoaderModuleMockMixin): ''' Test docker_volume states diff --git a/tests/unit/states/test_drac.py b/tests/unit/states/test_drac.py index daacd63e988f..ef6560be1456 100644 --- a/tests/unit/states/test_drac.py +++ b/tests/unit/states/test_drac.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.drac as drac -@skipIf(NO_MOCK, NO_MOCK_REASON) class DracTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.drac diff --git a/tests/unit/states/test_elasticsearch.py b/tests/unit/states/test_elasticsearch.py index 243fb0ca757b..c8bf3debaa5b 100644 --- a/tests/unit/states/test_elasticsearch.py +++ b/tests/unit/states/test_elasticsearch.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -21,7 +19,6 @@ from salt.states import elasticsearch -@skipIf(NO_MOCK, NO_MOCK_REASON) class ElasticsearchTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.elasticsearch diff --git a/tests/unit/states/test_eselect.py b/tests/unit/states/test_eselect.py index 782a02ebdc79..9030727ab321 100644 --- a/tests/unit/states/test_eselect.py +++ b/tests/unit/states/test_eselect.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.eselect as eselect -@skipIf(NO_MOCK, NO_MOCK_REASON) class EselectTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.eselect diff --git a/tests/unit/states/test_esxdatacenter.py b/tests/unit/states/test_esxdatacenter.py index 38d6f9a86b6a..53348c92e00a 100644 --- a/tests/unit/states/test_esxdatacenter.py +++ b/tests/unit/states/test_esxdatacenter.py @@ -14,16 +14,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class DatacenterConfiguredTestCase(TestCase, LoaderModuleMockMixin): '''Tests for salt.modules.esxdatacenter.datacenter_configured''' diff --git a/tests/unit/states/test_event.py b/tests/unit/states/test_event.py index 7b3e1246a4fa..cb0878df2586 100644 --- a/tests/unit/states/test_event.py +++ b/tests/unit/states/test_event.py @@ -10,16 +10,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class EventTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the event state diff --git a/tests/unit/states/test_file.py b/tests/unit/states/test_file.py index 72cc1fe72867..d761b8704fd2 100644 --- a/tests/unit/states/test_file.py +++ b/tests/unit/states/test_file.py @@ -20,14 +20,12 @@ from tests.support.unit import skipIf, TestCase from tests.support.helpers import destructiveTest from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, Mock, MagicMock, call, mock_open, patch) -from tests.support.paths import TMP +from tests.support.runtests import RUNTIME_VARS # Import salt libs import salt.utils.files @@ -44,7 +42,6 @@ import salt.utils.win_functions -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestFileState(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -341,27 +338,6 @@ def return_val(kwargs): 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, - 'file.is_link': mock_f, - 'file.readlink': mock_target, - 'file.symlink': mock_t, - 'user.info': mock_t, - 'file.lchown': mock_f}),\ - patch.dict(filestate.__opts__, {'test': False}),\ - patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])),\ - patch.object(os.path, 'isfile', mock_t),\ - patch.object(os.path, 'exists', mock_f),\ - 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, - 'changes': {}}) - 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, @@ -451,6 +427,331 @@ def return_val(kwargs): filestate.symlink(name, target, user=user, group=group), ret) + @skipIf(salt.utils.platform.is_windows(), 'Do not run on Windows') + def test_hardlink(self): + ''' + Test to create a hardlink. + ''' + + name = os.path.join(os.sep, 'tmp', 'testfile.txt') + target = salt.utils.files.mkstemp() + test_dir = os.path.join(os.sep, 'tmp') + user, group = 'salt', 'saltstack' + + def return_val(**kwargs): + res = { + 'name': name, + 'result': False, + 'comment': '', + 'changes': {}, + } + res.update(kwargs) + return res + + mock_t = MagicMock(return_value=True) + mock_f = MagicMock(return_value=False) + mock_empty = MagicMock(return_value='') + mock_uid = MagicMock(return_value='U1001') + mock_gid = MagicMock(return_value='g1001') + mock_nothing = MagicMock(return_value={}) + mock_stats = MagicMock(return_value={'inode': 1}) + mock_execerror = MagicMock(side_effect=CommandExecutionError) + + patches = {} + patches['file.user_to_uid'] = mock_empty + patches['file.group_to_gid'] = mock_empty + patches['user.info'] = mock_empty + patches['file.is_hardlink'] = mock_t + patches['file.stats'] = mock_empty + + # Argument validation + with patch.dict(filestate.__salt__, patches): + expected = ('Must provide name to file.hardlink') + ret = return_val(comment=expected, name='') + self.assertDictEqual(filestate.hardlink('', target), ret) + + # User validation for dir_mode + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_empty}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.object(os.path, 'isabs', mock_t): + expected = 'User {0} does not exist'.format(user) + ret = return_val(comment=expected, name=name) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), + ret) + + # Group validation for dir_mode + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_empty}),\ + patch.object(os.path, 'isabs', mock_t): + expected = 'Group {0} does not exist'.format(group) + ret = return_val(comment=expected, name=name) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), + ret) + + # Absolute path for name + nonabs = './non-existent-path/to/non-existent-file' + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}): + expected = 'Specified file {0} is not an absolute path'.format(nonabs) + ret = return_val(comment=expected, name=nonabs) + self.assertDictEqual(filestate.hardlink(nonabs, target, user=user, + group=group), ret) + + # Absolute path for target + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}): + expected = 'Specified target {0} is not an absolute path'.format(nonabs) + ret = return_val(comment=expected, name=name) + self.assertDictEqual(filestate.hardlink(name, nonabs, user=user, + group=group), ret) + # Test option -- nonexistent target + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.object(os.path, 'exists', mock_f),\ + patch.dict(filestate.__opts__, {'test': True}): + expected = 'Target {0} for hard link does not exist'.format(target) + ret = return_val(comment=expected, name=name) + self.assertDictEqual(filestate.hardlink(name, target, user=user, + group=group), ret) + + # Test option -- target is a directory + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.object(os.path, 'exists', mock_t),\ + patch.dict(filestate.__opts__, {'test': True}): + expected = 'Unable to hard link from directory {0}'.format(test_dir) + ret = return_val(comment=expected, name=name) + self.assertDictEqual(filestate.hardlink(name, test_dir, user=user, + group=group), ret) + + # Test option -- name is a directory + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__opts__, {'test': True}): + expected = 'Unable to hard link to directory {0}'.format(test_dir) + ret = return_val(comment=expected, name=test_dir) + self.assertDictEqual(filestate.hardlink(test_dir, target, user=user, + group=group), ret) + + # Test option -- name does not exist + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__opts__, {'test': True}): + expected = 'Hard link {0} to {1} is set for creation'.format(name, target) + changes = dict(new=name) + ret = return_val(result=None, comment=expected, name=name, changes=changes) + self.assertDictEqual(filestate.hardlink(name, target, + user=user, group=group), ret) + + # Test option -- hardlink matches + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_t}),\ + patch.dict(filestate.__salt__, {'file.stats': mock_stats}),\ + patch.object(os.path, 'exists', mock_t),\ + patch.dict(filestate.__opts__, {'test': True}): + expected = 'The hard link {0} is presently targetting {1}'.format(name, target) + ret = return_val(result=True, comment=expected, name=name) + self.assertDictEqual(filestate.hardlink(name, target, + user=user, group=group), ret) + + # Test option -- hardlink does not match + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_t}),\ + patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ + patch.object(os.path, 'exists', mock_t),\ + patch.dict(filestate.__opts__, {'test': True}): + expected = 'Link {0} target is set to be changed to {1}'.format(name, target) + changes = dict(change=name) + ret = return_val(result=None, comment=expected, name=name, changes=changes) + self.assertDictEqual(filestate.hardlink(name, target, + user=user, group=group), ret) + + # Test option -- force removal + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ + patch.object(os.path, 'exists', mock_t),\ + patch.dict(filestate.__opts__, {'test': True}): + expected = ( + 'The file or directory {0} is set for removal to ' + 'make way for a new hard link targeting {1}'.format(name, target) + ) + ret = return_val(result=None, comment=expected, name=name) + self.assertDictEqual(filestate.hardlink(name, target, force=True, + user=user, group=group), ret) + + # Test option -- without force removal + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ + patch.object(os.path, 'exists', mock_t),\ + patch.dict(filestate.__opts__, {'test': True}): + expected = ( + 'File or directory exists where the hard link {0} ' + 'should be. Did you mean to use force?'.format(name) + ) + ret = return_val(result=False, comment=expected, name=name) + self.assertDictEqual(filestate.hardlink(name, target, force=False, + user=user, group=group), ret) + + # Target is a directory + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}): + expected = 'Unable to hard link from directory {0}'.format(test_dir) + ret = return_val(comment=expected, name=name) + self.assertDictEqual(filestate.hardlink(name, test_dir, user=user, + group=group), ret) + + # Name is a directory + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}): + expected = 'Unable to hard link to directory {0}'.format(test_dir) + ret = return_val(comment=expected, name=test_dir) + self.assertDictEqual(filestate.hardlink(test_dir, target, user=user, + group=group), ret) + + # Try overwrite file with link + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ + patch.object(os.path, 'isfile', mock_t): + + expected = 'File exists where the hard link {0} should be'.format(name) + ret = return_val(comment=expected, name=name) + self.assertDictEqual(filestate.hardlink(name, target, user=user, + group=group), ret) + + # Try overwrite link with same + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_t}),\ + patch.dict(filestate.__salt__, {'file.stats': mock_stats}),\ + patch.object(os.path, 'isfile', mock_f): + + expected = ('Target of hard link {0} is already pointing ' + 'to {1}'.format(name, target)) + ret = return_val(result=True, comment=expected, name=name) + self.assertDictEqual(filestate.hardlink(name, target, user=user, + group=group), ret) + + # Really overwrite link with same + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_t}),\ + patch.dict(filestate.__salt__, {'file.link': mock_t}),\ + patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ + patch.object(os, 'remove', mock_t),\ + patch.object(os.path, 'isfile', mock_f): + + expected = 'Set target of hard link {0} -> {1}'.format(name, target) + changes = dict(new=name) + ret = return_val(result=True, comment=expected, name=name, changes=changes) + self.assertDictEqual(filestate.hardlink(name, target, user=user, + group=group), ret) + + # Fail at overwriting link with same + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_t}),\ + patch.dict(filestate.__salt__, {'file.link': mock_execerror}),\ + patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ + patch.object(os, 'remove', mock_t),\ + patch.object(os.path, 'isfile', mock_f): + + expected = ('Unable to set target of hard link {0} -> ' + '{1}: {2}'.format(name, target, '')) + ret = return_val(result=False, comment=expected, name=name) + self.assertDictEqual(filestate.hardlink(name, target, user=user, + group=group), ret) + + # Make new link + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ + patch.dict(filestate.__salt__, {'file.link': mock_f}),\ + patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ + patch.object(os, 'remove', mock_t),\ + patch.object(os.path, 'isfile', mock_f): + + expected = 'Created new hard link {0} -> {1}'.format(name, target) + changes = dict(new=name) + ret = return_val(result=True, comment=expected, name=name, changes=changes) + self.assertDictEqual(filestate.hardlink(name, target, user=user, + group=group), ret) + + # Fail while making new link + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ + patch.dict(filestate.__salt__, {'file.link': mock_execerror}),\ + patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ + patch.object(os, 'remove', mock_t),\ + patch.object(os.path, 'isfile', mock_f): + + expected = ('Unable to create new hard link {0} -> ' + '{1}: {2}'.format(name, target, '')) + ret = return_val(result=False, comment=expected, name=name) + self.assertDictEqual(filestate.hardlink(name, target, user=user, + group=group), ret) + + # Force making new link over file + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ + patch.dict(filestate.__salt__, {'file.link': mock_t}),\ + patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ + patch.object(os, 'remove', mock_t),\ + patch.object(os.path, 'isfile', mock_t): + + expected = 'Created new hard link {0} -> {1}'.format(name, target) + changes = dict(new=name) + changes['forced'] = 'File for hard link was forcibly replaced' + ret = return_val(result=True, comment=expected, name=name, changes=changes) + self.assertDictEqual(filestate.hardlink(name, target, user=user, + force=True, group=group), ret) + + # Force making new link over file but error out + with patch.dict(filestate.__salt__, patches),\ + patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ + patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ + patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ + patch.dict(filestate.__salt__, {'file.link': mock_execerror}),\ + patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ + patch.object(os, 'remove', mock_t),\ + patch.object(os.path, 'isfile', mock_t): + + expected = ('Unable to create new hard link {0} -> ' + '{1}: {2}'.format(name, target, '')) + changes = dict(forced='File for hard link was forcibly replaced') + ret = return_val(result=False, comment=expected, name=name, changes=changes) + self.assertDictEqual(filestate.hardlink(name, target, user=user, + force=True, group=group), ret) + # 'absent' function tests: 1 def test_absent(self): ''' @@ -766,8 +1067,8 @@ def test_managed(self): with patch.object(salt.utils.files, 'mkstemp', return_value=name): - comt = ('Unable to copy file {0} to {1}: ' - .format(name, name)) + comt = ('Unable to copy file {0} to {0}: ' + .format(name)) ret.update({'comment': comt, 'result': False}) self.assertDictEqual(filestate.managed (name, user=user, @@ -1524,7 +1825,7 @@ def test_rename(self): with patch.object(os.path, 'lexists', mock_lex): comt = ('The target file "{0}" exists and will not be ' 'overwritten'.format(name)) - ret.update({'comment': comt, 'result': False}) + ret.update({'comment': comt, 'result': True}) self.assertDictEqual(filestate.rename(name, source), ret) mock_lex = MagicMock(side_effect=[True, True, True]) @@ -2054,7 +2355,7 @@ def test__check_directory(self): # Run _check_directory function # Verify that it returns correctly # Delete tmp directory structure - root_tmp_dir = os.path.join(TMP, 'test__check_dir') + root_tmp_dir = os.path.join(RUNTIME_VARS.TMP, 'test__check_dir') expected_dir_mode = 0o777 depth = 3 try: diff --git a/tests/unit/states/test_gem.py b/tests/unit/states/test_gem.py index 5ae421466542..0f63a06f95f2 100644 --- a/tests/unit/states/test_gem.py +++ b/tests/unit/states/test_gem.py @@ -5,14 +5,13 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Late import so mock can do its job import salt.states.gem as gem -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestGemState(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/states/test_git.py b/tests/unit/states/test_git.py index d67a0317dd50..3248f4d58fcc 100644 --- a/tests/unit/states/test_git.py +++ b/tests/unit/states/test_git.py @@ -11,14 +11,12 @@ # Import Salt Testing Libs from tests.support.helpers import with_tempdir from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( Mock, MagicMock, patch, DEFAULT, - NO_MOCK, - NO_MOCK_REASON, ) # Import Salt Libs @@ -27,7 +25,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) class GitTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.git diff --git a/tests/unit/states/test_glusterfs.py b/tests/unit/states/test_glusterfs.py index c1a91ca61d17..795847e9f66e 100644 --- a/tests/unit/states/test_glusterfs.py +++ b/tests/unit/states/test_glusterfs.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -21,7 +19,6 @@ import salt.modules.glusterfs as mod_glusterfs -@skipIf(NO_MOCK, NO_MOCK_REASON) class GlusterfsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.glusterfs diff --git a/tests/unit/states/test_gnomedesktop.py b/tests/unit/states/test_gnomedesktop.py index 2e270a8ab343..912ed99e4dbc 100644 --- a/tests/unit/states/test_gnomedesktop.py +++ b/tests/unit/states/test_gnomedesktop.py @@ -6,16 +6,12 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON) +from tests.support.unit import TestCase # Import Salt Libs import salt.states.gnomedesktop as gnomedesktop -@skipIf(NO_MOCK, NO_MOCK_REASON) class GnomedesktopTestCase(TestCase): ''' Test cases for salt.states.gnomedesktop diff --git a/tests/unit/states/test_grafana.py b/tests/unit/states/test_grafana.py index e39cd7823d29..83b9a7791d97 100644 --- a/tests/unit/states/test_grafana.py +++ b/tests/unit/states/test_grafana.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -21,7 +19,6 @@ from salt.exceptions import SaltInvocationError -@skipIf(NO_MOCK, NO_MOCK_REASON) class GrafanaTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.grafana diff --git a/tests/unit/states/test_grafana_datasource.py b/tests/unit/states/test_grafana_datasource.py index bd146c7269a4..aef1f8a4a330 100644 --- a/tests/unit/states/test_grafana_datasource.py +++ b/tests/unit/states/test_grafana_datasource.py @@ -4,10 +4,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, Mock, MagicMock, patch @@ -28,7 +26,6 @@ def mock_json_response(data): return Mock(return_value=response) -@skipIf(NO_MOCK, NO_MOCK_REASON) class GrafanaDatasourceTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return {grafana_datasource: {}} diff --git a/tests/unit/states/test_grains.py b/tests/unit/states/test_grains.py index 15aac46adbe6..65f6cc6df689 100644 --- a/tests/unit/states/test_grains.py +++ b/tests/unit/states/test_grains.py @@ -10,10 +10,10 @@ import contextlib # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.utils.files @@ -24,18 +24,17 @@ from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) class GrainsTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): grains_test_dir = '__salt_test_state_grains' - if not os.path.exists(os.path.join(TMP, grains_test_dir)): - os.makedirs(os.path.join(TMP, grains_test_dir)) + if not os.path.exists(os.path.join(RUNTIME_VARS.TMP, grains_test_dir)): + os.makedirs(os.path.join(RUNTIME_VARS.TMP, grains_test_dir)) loader_globals = { '__opts__': { 'test': False, - 'conf_file': os.path.join(TMP, grains_test_dir, 'minion'), - 'cachedir': os.path.join(TMP, grains_test_dir), + 'conf_file': os.path.join(RUNTIME_VARS.TMP, grains_test_dir, 'minion'), + 'cachedir': os.path.join(RUNTIME_VARS.TMP, grains_test_dir), 'local': True, }, '__salt__': { @@ -100,6 +99,13 @@ def test_exists_found(self): self.assertEqual(ret['comment'], 'Grain exists') self.assertEqual(ret['changes'], {}) + # 'make_hashable' function tests: 1 + + def test_make_hashable(self): + with self.setGrains({'cmplx_lst_grain': [{'a': 'aval'}, {'foo': 'bar'}]}): + hashable_list = {'cmplx_lst_grain': [{'a': 'aval'}, {'foo': 'bar'}]} + self.assertEqual(grains.make_hashable(grains.__grains__).issubset(grains.make_hashable(hashable_list)), True) + # 'present' function tests: 12 def test_present_add(self): diff --git a/tests/unit/states/test_group.py b/tests/unit/states/test_group.py index b91cc5e5b50e..5dba7c51a1a2 100644 --- a/tests/unit/states/test_group.py +++ b/tests/unit/states/test_group.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ from salt.utils.odict import OrderedDict -@skipIf(NO_MOCK, NO_MOCK_REASON) class GroupTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the group state diff --git a/tests/unit/states/test_hg.py b/tests/unit/states/test_hg.py index 0eb00d4d8d2f..d1b5efe302a5 100644 --- a/tests/unit/states/test_hg.py +++ b/tests/unit/states/test_hg.py @@ -11,16 +11,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class HgTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the svn state diff --git a/tests/unit/states/test_hipchat.py b/tests/unit/states/test_hipchat.py deleted file mode 100644 index 722433a4af99..000000000000 --- a/tests/unit/states/test_hipchat.py +++ /dev/null @@ -1,74 +0,0 @@ -# -*- coding: utf-8 -*- -''' - :codeauthor: Jayesh Kariya -''' -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, - MagicMock, - patch) - -# Import Salt Libs -import salt.states.hipchat as hipchat - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -class HipchatTestCase(TestCase, LoaderModuleMockMixin): - ''' - Test cases for salt.states.hipchat - ''' - def setup_loader_modules(self): - return {hipchat: {}} - - # 'send_message' function tests: 1 - - def test_send_message(self): - ''' - Test to send a message to a Hipchat room. - ''' - name = 'salt' - room_id = '123456' - from_name = 'SuperAdmin' - message = 'This state was executed successfully.' - - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} - - with patch.dict(hipchat.__opts__, {'test': True}): - comt = ('The following message is to be sent to Hipchat: {0}' - .format(message)) - ret.update({'comment': comt}) - self.assertDictEqual(hipchat.send_message(name, room_id, from_name, - message), ret) - - with patch.dict(hipchat.__opts__, {'test': False}): - comt = ('Hipchat room id is missing: {0}'.format(name)) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(hipchat.send_message(name, None, from_name, - message), ret) - - comt = ('Hipchat from name is missing: {0}'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(hipchat.send_message(name, room_id, None, - message), ret) - - comt = ('Hipchat message is missing: {0}'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(hipchat.send_message(name, room_id, from_name, - None), ret) - - mock = MagicMock(return_value=True) - with patch.dict(hipchat.__salt__, {'hipchat.send_message': mock}): - comt = ('Sent message: {0}'.format(name)) - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(hipchat.send_message(name, room_id, - from_name, message), - ret) diff --git a/tests/unit/states/test_host.py b/tests/unit/states/test_host.py index 645a3d8a75f9..dc114d2a9de2 100644 --- a/tests/unit/states/test_host.py +++ b/tests/unit/states/test_host.py @@ -10,17 +10,14 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, call, patch, ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class HostTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the host state diff --git a/tests/unit/states/test_http.py b/tests/unit/states/test_http.py index 68dcc971744c..82050bfdf236 100644 --- a/tests/unit/states/test_http.py +++ b/tests/unit/states/test_http.py @@ -10,16 +10,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class HttpTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the HTTP state diff --git a/tests/unit/states/test_incron.py b/tests/unit/states/test_incron.py index 4fc49836032a..d6dcba00c804 100644 --- a/tests/unit/states/test_incron.py +++ b/tests/unit/states/test_incron.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.incron as incron -@skipIf(NO_MOCK, NO_MOCK_REASON) class IncronTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.incron diff --git a/tests/unit/states/test_influxdb08_database.py b/tests/unit/states/test_influxdb08_database.py index 21ee2472dd81..e2f8fa168bae 100644 --- a/tests/unit/states/test_influxdb08_database.py +++ b/tests/unit/states/test_influxdb08_database.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.influxdb08_database as influxdb08_database -@skipIf(NO_MOCK, NO_MOCK_REASON) class InfluxdbDatabaseTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.influxdb08_database diff --git a/tests/unit/states/test_influxdb08_user.py b/tests/unit/states/test_influxdb08_user.py index ebc596c16c38..930f037ea182 100644 --- a/tests/unit/states/test_influxdb08_user.py +++ b/tests/unit/states/test_influxdb08_user.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.influxdb08_user as influxdb08_user -@skipIf(NO_MOCK, NO_MOCK_REASON) class InfluxdbUserTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.influxdb08_user diff --git a/tests/unit/states/test_ini_manage.py b/tests/unit/states/test_ini_manage.py index bbfa4cbc7f27..e0707f871b49 100644 --- a/tests/unit/states/test_ini_manage.py +++ b/tests/unit/states/test_ini_manage.py @@ -7,11 +7,9 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch) # Import Salt Libs @@ -20,7 +18,6 @@ # pylint: disable=no-member -@skipIf(NO_MOCK, NO_MOCK_REASON) class IniManageTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ini_manage diff --git a/tests/unit/states/test_ipmi.py b/tests/unit/states/test_ipmi.py index 2b4047d8c36f..f7a929b32e79 100644 --- a/tests/unit/states/test_ipmi.py +++ b/tests/unit/states/test_ipmi.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.ipmi as ipmi -@skipIf(NO_MOCK, NO_MOCK_REASON) class IpmiTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ipmi diff --git a/tests/unit/states/test_ipset.py b/tests/unit/states/test_ipset.py index 0a549db3762b..5c33bc8f4175 100644 --- a/tests/unit/states/test_ipset.py +++ b/tests/unit/states/test_ipset.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, call, patch) @@ -19,7 +17,6 @@ import salt.states.ipset as ipset -@skipIf(NO_MOCK, NO_MOCK_REASON) class IpsetSetPresentTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ipset.present @@ -82,7 +79,6 @@ def test_create_fails(self): self._runner(ret, new_set='') -@skipIf(NO_MOCK, NO_MOCK_REASON) class IpsetSetAbsentTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ipset.present @@ -144,7 +140,6 @@ def test_remove_success(self): self._runner(ret, delete_set=True, flush_assertion=True, delete_set_assertion=True) -@skipIf(NO_MOCK, NO_MOCK_REASON) class IpsetPresentTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ipset.present @@ -221,7 +216,6 @@ def test_missing_entry(self): self.assertDictEqual(ipset.present(self.fake_name), ret) -@skipIf(NO_MOCK, NO_MOCK_REASON) class IpsetAbsentTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ipset.present @@ -296,7 +290,6 @@ def test_absent(self): self.assertDictEqual(ipset.absent(self.fake_name), ret) -@skipIf(NO_MOCK, NO_MOCK_REASON) class IpsetFlushTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ipset.present diff --git a/tests/unit/states/test_iptables.py b/tests/unit/states/test_iptables.py index ff96e29d1d66..90afa1ea3ea0 100644 --- a/tests/unit/states/test_iptables.py +++ b/tests/unit/states/test_iptables.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.iptables as iptables -@skipIf(NO_MOCK, NO_MOCK_REASON) class IptablesTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the iptables state diff --git a/tests/unit/states/test_jboss7.py b/tests/unit/states/test_jboss7.py index 8f1913633f07..d71915dae1e5 100644 --- a/tests/unit/states/test_jboss7.py +++ b/tests/unit/states/test_jboss7.py @@ -6,8 +6,8 @@ # Import Salt testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import Salt libs import salt.states.jboss7 as jboss7 @@ -15,7 +15,6 @@ from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) class JBoss7StateTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/states/test_kernelpkg.py b/tests/unit/states/test_kernelpkg.py index 4a81aacbf47c..12cdf32ff918 100644 --- a/tests/unit/states/test_kernelpkg.py +++ b/tests/unit/states/test_kernelpkg.py @@ -18,8 +18,6 @@ from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -33,7 +31,6 @@ STATE_NAME = 'kernelpkg-test' -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_MODULES, 'Salt modules could not be loaded') class KernelPkgTestCase(TestCase, LoaderModuleMockMixin): ''' diff --git a/tests/unit/states/test_keyboard.py b/tests/unit/states/test_keyboard.py index c1d171d02c78..c015ec00d864 100644 --- a/tests/unit/states/test_keyboard.py +++ b/tests/unit/states/test_keyboard.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.keyboard as keyboard -@skipIf(NO_MOCK, NO_MOCK_REASON) class KeyboardTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.keyboard diff --git a/tests/unit/states/test_keystone.py b/tests/unit/states/test_keystone.py index 81b0b3edf064..0161fa7838d6 100644 --- a/tests/unit/states/test_keystone.py +++ b/tests/unit/states/test_keystone.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.keystone as keystone -@skipIf(NO_MOCK, NO_MOCK_REASON) class KeystoneTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.keystone diff --git a/tests/unit/states/test_keystore.py b/tests/unit/states/test_keystore.py new file mode 100644 index 000000000000..34d35f08bb2e --- /dev/null +++ b/tests/unit/states/test_keystore.py @@ -0,0 +1,313 @@ +# -*- coding: utf-8 -*- +''' +Test cases for keystore state +''' +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch + +# Import Salt Libs +import salt.states.keystore as keystore + + +class KeystoreTestCase(TestCase, LoaderModuleMockMixin): + ''' + Test cases for salt.states.keystore + ''' + def setup_loader_modules(self): + return {keystore: {'__opts__': {'test': False}}} + + @patch('os.path.exists', MagicMock(return_value=True)) + def test_cert_already_present(self): + ''' + Test for existing value_present + ''' + + cert_return = [{ + "valid_until": "August 21 2017", + "sha1": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D5", + "valid_start": "August 22 2012", + "type": "TrustedCertEntry", + "alias": "stringhost", + "expired": True + }] + x509_return = { + "Not After": "2017-08-21 05:26:54", + "Subject Hash": "97:95:14:4F", + "Serial Number": "0D:FA", + "SHA1 Finger Print": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D5", + "SHA-256 Finger Print": "5F:0F:B5:16:65:81:AA:E6:4A:10:1C:15:83:B1:BE:BE:74:E8:14:A9:1E:7A:8A:14:BA:1E:83:5D:78:F6:E9:E7", + "MD5 Finger Print": "80:E6:17:AF:78:D8:E4:B8:FB:5F:41:3A:27:1D:CC:F2", + "Version": 1, + "Key Size": 512, + "Public Key": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJv8ZpB5hEK7qxP9K3v43hUS5fGT4waK\ne7ix4Z4mu5UBv+cw7WSFAt0Vaag0sAbsPzU8Hhsrj/qPABvfB8asUwcCAwEAAQ==\n-----END PUBLIC KEY-----\n", + "Issuer": { + "C": "JP", + "organizationName": "Frank4DD", + "CN": "Frank4DD Web CA", + "SP": "Tokyo", + "L": "Chuo-ku", + "emailAddress": "support@frank4dd.com", + "OU": "WebCert Support" + }, + "Issuer Hash": "92:DA:45:6B", + "Not Before": "2012-08-22 05:26:54", + "Subject": { + "C": "JP", + "SP": "Tokyo", + "organizationName": "Frank4DD", + "CN": "www.example.com" + } + } + + name = 'keystore.jks' + passphrase = "changeit" + entries = [{'alias': 'stringhost', + 'certificate': '''-----BEGIN CERTIFICATE----- + MIICEjCCAXsCAg36MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG + A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE + MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl + YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw + ODIyMDUyNjU0WhcNMTcwODIxMDUyNjU0WjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE + CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs + ZS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEAm/xmkHmEQrurE/0re/jeFRLl + 8ZPjBop7uLHhnia7lQG/5zDtZIUC3RVpqDSwBuw/NTweGyuP+o8AG98HxqxTBwID + AQABMA0GCSqGSIb3DQEBBQUAA4GBABS2TLuBeTPmcaTaUW/LCB2NYOy8GMdzR1mx + 8iBIu2H6/E2tiY3RIevV2OW61qY2/XRQg7YPxx3ffeUugX9F4J/iPnnu1zAxxyBy + 2VguKv4SWjRFoRkIfIlHX0qVviMhSlNy2ioFLy7JcPZb+v3ftDGywUqcBiVDoea0 + Hn+GmxZA\n-----END CERTIFICATE-----'''} + ] + + state_return = { + 'name': name, + 'changes': {}, + 'result': True, + 'comment': 'No changes made.\n' + } + + #with patch.dict(keystore.__opts__, {'test': False}): + with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): + with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): + self.assertDictEqual(keystore.managed(name, passphrase, entries), state_return) + + with patch.dict(keystore.__opts__, {'test': True}): + with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): + with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): + self.assertDictEqual(keystore.managed(name, passphrase, entries), state_return) + + @patch('os.path.exists', MagicMock(return_value=True)) + def test_cert_update(self): + ''' + Test for existing value_present + ''' + + cert_return = [{ + "valid_until": "August 21 2017", + "sha1": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D5", + "valid_start": "August 22 2012", + "type": "TrustedCertEntry", + "alias": "stringhost", + "expired": True + }] + x509_return = { + "Not After": "2017-08-21 05:26:54", + "Subject Hash": "97:95:14:4F", + "Serial Number": "0D:FA", + "SHA1 Finger Print": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D6", + "SHA-256 Finger Print": "5F:0F:B5:16:65:81:AA:E6:4A:10:1C:15:83:B1:BE:BE:74:E8:14:A9:1E:7A:8A:14:BA:1E:83:5D:78:F6:E9:E7", + "MD5 Finger Print": "80:E6:17:AF:78:D8:E4:B8:FB:5F:41:3A:27:1D:CC:F2", + "Version": 1, + "Key Size": 512, + "Public Key": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJv8ZpB5hEK7qxP9K3v43hUS5fGT4waK\ne7ix4Z4mu5UBv+cw7WSFAt0Vaag0sAbsPzU8Hhsrj/qPABvfB8asUwcCAwEAAQ==\n-----END PUBLIC KEY-----\n", + "Issuer": { + "C": "JP", + "organizationName": "Frank4DD", + "CN": "Frank4DD Web CA", + "SP": "Tokyo", + "L": "Chuo-ku", + "emailAddress": "support@frank4dd.com", + "OU": "WebCert Support" + }, + "Issuer Hash": "92:DA:45:6B", + "Not Before": "2012-08-22 05:26:54", + "Subject": { + "C": "JP", + "SP": "Tokyo", + "organizationName": "Frank4DD", + "CN": "www.example.com" + } + } + + name = 'keystore.jks' + passphrase = "changeit" + entries = [{'alias': 'stringhost', + 'certificate': '''-----BEGIN CERTIFICATE----- + MIICEjCCAXsCAg36MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG + A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE + MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl + YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw + ODIyMDUyNjU0WhcNMTcwODIxMDUyNjU0WjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE + CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs + ZS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEAm/xmkHmEQrurE/0re/jeFRLl + 8ZPjBop7uLHhnia7lQG/5zDtZIUC3RVpqDSwBuw/NTweGyuP+o8AG98HxqxTBwID + AQABMA0GCSqGSIb3DQEBBQUAA4GBABS2TLuBeTPmcaTaUW/LCB2NYOy8GMdzR1mx + 8iBIu2H6/E2tiY3RIevV2OW61qY2/XRQg7YPxx3ffeUugX9F4J/iPnnu1zAxxyBy + 2VguKv4SWjRFoRkIfIlHX0qVviMhSlNy2ioFLy7JcPZb+v3ftDGywUqcBiVDoea0 + Hn+GmxZA\n-----END CERTIFICATE-----'''} + ] + + test_return = { + 'name': name, + 'changes': {}, + 'result': None, + 'comment': 'Alias stringhost would have been updated\n' + } + state_return = { + 'name': name, + 'changes': {'stringhost': 'Updated'}, + 'result': True, + 'comment': 'Alias stringhost updated.\n' + } + + with patch.dict(keystore.__opts__, {'test': True}): + with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): + with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): + self.assertDictEqual(keystore.managed(name, passphrase, entries), test_return) + + with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): + with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): + with patch.dict(keystore.__salt__, {'keystore.remove': MagicMock(return_value=True)}): + with patch.dict(keystore.__salt__, {'keystore.add': MagicMock(return_value=True)}): + self.assertDictEqual(keystore.managed(name, passphrase, entries), state_return) + + @patch('os.path.exists', MagicMock(return_value=False)) + def test_new_file(self): + ''' + Test for existing value_present + ''' + name = 'keystore.jks' + passphrase = "changeit" + entries = [{'alias': 'stringhost', + 'certificate': '''-----BEGIN CERTIFICATE----- + MIICEjCCAXsCAg36MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG + A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE + MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl + YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw + ODIyMDUyNjU0WhcNMTcwODIxMDUyNjU0WjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE + CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs + ZS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEAm/xmkHmEQrurE/0re/jeFRLl + 8ZPjBop7uLHhnia7lQG/5zDtZIUC3RVpqDSwBuw/NTweGyuP+o8AG98HxqxTBwID + AQABMA0GCSqGSIb3DQEBBQUAA4GBABS2TLuBeTPmcaTaUW/LCB2NYOy8GMdzR1mx + 8iBIu2H6/E2tiY3RIevV2OW61qY2/XRQg7YPxx3ffeUugX9F4J/iPnnu1zAxxyBy + 2VguKv4SWjRFoRkIfIlHX0qVviMhSlNy2ioFLy7JcPZb+v3ftDGywUqcBiVDoea0 + Hn+GmxZA\n-----END CERTIFICATE-----'''} + ] + + test_return = { + 'name': name, + 'changes': {}, + 'result': None, + 'comment': 'Alias stringhost would have been added\n' + } + state_return = { + 'name': name, + 'changes': {'stringhost': 'Added'}, + 'result': True, + 'comment': 'Alias stringhost added.\n' + } + + with patch.dict(keystore.__opts__, {'test': True}): + self.assertDictEqual(keystore.managed(name, passphrase, entries), test_return) + + with patch.dict(keystore.__salt__, {'keystore.remove': MagicMock(return_value=True)}): + with patch.dict(keystore.__salt__, {'keystore.add': MagicMock(return_value=True)}): + self.assertDictEqual(keystore.managed(name, passphrase, entries), state_return) + + @patch('os.path.exists', MagicMock(return_value=True)) + def test_force_remove(self): + ''' + Test for existing value_present + ''' + + cert_return = [{ + "valid_until": "August 21 2017", + "sha1": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D5", + "valid_start": "August 22 2012", + "type": "TrustedCertEntry", + "alias": "oldhost", + "expired": True + }] + x509_return = { + "Not After": "2017-08-21 05:26:54", + "Subject Hash": "97:95:14:4F", + "Serial Number": "0D:FA", + "SHA1 Finger Print": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D6", + "SHA-256 Finger Print": "5F:0F:B5:16:65:81:AA:E6:4A:10:1C:15:83:B1:BE:BE:74:E8:14:A9:1E:7A:8A:14:BA:1E:83:5D:78:F6:E9:E7", + "MD5 Finger Print": "80:E6:17:AF:78:D8:E4:B8:FB:5F:41:3A:27:1D:CC:F2", + "Version": 1, + "Key Size": 512, + "Public Key": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJv8ZpB5hEK7qxP9K3v43hUS5fGT4waK\ne7ix4Z4mu5UBv+cw7WSFAt0Vaag0sAbsPzU8Hhsrj/qPABvfB8asUwcCAwEAAQ==\n-----END PUBLIC KEY-----\n", + "Issuer": { + "C": "JP", + "organizationName": "Frank4DD", + "CN": "Frank4DD Web CA", + "SP": "Tokyo", + "L": "Chuo-ku", + "emailAddress": "support@frank4dd.com", + "OU": "WebCert Support" + }, + "Issuer Hash": "92:DA:45:6B", + "Not Before": "2012-08-22 05:26:54", + "Subject": { + "C": "JP", + "SP": "Tokyo", + "organizationName": "Frank4DD", + "CN": "www.example.com" + } + } + + name = 'keystore.jks' + passphrase = "changeit" + entries = [{'alias': 'stringhost', + 'certificate': '''-----BEGIN CERTIFICATE----- + MIICEjCCAXsCAg36MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG + A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE + MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl + YiBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBmcmFuazRkZC5jb20wHhcNMTIw + ODIyMDUyNjU0WhcNMTcwODIxMDUyNjU0WjBKMQswCQYDVQQGEwJKUDEOMAwGA1UE + CAwFVG9reW8xETAPBgNVBAoMCEZyYW5rNEREMRgwFgYDVQQDDA93d3cuZXhhbXBs + ZS5jb20wXDANBgkqhkiG9w0BAQEFAANLADBIAkEAm/xmkHmEQrurE/0re/jeFRLl + 8ZPjBop7uLHhnia7lQG/5zDtZIUC3RVpqDSwBuw/NTweGyuP+o8AG98HxqxTBwID + AQABMA0GCSqGSIb3DQEBBQUAA4GBABS2TLuBeTPmcaTaUW/LCB2NYOy8GMdzR1mx + 8iBIu2H6/E2tiY3RIevV2OW61qY2/XRQg7YPxx3ffeUugX9F4J/iPnnu1zAxxyBy + 2VguKv4SWjRFoRkIfIlHX0qVviMhSlNy2ioFLy7JcPZb+v3ftDGywUqcBiVDoea0 + Hn+GmxZA\n-----END CERTIFICATE-----'''} + ] + + test_return = { + 'name': name, + 'changes': {}, + 'result': None, + 'comment': 'Alias stringhost would have been updated\nAlias oldhost would have been removed' + } + state_return = { + 'name': name, + 'changes': {'oldhost': 'Removed', 'stringhost': 'Updated'}, + 'result': True, + 'comment': 'Alias stringhost updated.\nAlias oldhost removed.\n' + } + + with patch.dict(keystore.__opts__, {'test': True}): + with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): + with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): + self.assertDictEqual(keystore.managed(name, passphrase, entries, force_remove=True), test_return) + + with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): + with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): + with patch.dict(keystore.__salt__, {'keystore.remove': MagicMock(return_value=True)}): + with patch.dict(keystore.__salt__, {'keystore.add': MagicMock(return_value=True)}): + self.assertDictEqual(keystore.managed(name, passphrase, entries, force_remove=True), state_return) diff --git a/tests/unit/states/test_kmod.py b/tests/unit/states/test_kmod.py index 6144e79ff41f..a820d0cdfa36 100644 --- a/tests/unit/states/test_kmod.py +++ b/tests/unit/states/test_kmod.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.kmod as kmod -@skipIf(NO_MOCK, NO_MOCK_REASON) class KmodTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.kmod diff --git a/tests/unit/states/test_kubernetes.py b/tests/unit/states/test_kubernetes.py index fd5cd3e12ac6..670f49753208 100644 --- a/tests/unit/states/test_kubernetes.py +++ b/tests/unit/states/test_kubernetes.py @@ -13,8 +13,6 @@ from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -24,7 +22,6 @@ from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(kubernetes is False, "Probably Kubernetes client lib is not installed. \ Skipping test_kubernetes.py") class KubernetesTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/states/test_layman.py b/tests/unit/states/test_layman.py index 5e446e9c36ec..b738e02cf080 100644 --- a/tests/unit/states/test_layman.py +++ b/tests/unit/states/test_layman.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.layman as layman -@skipIf(NO_MOCK, NO_MOCK_REASON) class LaymanTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.layman diff --git a/tests/unit/states/test_ldap.py b/tests/unit/states/test_ldap.py index e3ceb81f6054..b8066ba76c0e 100644 --- a/tests/unit/states/test_ldap.py +++ b/tests/unit/states/test_ldap.py @@ -17,8 +17,7 @@ from salt.utils.oset import OrderedSet from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase # emulates the LDAP database. each key is the DN of an entry and it # maps to a dict which maps attribute names to sets of values. @@ -164,7 +163,6 @@ def _dump_db(d=None): for dn in d)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class LDAPTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/states/test_libcloud_dns.py b/tests/unit/states/test_libcloud_dns.py index 9ff794d4d5f1..108d1d274fb6 100644 --- a/tests/unit/states/test_libcloud_dns.py +++ b/tests/unit/states/test_libcloud_dns.py @@ -8,11 +8,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON -) +from tests.support.unit import TestCase import salt.states.libcloud_dns as libcloud_dns from salt.modules.libcloud_dns import _simple_record, _simple_zone @@ -37,7 +33,6 @@ def __init__(self, id, name, record_type, data): self.extra = {} -@skipIf(NO_MOCK, NO_MOCK_REASON) class LibcloudDnsModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/states/test_linux_acl.py b/tests/unit/states/test_linux_acl.py index 54f359983c2e..a86706b0fa45 100644 --- a/tests/unit/states/test_linux_acl.py +++ b/tests/unit/states/test_linux_acl.py @@ -10,8 +10,6 @@ from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -21,7 +19,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not sys.platform.startswith('linux'), 'Test for Linux only') class LinuxAclTestCase(TestCase, LoaderModuleMockMixin): ''' diff --git a/tests/unit/states/test_locale.py b/tests/unit/states/test_locale.py index 765cf9414d8a..ec1d3332b82a 100644 --- a/tests/unit/states/test_locale.py +++ b/tests/unit/states/test_locale.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.locale as locale -@skipIf(NO_MOCK, NO_MOCK_REASON) class LocaleTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the locale state diff --git a/tests/unit/states/test_loop.py b/tests/unit/states/test_loop.py new file mode 100644 index 000000000000..185a7d56c4cc --- /dev/null +++ b/tests/unit/states/test_loop.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +''' +Tests for loop state(s) +''' + +# Import Python Libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase +from tests.support.mock import MagicMock + +# Import Salt Libs +import salt.states.loop as loop + + +class LoopTestCase(TestCase, LoaderModuleMockMixin): + + mock = MagicMock(return_value=True) + func = 'foo.bar' + m_args = ['foo', 'bar', 'baz'] + m_kwargs = {'hello': 'world'} + condition = 'm_ret is True' + period = 1 + timeout = 3 + + def setup_loader_modules(self): + return { + loop: { + '__opts__': {'test': False}, + '__salt__': {self.func: self.mock}, + } + } + + def setUp(self): + self.mock.reset_mock() + + def test_until(self): + ret = loop.until( + name=self.func, + m_args=self.m_args, + m_kwargs=self.m_kwargs, + condition=self.condition, + period=self.period, + timeout=self.timeout) + assert ret['result'] is True + self.mock.assert_called_once_with(*self.m_args, **self.m_kwargs) + + def test_until_without_args(self): + ret = loop.until( + name=self.func, + m_kwargs=self.m_kwargs, + condition=self.condition, + period=self.period, + timeout=self.timeout) + assert ret['result'] is True + self.mock.assert_called_once_with(**self.m_kwargs) + + def test_until_without_kwargs(self): + ret = loop.until( + name=self.func, + m_args=self.m_args, + condition=self.condition, + period=self.period, + timeout=self.timeout) + assert ret['result'] is True + self.mock.assert_called_once_with(*self.m_args) + + def test_until_without_args_or_kwargs(self): + ret = loop.until( + name=self.func, + condition=self.condition, + period=self.period, + timeout=self.timeout) + assert ret['result'] is True + self.mock.assert_called_once_with() diff --git a/tests/unit/states/test_lvm.py b/tests/unit/states/test_lvm.py index 1e1e687ff409..2b99717925dd 100644 --- a/tests/unit/states/test_lvm.py +++ b/tests/unit/states/test_lvm.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.lvm as lvm -@skipIf(NO_MOCK, NO_MOCK_REASON) class LvmTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.lvm diff --git a/tests/unit/states/test_lvs_server.py b/tests/unit/states/test_lvs_server.py index 1707acc7aa1a..402ec90dd752 100644 --- a/tests/unit/states/test_lvs_server.py +++ b/tests/unit/states/test_lvs_server.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.lvs_server as lvs_server -@skipIf(NO_MOCK, NO_MOCK_REASON) class LvsServerTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.lvs_server diff --git a/tests/unit/states/test_lvs_service.py b/tests/unit/states/test_lvs_service.py index fb251dac2b09..9efb46ef5352 100644 --- a/tests/unit/states/test_lvs_service.py +++ b/tests/unit/states/test_lvs_service.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.lvs_service as lvs_service -@skipIf(NO_MOCK, NO_MOCK_REASON) class LvsServiceTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.lvs_service diff --git a/tests/unit/states/test_lxc.py b/tests/unit/states/test_lxc.py index 2b0a8e2c1015..372ded7bce3e 100644 --- a/tests/unit/states/test_lxc.py +++ b/tests/unit/states/test_lxc.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -19,7 +17,6 @@ import salt.utils.versions -@skipIf(NO_MOCK, NO_MOCK_REASON) class LxcTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.lxc diff --git a/tests/unit/states/test_makeconf.py b/tests/unit/states/test_makeconf.py index 76d28528d83a..530207eb3f08 100644 --- a/tests/unit/states/test_makeconf.py +++ b/tests/unit/states/test_makeconf.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.makeconf as makeconf -@skipIf(NO_MOCK, NO_MOCK_REASON) class MakeconfTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.makeconf diff --git a/tests/unit/states/test_mdadm_raid.py b/tests/unit/states/test_mdadm_raid.py index 70b94b127d10..f742c932f747 100644 --- a/tests/unit/states/test_mdadm_raid.py +++ b/tests/unit/states/test_mdadm_raid.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.mdadm_raid as mdadm -@skipIf(NO_MOCK, NO_MOCK_REASON) class MdadmTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the mdadm state diff --git a/tests/unit/states/test_memcached.py b/tests/unit/states/test_memcached.py index cb69f9ee15a8..d67402915ab2 100644 --- a/tests/unit/states/test_memcached.py +++ b/tests/unit/states/test_memcached.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -20,7 +18,6 @@ import salt.states.memcached as memcached -@skipIf(NO_MOCK, NO_MOCK_REASON) class MemcachedTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.memcached diff --git a/tests/unit/states/test_modjk.py b/tests/unit/states/test_modjk.py index ce7c379dd769..5d063c99c6b3 100644 --- a/tests/unit/states/test_modjk.py +++ b/tests/unit/states/test_modjk.py @@ -6,10 +6,7 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON) +from tests.support.unit import TestCase # Import Salt Libs import salt.states.modjk as modjk @@ -22,7 +19,6 @@ LIST_NOT_STR = "workers should be a list not a " -@skipIf(NO_MOCK, NO_MOCK_REASON) class ModjkTestCase(TestCase): ''' Test cases for salt.states.modjk diff --git a/tests/unit/states/test_modjk_worker.py b/tests/unit/states/test_modjk_worker.py index 5ac1c6a8e32c..aacc806cfd1f 100644 --- a/tests/unit/states/test_modjk_worker.py +++ b/tests/unit/states/test_modjk_worker.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.modjk_worker as modjk_worker -@skipIf(NO_MOCK, NO_MOCK_REASON) class ModjkWorkerTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.modjk_worker diff --git a/tests/unit/states/test_module.py b/tests/unit/states/test_module.py index b505e85b90c0..0b7f51a29756 100644 --- a/tests/unit/states/test_module.py +++ b/tests/unit/states/test_module.py @@ -13,10 +13,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -107,7 +105,6 @@ def _mocked_none_return(ret=None): return ret -@skipIf(NO_MOCK, NO_MOCK_REASON) class ModuleStateTest(TestCase, LoaderModuleMockMixin): ''' Tests module state (salt/states/module.py) diff --git a/tests/unit/states/test_mongodb_database.py b/tests/unit/states/test_mongodb_database.py index 5d64ba6a686a..cf313bc4d435 100644 --- a/tests/unit/states/test_mongodb_database.py +++ b/tests/unit/states/test_mongodb_database.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.mongodb_database as mongodb_database -@skipIf(NO_MOCK, NO_MOCK_REASON) class MongodbDatabaseTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.mongodb_database diff --git a/tests/unit/states/test_mongodb_user.py b/tests/unit/states/test_mongodb_user.py index 12f9b249ad11..a0e76cdbcc44 100644 --- a/tests/unit/states/test_mongodb_user.py +++ b/tests/unit/states/test_mongodb_user.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.mongodb_user as mongodb_user -@skipIf(NO_MOCK, NO_MOCK_REASON) class MongodbUserTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.mongodb_user diff --git a/tests/unit/states/test_mount.py b/tests/unit/states/test_mount.py index 3e3a75d3cd8b..5d57d62cca44 100644 --- a/tests/unit/states/test_mount.py +++ b/tests/unit/states/test_mount.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -19,7 +17,6 @@ import salt.states.mount as mount -@skipIf(NO_MOCK, NO_MOCK_REASON) class MountTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.mount @@ -449,3 +446,596 @@ def test_mod_watch(self): 'changes': {}} self.assertDictEqual(mount.mod_watch(name, sfun='unmount'), ret) + + def test__convert_to_fast_none(self): + ''' + Test the device name conversor + ''' + assert mount._convert_to('/dev/sda1', None) == '/dev/sda1' + + def test__convert_to_fast_device(self): + ''' + Test the device name conversor + ''' + assert mount._convert_to('/dev/sda1', 'device') == '/dev/sda1' + + def test__convert_to_fast_token(self): + ''' + Test the device name conversor + ''' + assert mount._convert_to('LABEL=home', 'label') == 'LABEL=home' + + def test__convert_to_device_none(self): + ''' + Test the device name conversor + ''' + salt_mock = { + 'disk.blkid': MagicMock(return_value={}), + } + with patch.dict(mount.__salt__, salt_mock): + assert mount._convert_to('/dev/sda1', 'uuid') is None + salt_mock['disk.blkid'].assert_called_with('/dev/sda1') + + def test__convert_to_device_token(self): + ''' + Test the device name conversor + ''' + uuid = '988c663d-74a2-432b-ba52-3eea34015f22' + salt_mock = { + 'disk.blkid': MagicMock(return_value={ + '/dev/sda1': {'UUID': uuid} + }), + } + with patch.dict(mount.__salt__, salt_mock): + uuid = 'UUID={}'.format(uuid) + assert mount._convert_to('/dev/sda1', 'uuid') == uuid + salt_mock['disk.blkid'].assert_called_with('/dev/sda1') + + def test__convert_to_token_device(self): + ''' + Test the device name conversor + ''' + uuid = '988c663d-74a2-432b-ba52-3eea34015f22' + salt_mock = { + 'disk.blkid': MagicMock(return_value={ + '/dev/sda1': {'UUID': uuid} + }), + } + with patch.dict(mount.__salt__, salt_mock): + uuid = 'UUID={}'.format(uuid) + assert mount._convert_to(uuid, 'device') == '/dev/sda1' + salt_mock['disk.blkid'].assert_called_with(token=uuid) + + def test_fstab_present_macos_test_present(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': None, + 'changes': {}, + 'comment': ['/home entry is already in /etc/auto_salt.'], + } + + grains_mock = {'os': 'MacOS'} + opts_mock = {'test': True} + salt_mock = { + 'mount.set_automaster': MagicMock(return_value='present') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_automaster'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + opts='noowners', + config='/etc/auto_salt', + test=True) + + def test_fstab_present_aix_test_present(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': None, + 'changes': {}, + 'comment': ['/home entry is already in /etc/filesystems.'], + } + + grains_mock = {'os': 'AIX'} + opts_mock = {'test': True} + salt_mock = { + 'mount.set_filesystems': MagicMock(return_value='present') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_filesystems'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + mount=True, + opts='', + config='/etc/filesystems', + test=True, + match_on='auto') + + def test_fstab_present_test_present(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': None, + 'changes': {}, + 'comment': ['/home entry is already in /etc/fstab.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': True} + salt_mock = { + 'mount.set_fstab': MagicMock(return_value='present') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_fstab'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + opts='defaults', + dump=0, + pass_num=0, + config='/etc/fstab', + test=True, + match_on='auto') + + def test_fstab_present_test_new(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': None, + 'changes': {}, + 'comment': ['/home entry will be written in /etc/fstab.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': True} + salt_mock = { + 'mount.set_fstab': MagicMock(return_value='new') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_fstab'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + opts='defaults', + dump=0, + pass_num=0, + config='/etc/fstab', + test=True, + match_on='auto') + + def test_fstab_present_test_change(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': None, + 'changes': {}, + 'comment': ['/home entry will be updated in /etc/fstab.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': True} + salt_mock = { + 'mount.set_fstab': MagicMock(return_value='change') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_fstab'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + opts='defaults', + dump=0, + pass_num=0, + config='/etc/fstab', + test=True, + match_on='auto') + + def test_fstab_present_test_error(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': False, + 'changes': {}, + 'comment': ['/home entry cannot be created in /etc/fstab: error.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': True} + salt_mock = { + 'mount.set_fstab': MagicMock(return_value='error') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_fstab'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + opts='defaults', + dump=0, + pass_num=0, + config='/etc/fstab', + test=True, + match_on='auto') + + def test_fstab_present_macos_present(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': True, + 'changes': {}, + 'comment': ['/home entry was already in /etc/auto_salt.'], + } + + grains_mock = {'os': 'MacOS'} + opts_mock = {'test': False} + salt_mock = { + 'mount.set_automaster': MagicMock(return_value='present') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_automaster'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + opts='noowners', + config='/etc/auto_salt') + + def test_fstab_present_aix_present(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': True, + 'changes': {}, + 'comment': ['/home entry was already in /etc/filesystems.'], + } + + grains_mock = {'os': 'AIX'} + opts_mock = {'test': False} + salt_mock = { + 'mount.set_filesystems': MagicMock(return_value='present') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_filesystems'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + mount=True, + opts='', + config='/etc/filesystems', + match_on='auto') + + def test_fstab_present_present(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': True, + 'changes': {}, + 'comment': ['/home entry was already in /etc/fstab.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': False} + salt_mock = { + 'mount.set_fstab': MagicMock(return_value='present') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_fstab'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + opts='defaults', + dump=0, + pass_num=0, + config='/etc/fstab', + match_on='auto') + + def test_fstab_present_new(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': True, + 'changes': {'persist': 'new'}, + 'comment': ['/home entry added in /etc/fstab.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': False} + salt_mock = { + 'mount.set_fstab': MagicMock(return_value='new') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_fstab'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + opts='defaults', + dump=0, + pass_num=0, + config='/etc/fstab', + match_on='auto') + + def test_fstab_present_change(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': True, + 'changes': {'persist': 'change'}, + 'comment': ['/home entry updated in /etc/fstab.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': False} + salt_mock = { + 'mount.set_fstab': MagicMock(return_value='change') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_fstab'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + opts='defaults', + dump=0, + pass_num=0, + config='/etc/fstab', + match_on='auto') + + def test_fstab_present_fail(self): + ''' + Test fstab_present + ''' + ret = { + 'name': '/dev/sda1', + 'result': False, + 'changes': {}, + 'comment': ['/home entry cannot be changed in /etc/fstab: error.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': False} + salt_mock = { + 'mount.set_fstab': MagicMock(return_value='error') + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret + salt_mock['mount.set_fstab'].assert_called_with(name='/home', + device='/dev/sda1', + fstype='ext2', + opts='defaults', + dump=0, + pass_num=0, + config='/etc/fstab', + match_on='auto') + + def test_fstab_absent_macos_test_absent(self): + ''' + Test fstab_absent + ''' + ret = { + 'name': '/dev/sda1', + 'result': None, + 'changes': {}, + 'comment': ['/home entry is already missing in /etc/auto_salt.'], + } + + grains_mock = {'os': 'MacOS'} + opts_mock = {'test': True} + salt_mock = { + 'mount.automaster': MagicMock(return_value={}) + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent('/dev/sda1', '/home') == ret + salt_mock['mount.automaster'].assert_called_with('/etc/auto_salt') + + def test_fstab_absent_aix_test_absent(self): + ''' + Test fstab_absent + ''' + ret = { + 'name': '/dev/sda1', + 'result': None, + 'changes': {}, + 'comment': ['/home entry is already missing in /etc/filesystems.'], + } + + grains_mock = {'os': 'AIX'} + opts_mock = {'test': True} + salt_mock = { + 'mount.filesystems': MagicMock(return_value={}) + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent('/dev/sda1', '/home') == ret + salt_mock['mount.filesystems'].assert_called_with('/etc/filesystems') + + def test_fstab_absent_test_absent(self): + ''' + Test fstab_absent + ''' + ret = { + 'name': '/dev/sda1', + 'result': None, + 'changes': {}, + 'comment': ['/home entry is already missing in /etc/fstab.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': True} + salt_mock = { + 'mount.fstab': MagicMock(return_value={}) + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent('/dev/sda1', '/home') == ret + salt_mock['mount.fstab'].assert_called_with('/etc/fstab') + + def test_fstab_absent_test_present(self): + ''' + Test fstab_absent + ''' + ret = { + 'name': '/dev/sda1', + 'result': None, + 'changes': {}, + 'comment': ['/home entry will be removed from /etc/fstab.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': True} + salt_mock = { + 'mount.fstab': MagicMock(return_value={'/home': {}}) + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent('/dev/sda1', '/home') == ret + salt_mock['mount.fstab'].assert_called_with('/etc/fstab') + + def test_fstab_absent_macos_present(self): + ''' + Test fstab_absent + ''' + ret = { + 'name': '/dev/sda1', + 'result': True, + 'changes': {'persist': 'removed'}, + 'comment': ['/home entry removed from /etc/auto_salt.'], + } + + grains_mock = {'os': 'MacOS'} + opts_mock = {'test': False} + salt_mock = { + 'mount.automaster': MagicMock(return_value={'/home': {}}), + 'mount.rm_automaster': MagicMock(return_value=True) + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent('/dev/sda1', '/home') == ret + salt_mock['mount.automaster'].assert_called_with('/etc/auto_salt') + salt_mock['mount.rm_automaster'].assert_called_with(name='/home', + device='/dev/sda1', + config='/etc/auto_salt') + + def test_fstab_absent_aix_present(self): + ''' + Test fstab_absent + ''' + ret = { + 'name': '/dev/sda1', + 'result': True, + 'changes': {'persist': 'removed'}, + 'comment': ['/home entry removed from /etc/filesystems.'], + } + + grains_mock = {'os': 'AIX'} + opts_mock = {'test': False} + salt_mock = { + 'mount.filesystems': MagicMock(return_value={'/home': {}}), + 'mount.rm_filesystems': MagicMock(return_value=True) + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent('/dev/sda1', '/home') == ret + salt_mock['mount.filesystems'].assert_called_with('/etc/filesystems') + salt_mock['mount.rm_filesystems'].assert_called_with(name='/home', + device='/dev/sda1', + config='/etc/filesystems') + + def test_fstab_absent_present(self): + ''' + Test fstab_absent + ''' + ret = { + 'name': '/dev/sda1', + 'result': True, + 'changes': {'persist': 'removed'}, + 'comment': ['/home entry removed from /etc/fstab.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': False} + salt_mock = { + 'mount.fstab': MagicMock(return_value={'/home': {}}), + 'mount.rm_fstab': MagicMock(return_value=True) + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent('/dev/sda1', '/home') == ret + salt_mock['mount.fstab'].assert_called_with('/etc/fstab') + salt_mock['mount.rm_fstab'].assert_called_with(name='/home', + device='/dev/sda1', + config='/etc/fstab') + + def test_fstab_absent_absent(self): + ''' + Test fstab_absent + ''' + ret = { + 'name': '/dev/sda1', + 'result': True, + 'changes': {}, + 'comment': ['/home entry is already missing in /etc/fstab.'], + } + + grains_mock = {'os': 'Linux'} + opts_mock = {'test': False} + salt_mock = { + 'mount.fstab': MagicMock(return_value={}) + } + with patch.dict(mount.__grains__, grains_mock), \ + patch.dict(mount.__opts__, opts_mock), \ + patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent('/dev/sda1', '/home') == ret + salt_mock['mount.fstab'].assert_called_with('/etc/fstab') diff --git a/tests/unit/states/test_mysql_grants.py b/tests/unit/states/test_mysql_grants.py index 12972dab7d3f..3b30b8f42fed 100644 --- a/tests/unit/states/test_mysql_grants.py +++ b/tests/unit/states/test_mysql_grants.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.mysql_grants as mysql_grants -@skipIf(NO_MOCK, NO_MOCK_REASON) class MysqlGrantsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.mysql_grants diff --git a/tests/unit/states/test_mysql_query.py b/tests/unit/states/test_mysql_query.py index 5c32e4d1b692..95a7e00774af 100644 --- a/tests/unit/states/test_mysql_query.py +++ b/tests/unit/states/test_mysql_query.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -19,7 +17,6 @@ import salt.states.mysql_query as mysql_query -@skipIf(NO_MOCK, NO_MOCK_REASON) class MysqlQueryTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.mysql_query diff --git a/tests/unit/states/test_mysql_user.py b/tests/unit/states/test_mysql_user.py index 972d87cdbc3e..c1797a343a4d 100644 --- a/tests/unit/states/test_mysql_user.py +++ b/tests/unit/states/test_mysql_user.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -19,7 +17,6 @@ import salt.utils.data -@skipIf(NO_MOCK, NO_MOCK_REASON) class MysqlUserTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.mysql_user diff --git a/tests/unit/states/test_net_napalm_yang.py b/tests/unit/states/test_net_napalm_yang.py index 40318977bb95..ea66ac9b97f5 100644 --- a/tests/unit/states/test_net_napalm_yang.py +++ b/tests/unit/states/test_net_napalm_yang.py @@ -10,10 +10,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -23,7 +21,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class NetyangTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return {netyang: {}} diff --git a/tests/unit/states/test_network.py b/tests/unit/states/test_network.py index e086c5fb0f1a..17da548b3b3e 100644 --- a/tests/unit/states/test_network.py +++ b/tests/unit/states/test_network.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -53,7 +51,6 @@ def grains(lis, bol): return {'A': 'B'} -@skipIf(NO_MOCK, NO_MOCK_REASON) class NetworkTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the network state diff --git a/tests/unit/states/test_nexus.py b/tests/unit/states/test_nexus.py index b3525d774a36..17299ecbc181 100644 --- a/tests/unit/states/test_nexus.py +++ b/tests/unit/states/test_nexus.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.nexus as nexus -@skipIf(NO_MOCK, NO_MOCK_REASON) class nexusTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.nexus diff --git a/tests/unit/states/test_nftables.py b/tests/unit/states/test_nftables.py index 5566292bbbde..d9767f2a923f 100644 --- a/tests/unit/states/test_nftables.py +++ b/tests/unit/states/test_nftables.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.nftables as nftables -@skipIf(NO_MOCK, NO_MOCK_REASON) class NftablesTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the nftables state diff --git a/tests/unit/states/test_npm.py b/tests/unit/states/test_npm.py index 3bfb4bc4d8d8..b51f8e070f50 100644 --- a/tests/unit/states/test_npm.py +++ b/tests/unit/states/test_npm.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -20,7 +18,6 @@ import salt.states.npm as npm -@skipIf(NO_MOCK, NO_MOCK_REASON) class NpmTestCase(TestCase, LoaderModuleMockMixin): ''' diff --git a/tests/unit/states/test_ntp.py b/tests/unit/states/test_ntp.py index 5c1011094109..01133d041847 100644 --- a/tests/unit/states/test_ntp.py +++ b/tests/unit/states/test_ntp.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.ntp as ntp -@skipIf(NO_MOCK, NO_MOCK_REASON) class NtpTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ntp diff --git a/tests/unit/states/test_openstack_config.py b/tests/unit/states/test_openstack_config.py index 5ae0f83d6219..7bd203be17de 100644 --- a/tests/unit/states/test_openstack_config.py +++ b/tests/unit/states/test_openstack_config.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -20,7 +18,6 @@ import salt.states.openstack_config as openstack_config -@skipIf(NO_MOCK, NO_MOCK_REASON) class OpenstackConfigTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.openstack_config diff --git a/tests/unit/states/test_openvswitch_port.py b/tests/unit/states/test_openvswitch_port.py index 046b48ca9649..8d603874c4fc 100644 --- a/tests/unit/states/test_openvswitch_port.py +++ b/tests/unit/states/test_openvswitch_port.py @@ -4,18 +4,15 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, - NO_MOCK, - NO_MOCK_REASON, patch) # Import Salt Libs import salt.states.openvswitch_port as openvswitch_port -@skipIf(NO_MOCK, NO_MOCK_REASON) class OpenvswitchPortTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.openvswitch_port diff --git a/tests/unit/states/test_pagerduty.py b/tests/unit/states/test_pagerduty.py index 18a16c9ab275..54e98ca7633b 100644 --- a/tests/unit/states/test_pagerduty.py +++ b/tests/unit/states/test_pagerduty.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.pagerduty as pagerduty -@skipIf(NO_MOCK, NO_MOCK_REASON) class PagerdutyTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.pagerduty diff --git a/tests/unit/states/test_pecl.py b/tests/unit/states/test_pecl.py index 8f1dff940f2f..e6d37b0c2f1f 100644 --- a/tests/unit/states/test_pecl.py +++ b/tests/unit/states/test_pecl.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.pecl as pecl -@skipIf(NO_MOCK, NO_MOCK_REASON) class PeclTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.pecl diff --git a/tests/unit/states/test_pip_state.py b/tests/unit/states/test_pip_state.py index 08f31b8a73f6..5efb70f8d7a6 100644 --- a/tests/unit/states/test_pip_state.py +++ b/tests/unit/states/test_pip_state.py @@ -15,7 +15,7 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin, SaltReturnAssertsMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch # Import salt libs import salt.states.pip_state as pip_state @@ -31,7 +31,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PIP, 'The \'pip\' library is not importable(installed system-wide)') class PipStateTest(TestCase, SaltReturnAssertsMixin, LoaderModuleMockMixin): diff --git a/tests/unit/states/test_pkg.py b/tests/unit/states/test_pkg.py index 42fe6c6867ab..174ab65ab8e7 100644 --- a/tests/unit/states/test_pkg.py +++ b/tests/unit/states/test_pkg.py @@ -5,10 +5,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ from salt.ext.six.moves import zip -@skipIf(NO_MOCK, NO_MOCK_REASON) class PkgTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.pkg diff --git a/tests/unit/states/test_pkgng.py b/tests/unit/states/test_pkgng.py index 25398ec2665e..42d9101fcc0e 100644 --- a/tests/unit/states/test_pkgng.py +++ b/tests/unit/states/test_pkgng.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.pkgng as pkgng -@skipIf(NO_MOCK, NO_MOCK_REASON) class PkgngTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.pkgng diff --git a/tests/unit/states/test_portage_config.py b/tests/unit/states/test_portage_config.py index 1138939f4b54..2feb5cb5dfba 100644 --- a/tests/unit/states/test_portage_config.py +++ b/tests/unit/states/test_portage_config.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.portage_config as portage_config -@skipIf(NO_MOCK, NO_MOCK_REASON) class PortageConfigTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.portage_config diff --git a/tests/unit/states/test_ports.py b/tests/unit/states/test_ports.py index 3dbdfdbee4b6..27130ceca82b 100644 --- a/tests/unit/states/test_ports.py +++ b/tests/unit/states/test_ports.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -43,7 +41,6 @@ def __init__(self): self.modules = {'A': MockContext()} -@skipIf(NO_MOCK, NO_MOCK_REASON) class PortsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ports diff --git a/tests/unit/states/test_postgres.py b/tests/unit/states/test_postgres.py index 8291d4cd2042..e8b9793112ce 100644 --- a/tests/unit/states/test_postgres.py +++ b/tests/unit/states/test_postgres.py @@ -5,8 +5,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, Mock, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import Mock, MagicMock, patch # Import salt libs import salt.modules.postgres as postgresmod @@ -17,7 +17,6 @@ import salt.states.postgres_schema as postgres_schema -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresUserTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -164,7 +163,6 @@ def test_present__no_update(self): self.assertEqual(self.salt_stub['postgres.user_update'].call_count, 0) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresGroupTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -309,7 +307,6 @@ def test_present__no_update(self): self.assertEqual(self.salt_stub['postgres.group_update'].call_count, 0) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresExtensionTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): @@ -498,7 +495,6 @@ def test_absent_failedtest(self): ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresSchemaTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/states/test_postgres_cluster.py b/tests/unit/states/test_postgres_cluster.py index 9bf6b3b1e2e5..632c00db0afd 100644 --- a/tests/unit/states/test_postgres_cluster.py +++ b/tests/unit/states/test_postgres_cluster.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.postgres_cluster as postgres_cluster -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresClusterTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.postgres_cluster diff --git a/tests/unit/states/test_postgres_database.py b/tests/unit/states/test_postgres_database.py index 7688787c89f9..b24fd73b5e06 100644 --- a/tests/unit/states/test_postgres_database.py +++ b/tests/unit/states/test_postgres_database.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.postgres_database as postgres_database -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresDatabaseTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.postgres_database diff --git a/tests/unit/states/test_postgres_extension.py b/tests/unit/states/test_postgres_extension.py index f8bb340ece32..5f454f766245 100644 --- a/tests/unit/states/test_postgres_extension.py +++ b/tests/unit/states/test_postgres_extension.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.postgres_extension as postgres_extension -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresExtensionTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.postgres_extension diff --git a/tests/unit/states/test_postgres_group.py b/tests/unit/states/test_postgres_group.py index 8948c01a211c..95a66239b277 100644 --- a/tests/unit/states/test_postgres_group.py +++ b/tests/unit/states/test_postgres_group.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.postgres_group as postgres_group -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresGroupTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.postgres_group diff --git a/tests/unit/states/test_postgres_initdb.py b/tests/unit/states/test_postgres_initdb.py index 9c8088ad03e7..42b825682e53 100644 --- a/tests/unit/states/test_postgres_initdb.py +++ b/tests/unit/states/test_postgres_initdb.py @@ -5,10 +5,8 @@ from __future__ import absolute_import, unicode_literals, print_function from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -16,7 +14,6 @@ import salt.states.postgres_initdb as postgres_initdb -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresInitdbTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.postgres_initdb diff --git a/tests/unit/states/test_postgres_language.py b/tests/unit/states/test_postgres_language.py index 7a269f9afd15..843ad921e10f 100644 --- a/tests/unit/states/test_postgres_language.py +++ b/tests/unit/states/test_postgres_language.py @@ -5,10 +5,8 @@ from __future__ import absolute_import, unicode_literals, print_function from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -16,7 +14,6 @@ import salt.states.postgres_language as postgres_language -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresLanguageTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.postgres_language diff --git a/tests/unit/states/test_postgres_privileges.py b/tests/unit/states/test_postgres_privileges.py index ab2dd2d1ab94..372ffed20db5 100644 --- a/tests/unit/states/test_postgres_privileges.py +++ b/tests/unit/states/test_postgres_privileges.py @@ -5,10 +5,8 @@ from __future__ import absolute_import, unicode_literals, print_function from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -16,7 +14,6 @@ import salt.states.postgres_privileges as postgres_privileges -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresPrivilegesTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.postgres_privileges diff --git a/tests/unit/states/test_postgres_schema.py b/tests/unit/states/test_postgres_schema.py index 082135d15179..8e6b1a389034 100644 --- a/tests/unit/states/test_postgres_schema.py +++ b/tests/unit/states/test_postgres_schema.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.postgres_schema as postgres_schema -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresSchemaTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.postgres_schema diff --git a/tests/unit/states/test_postgres_user.py b/tests/unit/states/test_postgres_user.py index 75b6525b287e..3c636ac4c553 100644 --- a/tests/unit/states/test_postgres_user.py +++ b/tests/unit/states/test_postgres_user.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.postgres_user as postgres_user -@skipIf(NO_MOCK, NO_MOCK_REASON) class PostgresUserTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.postgres_user diff --git a/tests/unit/states/test_powerpath.py b/tests/unit/states/test_powerpath.py index 4f253fe3bd70..584041a866ef 100644 --- a/tests/unit/states/test_powerpath.py +++ b/tests/unit/states/test_powerpath.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.powerpath as powerpath -@skipIf(NO_MOCK, NO_MOCK_REASON) class PowerpathTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.powerpath diff --git a/tests/unit/states/test_process.py b/tests/unit/states/test_process.py index f46005e22a25..326dfd627ee8 100644 --- a/tests/unit/states/test_process.py +++ b/tests/unit/states/test_process.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.process as process -@skipIf(NO_MOCK, NO_MOCK_REASON) class ProcessTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.process diff --git a/tests/unit/states/test_proxy.py b/tests/unit/states/test_proxy.py index f21e0506437c..3ef1484889d1 100644 --- a/tests/unit/states/test_proxy.py +++ b/tests/unit/states/test_proxy.py @@ -5,10 +5,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch, call @@ -18,7 +16,6 @@ import salt.states.proxy as proxy -@skipIf(NO_MOCK, NO_MOCK_REASON) class ProxyTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the proxy state diff --git a/tests/unit/states/test_pyenv.py b/tests/unit/states/test_pyenv.py index 67248bcdcee6..e6392fe4aaf6 100644 --- a/tests/unit/states/test_pyenv.py +++ b/tests/unit/states/test_pyenv.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.pyenv as pyenv -@skipIf(NO_MOCK, NO_MOCK_REASON) class PyenvTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.pyenv diff --git a/tests/unit/states/test_pyrax_queues.py b/tests/unit/states/test_pyrax_queues.py index ede7ee218fb6..c39664686feb 100644 --- a/tests/unit/states/test_pyrax_queues.py +++ b/tests/unit/states/test_pyrax_queues.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.pyrax_queues as pyrax_queues -@skipIf(NO_MOCK, NO_MOCK_REASON) class PyraxQueuesTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.pyrax_queues diff --git a/tests/unit/states/test_quota.py b/tests/unit/states/test_quota.py index 9e30d2d6f0b8..4362adcd6fd2 100644 --- a/tests/unit/states/test_quota.py +++ b/tests/unit/states/test_quota.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.quota as quota -@skipIf(NO_MOCK, NO_MOCK_REASON) class QuotaTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.quota diff --git a/tests/unit/states/test_rabbitmq_cluster.py b/tests/unit/states/test_rabbitmq_cluster.py index 98e4047aa051..51a5bb1bf56c 100644 --- a/tests/unit/states/test_rabbitmq_cluster.py +++ b/tests/unit/states/test_rabbitmq_cluster.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.rabbitmq_cluster as rabbitmq_cluster -@skipIf(NO_MOCK, NO_MOCK_REASON) class RabbitmqClusterTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the rabbitmq_cluster state diff --git a/tests/unit/states/test_rabbitmq_plugin.py b/tests/unit/states/test_rabbitmq_plugin.py index 7a3a9a5ae88a..6590788b715d 100644 --- a/tests/unit/states/test_rabbitmq_plugin.py +++ b/tests/unit/states/test_rabbitmq_plugin.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.rabbitmq_plugin as rabbitmq_plugin -@skipIf(NO_MOCK, NO_MOCK_REASON) class RabbitmqPluginTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.rabbitmq_plugin diff --git a/tests/unit/states/test_rabbitmq_policy.py b/tests/unit/states/test_rabbitmq_policy.py index 9707b5ceb479..9209e77a4a37 100644 --- a/tests/unit/states/test_rabbitmq_policy.py +++ b/tests/unit/states/test_rabbitmq_policy.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.rabbitmq_policy as rabbitmq_policy -@skipIf(NO_MOCK, NO_MOCK_REASON) class RabbitmqPolicyTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.rabbitmq_policy diff --git a/tests/unit/states/test_rabbitmq_vhost.py b/tests/unit/states/test_rabbitmq_vhost.py index 550f4a0846ec..70854cce7efd 100644 --- a/tests/unit/states/test_rabbitmq_vhost.py +++ b/tests/unit/states/test_rabbitmq_vhost.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.rabbitmq_vhost as rabbitmq_vhost -@skipIf(NO_MOCK, NO_MOCK_REASON) class RabbitmqVhostTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.rabbitmq_vhost diff --git a/tests/unit/states/test_rbenv.py b/tests/unit/states/test_rbenv.py index 03cfb06165f5..a8b1833a350f 100644 --- a/tests/unit/states/test_rbenv.py +++ b/tests/unit/states/test_rbenv.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.rbenv as rbenv -@skipIf(NO_MOCK, NO_MOCK_REASON) class RbenvTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.rbenv diff --git a/tests/unit/states/test_rdp.py b/tests/unit/states/test_rdp.py index 66005706765a..248cfb670935 100644 --- a/tests/unit/states/test_rdp.py +++ b/tests/unit/states/test_rdp.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.rdp as rdp -@skipIf(NO_MOCK, NO_MOCK_REASON) class RdpTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.rdp diff --git a/tests/unit/states/test_redismod.py b/tests/unit/states/test_redismod.py index 5e8cac623325..e417e2de26b1 100644 --- a/tests/unit/states/test_redismod.py +++ b/tests/unit/states/test_redismod.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.redismod as redismod -@skipIf(NO_MOCK, NO_MOCK_REASON) class RedismodTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.redismod diff --git a/tests/unit/states/test_rsync.py b/tests/unit/states/test_rsync.py index e2e6c9e4da13..cecebfc37c6e 100644 --- a/tests/unit/states/test_rsync.py +++ b/tests/unit/states/test_rsync.py @@ -10,16 +10,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class RsyncTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the Rsync state diff --git a/tests/unit/states/test_rvm.py b/tests/unit/states/test_rvm.py index 30e0cab97218..1acad27db146 100644 --- a/tests/unit/states/test_rvm.py +++ b/tests/unit/states/test_rvm.py @@ -5,8 +5,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.states.rvm as rvm @@ -15,7 +15,6 @@ from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestRvmState(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/states/test_saltmod.py b/tests/unit/states/test_saltmod.py index 2408ead9e136..1519aefa225c 100644 --- a/tests/unit/states/test_saltmod.py +++ b/tests/unit/states/test_saltmod.py @@ -9,12 +9,10 @@ import tempfile # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -27,14 +25,13 @@ import salt.states.saltmod as saltmod -@skipIf(NO_MOCK, NO_MOCK_REASON) class SaltmodTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.saltmod ''' def setup_loader_modules(self): utils = salt.loader.utils( - salt.config.DEFAULT_MINION_OPTS, + salt.config.DEFAULT_MINION_OPTS.copy(), whitelist=['state'] ) return { @@ -43,7 +40,7 @@ def setup_loader_modules(self): '__opts__': { '__role': 'master', 'file_client': 'remote', - 'sock_dir': tempfile.mkdtemp(dir=TMP), + 'sock_dir': tempfile.mkdtemp(dir=RUNTIME_VARS.TMP), 'transport': 'tcp' }, '__salt__': {'saltutil.cmd': MagicMock()}, @@ -299,10 +296,9 @@ def cmd_mock(*args, **kwargs): assert cmd_kwargs['roster'] == 'my_roster' -@skipIf(NO_MOCK, NO_MOCK_REASON) class StatemodTests(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - self.tmp_cachedir = tempfile.mkdtemp(dir=TMP) + self.tmp_cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) return { saltmod: { '__env__': 'base', diff --git a/tests/unit/states/test_saltutil.py b/tests/unit/states/test_saltutil.py new file mode 100644 index 000000000000..1f7eb062cf8a --- /dev/null +++ b/tests/unit/states/test_saltutil.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +''' + Tests for the saltutil state +''' +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +import inspect + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase +from tests.support.mock import ( + MagicMock, + patch +) + +# Import Salt Libs +import salt.states.saltutil as saltutil_state +import salt.modules.saltutil as saltutil_module + + +class Saltutil(TestCase, LoaderModuleMockMixin): + ''' + Test cases for salt.states.saltutil + ''' + def setup_loader_modules(self): + return {saltutil_state: {'__opts__': {'test': False}}} + + def test_saltutil_sync_all_nochange(self): + sync_output = { + 'clouds': [], + 'engines': [], + 'executors': [], + 'grains': [], + 'beacons': [], + 'utils': [], + 'returners': [], + 'modules': [], + 'renderers': [], + 'log_handlers': [], + 'thorium': [], + 'states': [], + 'sdb': [], + 'proxymodules': [], + 'output': [], + 'pillar': [], + 'matchers': [], + 'serializers': [], + } + state_id = 'somename' + state_result = {'changes': {}, + 'comment': 'No updates to sync', + 'name': 'somename', + 'result': True + } + + mock_moduleout = MagicMock(return_value=sync_output) + with patch.dict(saltutil_state.__salt__, {'saltutil.sync_all': mock_moduleout}): + result = saltutil_state.sync_all(state_id, refresh=True) + self.assertEqual(result, state_result) + + def test_saltutil_sync_all_test(self): + sync_output = { + 'clouds': [], + 'engines': [], + 'executors': [], + 'grains': [], + 'beacons': [], + 'utils': [], + 'returners': [], + 'modules': [], + 'renderers': [], + 'log_handlers': [], + 'thorium': [], + 'states': [], + 'sdb': [], + 'proxymodules': [], + 'output': [], + 'pillar': [], + 'matchers': [], + 'serializers': [], + } + state_id = 'somename' + state_result = {'changes': {}, + 'comment': 'saltutil.sync_all would have been run', + 'name': 'somename', + 'result': None + } + + mock_moduleout = MagicMock(return_value=sync_output) + with patch.dict(saltutil_state.__salt__, {'saltutil.sync_all': mock_moduleout}): + with patch.dict(saltutil_state.__opts__, {'test': True}): + result = saltutil_state.sync_all(state_id, refresh=True) + self.assertEqual(result, state_result) + + def test_saltutil_sync_all_change(self): + sync_output = { + 'clouds': [], + 'engines': [], + 'executors': [], + 'grains': [], + 'beacons': [], + 'utils': [], + 'returners': [], + 'modules': ['modules.file'], + 'renderers': [], + 'log_handlers': [], + 'thorium': [], + 'states': ['states.saltutil', 'states.ssh_auth'], + 'sdb': [], + 'proxymodules': [], + 'output': [], + 'pillar': [], + 'matchers': [], + 'serializers': [], + } + state_id = 'somename' + state_result = {'changes': {'modules': ['modules.file'], + 'states': ['states.saltutil', 'states.ssh_auth']}, + 'comment': 'Sync performed', + 'name': 'somename', + 'result': True + } + + mock_moduleout = MagicMock(return_value=sync_output) + with patch.dict(saltutil_state.__salt__, {'saltutil.sync_all': mock_moduleout}): + result = saltutil_state.sync_all(state_id, refresh=True) + self.assertEqual(result, state_result) + + def test_saltutil_sync_states_should_match_saltutil_module(self): + module_functions = [ + f[0] for f in inspect.getmembers(saltutil_module, inspect.isfunction) + if f[0].startswith('sync_') + ] + state_functions = [ + f[0] for f in inspect.getmembers(saltutil_state, inspect.isfunction) + if f[0].startswith('sync_') + ] + for fn in module_functions: + self.assertIn( + fn, + state_functions, + msg='modules.saltutil.{} has no matching state in states.saltutil'.format(fn) + ) diff --git a/tests/unit/states/test_schedule.py b/tests/unit/states/test_schedule.py index 156e0db3f17b..79e6ba3c72d6 100644 --- a/tests/unit/states/test_schedule.py +++ b/tests/unit/states/test_schedule.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.schedule as schedule -@skipIf(NO_MOCK, NO_MOCK_REASON) class ScheduleTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.schedule diff --git a/tests/unit/states/test_selinux.py b/tests/unit/states/test_selinux.py index 04b47f6b213f..4040815ad908 100644 --- a/tests/unit/states/test_selinux.py +++ b/tests/unit/states/test_selinux.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.selinux as selinux -@skipIf(NO_MOCK, NO_MOCK_REASON) class SelinuxTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.selinux diff --git a/tests/unit/states/test_serverdensity_device.py b/tests/unit/states/test_serverdensity_device.py index d75d77ef7163..6e54931548bd 100644 --- a/tests/unit/states/test_serverdensity_device.py +++ b/tests/unit/states/test_serverdensity_device.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.serverdensity_device as serverdensity_device -@skipIf(NO_MOCK, NO_MOCK_REASON) class ServerdensityDeviceTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.serverdensity_device diff --git a/tests/unit/states/test_service.py b/tests/unit/states/test_service.py index 9699f1e246fd..2677c0560116 100644 --- a/tests/unit/states/test_service.py +++ b/tests/unit/states/test_service.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -27,7 +25,6 @@ def func(name): return name -@skipIf(NO_MOCK, NO_MOCK_REASON) class ServiceTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the service state diff --git a/tests/unit/states/test_slack.py b/tests/unit/states/test_slack.py index 51b8fb99b390..81677538fb30 100644 --- a/tests/unit/states/test_slack.py +++ b/tests/unit/states/test_slack.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.slack as slack -@skipIf(NO_MOCK, NO_MOCK_REASON) class SlackTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.slack @@ -29,14 +26,15 @@ def setup_loader_modules(self): # 'post_message' function tests: 1 - def test_post_message(self): + def test_post_message_apikey(self): ''' - Test to send a message to a Slack channel. + Test to send a message to a Slack channel using an API Key. ''' name = 'slack-message' channel = '#general' from_name = 'SuperAdmin' message = 'This state was executed successfully.' + api_key = 'xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXXXX' ret = {'name': name, 'changes': {}, @@ -47,29 +45,115 @@ def test_post_message(self): comt = ('The following message is to be sent to Slack: {0}' .format(message)) ret.update({'comment': comt}) - self.assertDictEqual(slack.post_message(name, channel, from_name, - message), ret) + self.assertDictEqual(slack.post_message(name, + channel=channel, + from_name=from_name, + message=message, + api_key=api_key), ret) with patch.dict(slack.__opts__, {'test': False}): - comt = ('Slack channel is missing: None') + comt = ('Please specify api_key or webhook.') ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(slack.post_message(name, None, from_name, - message), ret) + self.assertDictEqual(slack.post_message(name, + channel=None, + from_name=from_name, + message=message, + api_key=None), ret) - comt = ('Slack from name is missing: None') + comt = ('Slack channel is missing.') ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(slack.post_message(name, channel, None, - message), ret) + self.assertDictEqual(slack.post_message(name, + channel=None, + from_name=from_name, + message=message, + api_key=api_key), ret) - comt = ('Slack message is missing: None') + comt = ('Slack from name is missing.') ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(slack.post_message(name, channel, from_name, - None), ret) + self.assertDictEqual(slack.post_message(name, + channel=channel, + from_name=None, + message=message, + api_key=api_key), ret) + + comt = ('Slack message is missing.') + ret.update({'comment': comt, 'result': False}) + self.assertDictEqual(slack.post_message(name, + channel=channel, + from_name=from_name, + message=None, + api_key=api_key), ret) mock = MagicMock(return_value=True) with patch.dict(slack.__salt__, {'slack.post_message': mock}): comt = ('Sent message: slack-message') ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(slack.post_message(name, channel, - from_name, message), + self.assertDictEqual(slack.post_message(name, + channel=channel, + from_name=from_name, + message=message, + api_key=api_key), + ret) + + def test_post_message_webhook(self): + ''' + Test to send a message to a Slack channel using an webhook. + ''' + name = 'slack-message' + channel = '#general' + username = 'SuperAdmin' + message = 'This state was executed successfully.' + webhook = 'XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX' + api_key = 'xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXXXX' + + ret = {'name': name, + 'changes': {}, + 'result': None, + 'comment': ''} + + with patch.dict(slack.__opts__, {'test': True}): + comt = ('The following message is to be sent to Slack: {0}' + .format(message)) + ret.update({'comment': comt}) + self.assertDictEqual(slack.post_message(name, + channel=channel, + username=username, + message=message, + webhook=webhook), ret) + + with patch.dict(slack.__opts__, {'test': False}): + comt = ('Please specify api_key or webhook.') + ret.update({'comment': comt, 'result': False}) + self.assertDictEqual(slack.post_message(name, + channel=channel, + username=username, + message=None, + webhook=None), ret) + + comt = ('Please specify only either api_key or webhook.') + ret.update({'comment': comt, 'result': False}) + self.assertDictEqual(slack.post_message(name, + channel=channel, + username=username, + message=message, + api_key=api_key, + webhook=webhook), ret) + + comt = ('Slack message is missing.') + ret.update({'comment': comt, 'result': False}) + self.assertDictEqual(slack.post_message(name, + channel=channel, + username=username, + message=None, + webhook=webhook), ret) + + mock = MagicMock(return_value=True) + with patch.dict(slack.__salt__, {'slack.call_hook': mock}): + comt = ('Sent message: slack-message') + ret.update({'comment': comt, 'result': True}) + self.assertDictEqual(slack.post_message(name, + channel=channel, + username=username, + message=message, + webhook=webhook), ret) diff --git a/tests/unit/states/test_smtp.py b/tests/unit/states/test_smtp.py index d05c6d665ccc..9dd8693ed8a2 100644 --- a/tests/unit/states/test_smtp.py +++ b/tests/unit/states/test_smtp.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.smtp as smtp -@skipIf(NO_MOCK, NO_MOCK_REASON) class SmtpTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.smtp diff --git a/tests/unit/states/test_splunk_search.py b/tests/unit/states/test_splunk_search.py index 62a5a596155a..7e4824f3dc8d 100644 --- a/tests/unit/states/test_splunk_search.py +++ b/tests/unit/states/test_splunk_search.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.splunk_search as splunk_search -@skipIf(NO_MOCK, NO_MOCK_REASON) class SplunkSearchTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.splunk_search diff --git a/tests/unit/states/test_ssh_auth.py b/tests/unit/states/test_ssh_auth.py index 57a7cc99afb7..3039daaa3df0 100644 --- a/tests/unit/states/test_ssh_auth.py +++ b/tests/unit/states/test_ssh_auth.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.ssh_auth as ssh_auth -@skipIf(NO_MOCK, NO_MOCK_REASON) class SshAuthTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ssh_auth @@ -102,3 +99,78 @@ def test_absent(self): ret.update({'comment': comt, 'result': True, 'changes': {name: 'Removed'}}) self.assertDictEqual(ssh_auth.absent(name, user, source), ret) + + def test_manage(self): + ''' + Test to verifies that the specified SSH key is absent. + ''' + user = 'root' + ret = {'name': '', + 'changes': {}, + 'result': None, + 'comment': ''} + + mock_rm = MagicMock(side_effect=['User authorized keys file not present', + 'Key removed']) + mock_up = MagicMock(side_effect=['update', 'updated']) + mock_set = MagicMock(side_effect=['replace', 'new']) + mock_keys = MagicMock(return_value={'somekey': { + "enc": "ssh-rsa", + "comment": "user@host", + "options": [], + "fingerprint": "b7"}}) + with patch.dict(ssh_auth.__salt__, {'ssh.rm_auth_key': mock_rm, + 'ssh.set_auth_key': mock_set, + 'ssh.check_key': mock_up, + 'ssh.auth_keys': mock_keys}): + with patch('salt.states.ssh_auth.present') as call_mocked_present: + mock_present = {'comment': '', + 'changes': {}, + 'result': None + } + call_mocked_present.return_value = mock_present + with patch.dict(ssh_auth.__opts__, {'test': True}): + # test: expected keys found. No chanages + self.assertDictEqual(ssh_auth.manage('sshid', ['somekey'], user), ret) + + comt = ('somekey Key set for removal') + ret.update({'comment': comt}) + # test: unexpected sshkey found. Should be removed. + self.assertDictEqual(ssh_auth.manage('sshid', [], user), ret) + + with patch('salt.states.ssh_auth.present') as call_mocked_present: + mock_present = {'comment': '', + 'changes': {}, + 'result': True + } + call_mocked_present.return_value = mock_present + with patch.dict(ssh_auth.__opts__, {'test': False}): + # expected keys found. No changes + ret = {'name': '', + 'changes': {}, + 'result': True, + 'comment': ''} + self.assertDictEqual(ssh_auth.manage('sshid', ['somekey'], user), ret) + + with patch('salt.states.ssh_auth.absent') as call_mocked_absent: + mock_absent = {'comment': 'Key removed'} + call_mocked_absent.return_value = mock_absent + ret.update({'comment': '', 'result': True, + 'changes': {'somekey': 'Key removed'}}) + # unexpected sshkey found. Was removed. + self.assertDictEqual(ssh_auth.manage('sshid', ['addkey'], user), ret) + + # add a key + with patch('salt.states.ssh_auth.present') as call_mocked_present: + mock_present = {'comment': 'The authorized host key newkey for user {} was added'.format(user), + 'changes': {'newkey': 'New'}, + 'result': True + } + call_mocked_present.return_value = mock_present + with patch.dict(ssh_auth.__opts__, {'test': False}): + # added key newkey + ret = {'name': '', + 'changes': {'newkey': 'New'}, + 'result': True, + 'comment': ''} + self.assertDictEqual(ssh_auth.manage('sshid', ['newkey', 'somekey'], user), ret) diff --git a/tests/unit/states/test_ssh_known_hosts.py b/tests/unit/states/test_ssh_known_hosts.py index 2fa555c2fbf0..a8a9a7099159 100644 --- a/tests/unit/states/test_ssh_known_hosts.py +++ b/tests/unit/states/test_ssh_known_hosts.py @@ -8,10 +8,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -20,7 +18,6 @@ import salt.states.ssh_known_hosts as ssh_known_hosts -@skipIf(NO_MOCK, NO_MOCK_REASON) class SshKnownHostsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.ssh_known_hosts diff --git a/tests/unit/states/test_status.py b/tests/unit/states/test_status.py index f82e964b93ee..fa0c67bbc304 100644 --- a/tests/unit/states/test_status.py +++ b/tests/unit/states/test_status.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.status as status -@skipIf(NO_MOCK, NO_MOCK_REASON) class StatusTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.status diff --git a/tests/unit/states/test_supervisord.py b/tests/unit/states/test_supervisord.py index 5720e3d7e34f..5b363b3ead44 100644 --- a/tests/unit/states/test_supervisord.py +++ b/tests/unit/states/test_supervisord.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -19,7 +17,6 @@ import salt.states.supervisord as supervisord -@skipIf(NO_MOCK, NO_MOCK_REASON) class SupervisordTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.supervisord diff --git a/tests/unit/states/test_svn.py b/tests/unit/states/test_svn.py index d80855667b26..9ebd22e2d74f 100644 --- a/tests/unit/states/test_svn.py +++ b/tests/unit/states/test_svn.py @@ -11,16 +11,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class SvnTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the svn state diff --git a/tests/unit/states/test_sysctl.py b/tests/unit/states/test_sysctl.py index 15e8e3c3cf93..1bf551917637 100644 --- a/tests/unit/states/test_sysctl.py +++ b/tests/unit/states/test_sysctl.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) @@ -21,7 +19,6 @@ import salt.states.sysctl as sysctl -@skipIf(NO_MOCK, NO_MOCK_REASON) class SysctlTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.sysctl diff --git a/tests/unit/states/test_syslog_ng.py b/tests/unit/states/test_syslog_ng.py index 1dcd4086f6a3..929f43530329 100644 --- a/tests/unit/states/test_syslog_ng.py +++ b/tests/unit/states/test_syslog_ng.py @@ -10,8 +10,8 @@ import tempfile from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch import salt.utils.files import salt.utils.yaml @@ -295,7 +295,6 @@ def remove_whitespaces(source): return re.sub(r"\s+", "", source.strip()) -@skipIf(NO_MOCK, NO_MOCK_REASON) class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { diff --git a/tests/unit/states/test_sysrc.py b/tests/unit/states/test_sysrc.py index 946d624c25f3..957023d6e688 100644 --- a/tests/unit/states/test_sysrc.py +++ b/tests/unit/states/test_sysrc.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.sysrc as sysrc -@skipIf(NO_MOCK, NO_MOCK_REASON) class SysrcTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the sysrc state diff --git a/tests/unit/states/test_test.py b/tests/unit/states/test_test.py index 5392c62bda6b..411b3936034d 100644 --- a/tests/unit/states/test_test.py +++ b/tests/unit/states/test_test.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( patch, MagicMock, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -23,7 +21,6 @@ from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the test state diff --git a/tests/unit/states/test_timezone.py b/tests/unit/states/test_timezone.py index 0d5d3eaa66b0..53d327a61477 100644 --- a/tests/unit/states/test_timezone.py +++ b/tests/unit/states/test_timezone.py @@ -8,12 +8,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -21,7 +19,6 @@ import salt.states.timezone as timezone -@skipIf(NO_MOCK, NO_MOCK_REASON) class TimezoneTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the timezone state diff --git a/tests/unit/states/test_tomcat.py b/tests/unit/states/test_tomcat.py index d3b5b9aaa359..9c2e84566b15 100644 --- a/tests/unit/states/test_tomcat.py +++ b/tests/unit/states/test_tomcat.py @@ -12,16 +12,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class TomcatTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the tomcat state diff --git a/tests/unit/states/test_user.py b/tests/unit/states/test_user.py index 4b7a3d8149b9..43d1454b9b09 100644 --- a/tests/unit/states/test_user.py +++ b/tests/unit/states/test_user.py @@ -9,13 +9,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( Mock, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -24,7 +22,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) class UserTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the user state diff --git a/tests/unit/states/test_vbox_guest.py b/tests/unit/states/test_vbox_guest.py index e98314429607..594518a7de62 100644 --- a/tests/unit/states/test_vbox_guest.py +++ b/tests/unit/states/test_vbox_guest.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.vbox_guest as vbox_guest -@skipIf(NO_MOCK, NO_MOCK_REASON) class VboxGuestTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the vbox_guest state diff --git a/tests/unit/states/test_virt.py b/tests/unit/states/test_virt.py index 80229899375c..b68d0b4d908b 100644 --- a/tests/unit/states/test_virt.py +++ b/tests/unit/states/test_virt.py @@ -8,12 +8,10 @@ import shutil # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.paths import TMP -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, mock_open, patch) @@ -42,7 +40,6 @@ def get_error_message(self): return six.text_type(self) -@skipIf(NO_MOCK, NO_MOCK_REASON) class LibvirtTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.libvirt @@ -57,7 +54,7 @@ def setup_loader_modules(self): @classmethod def setUpClass(cls): - cls.pki_dir = tempfile.mkdtemp(dir=TMP) + cls.pki_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @classmethod def tearDownClass(cls): @@ -229,7 +226,7 @@ def test_running(self): 'result': True, 'comment': 'myvm is running'} with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value='stopped'), + 'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}), 'virt.start': MagicMock(return_value=0), }): ret.update({'changes': {'myvm': 'Domain started'}, @@ -322,7 +319,7 @@ def test_running(self): password='supersecret') with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value='stopped'), + 'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}), 'virt.start': MagicMock(side_effect=[self.mock_libvirt.libvirtError('libvirt error msg')]) }): ret.update({'changes': {}, 'result': False, 'comment': 'libvirt error msg'}) @@ -330,7 +327,7 @@ def test_running(self): # Working update case when running with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value='running'), + 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), 'virt.update': MagicMock(return_value={'definition': True, 'cpu': True}) }): ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}}, @@ -340,7 +337,7 @@ def test_running(self): # Working update case when stopped with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value='stopped'), + 'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}), 'virt.start': MagicMock(return_value=0), 'virt.update': MagicMock(return_value={'definition': True}) }): @@ -351,7 +348,7 @@ def test_running(self): # Failed live update case with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value='running'), + 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), 'virt.update': MagicMock(return_value={'definition': True, 'cpu': False, 'errors': ['some error']}) }): ret.update({'changes': {'myvm': {'definition': True, 'cpu': False, 'errors': ['some error']}}, @@ -361,7 +358,7 @@ def test_running(self): # Failed definition update case with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value='running'), + 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), 'virt.update': MagicMock(side_effect=[self.mock_libvirt.libvirtError('error message')]) }): ret.update({'changes': {}, @@ -378,8 +375,11 @@ def test_stopped(self): 'result': True} shutdown_mock = MagicMock(return_value=True) + + # Normal case with patch.dict(virt.__salt__, { # pylint: disable=no-member 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), + 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), 'virt.shutdown': shutdown_mock }): ret.update({'changes': { @@ -389,8 +389,10 @@ def test_stopped(self): self.assertDictEqual(virt.stopped('myvm'), ret) shutdown_mock.assert_called_with('myvm', connection=None, username=None, password=None) + # Normal case with user-provided connection parameters with patch.dict(virt.__salt__, { # pylint: disable=no-member 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), + 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), 'virt.shutdown': shutdown_mock, }): self.assertDictEqual(virt.stopped('myvm', @@ -399,8 +401,10 @@ def test_stopped(self): password='secret'), ret) shutdown_mock.assert_called_with('myvm', connection='myconnection', username='user', password='secret') + # Case where an error occurred during the shutdown with patch.dict(virt.__salt__, { # pylint: disable=no-member 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), + 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), 'virt.shutdown': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error')) }): ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]}, @@ -408,10 +412,21 @@ def test_stopped(self): 'comment': 'No changes had happened'}) self.assertDictEqual(virt.stopped('myvm'), ret) + # Case there the domain doesn't exist with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'}) self.assertDictEqual(virt.stopped('myvm'), ret) + # Case where the domain is already stopped + with patch.dict(virt.__salt__, { # pylint: disable=no-member + 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), + 'virt.vm_state': MagicMock(return_value={'myvm': 'shutdown'}) + }): + ret.update({'changes': {}, + 'result': True, + 'comment': 'No changes had happened'}) + self.assertDictEqual(virt.stopped('myvm'), ret) + def test_powered_off(self): ''' powered_off state test cases. @@ -421,8 +436,11 @@ def test_powered_off(self): 'result': True} stop_mock = MagicMock(return_value=True) + + # Normal case with patch.dict(virt.__salt__, { # pylint: disable=no-member 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), + 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), 'virt.stop': stop_mock }): ret.update({'changes': { @@ -432,8 +450,10 @@ def test_powered_off(self): self.assertDictEqual(virt.powered_off('myvm'), ret) stop_mock.assert_called_with('myvm', connection=None, username=None, password=None) + # Normal case with user-provided connection parameters with patch.dict(virt.__salt__, { # pylint: disable=no-member 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), + 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), 'virt.stop': stop_mock, }): self.assertDictEqual(virt.powered_off('myvm', @@ -442,8 +462,10 @@ def test_powered_off(self): password='secret'), ret) stop_mock.assert_called_with('myvm', connection='myconnection', username='user', password='secret') + # Case where an error occurred during the poweroff with patch.dict(virt.__salt__, { # pylint: disable=no-member 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), + 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), 'virt.stop': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error')) }): ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]}, @@ -451,10 +473,21 @@ def test_powered_off(self): 'comment': 'No changes had happened'}) self.assertDictEqual(virt.powered_off('myvm'), ret) + # Case there the domain doesn't exist with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'}) self.assertDictEqual(virt.powered_off('myvm'), ret) + # Case where the domain is already stopped + with patch.dict(virt.__salt__, { # pylint: disable=no-member + 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), + 'virt.vm_state': MagicMock(return_value={'myvm': 'shutdown'}) + }): + ret.update({'changes': {}, + 'result': True, + 'comment': 'No changes had happened'}) + self.assertDictEqual(virt.powered_off('myvm'), ret) + def test_snapshot(self): ''' snapshot state test cases. @@ -573,7 +606,7 @@ def test_network_running(self): define_mock.assert_called_with('mynet', 'br2', 'bridge', - 'openvswitch', + vport='openvswitch', tag=180, autostart=False, start=True, @@ -582,7 +615,7 @@ def test_network_running(self): password='secret') with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.network_info': MagicMock(return_value={'active': True}), + 'virt.network_info': MagicMock(return_value={'mynet': {'active': True}}), 'virt.network_define': define_mock, }): ret.update({'changes': {}, 'comment': 'Network mynet exists and is running'}) @@ -590,7 +623,7 @@ def test_network_running(self): start_mock = MagicMock(return_value=True) with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.network_info': MagicMock(return_value={'active': False}), + 'virt.network_info': MagicMock(return_value={'mynet': {'active': False}}), 'virt.network_start': start_mock, 'virt.network_define': define_mock, }): @@ -666,10 +699,13 @@ def test_pool_running(self): connection='myconnection', username='user', password='secret') - mocks['start'].assert_not_called() + mocks['start'].assert_called_with('mypool', + connection='myconnection', + username='user', + password='secret') with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.pool_info': MagicMock(return_value={'state': 'running'}), + 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'running'}}), }): ret.update({'changes': {}, 'comment': 'Pool mypool exists and is running'}) self.assertDictEqual(virt.pool_running('mypool', @@ -680,7 +716,7 @@ def test_pool_running(self): for mock in mocks: mocks[mock].reset_mock() with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.pool_info': MagicMock(return_value={'state': 'stopped'}), + 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'stopped'}}), 'virt.pool_build': mocks['build'], 'virt.pool_start': mocks['start'] }): @@ -701,3 +737,111 @@ def test_pool_running(self): ptype='logical', target='/dev/base', source={'devices': [{'path': '/dev/sda'}]}), ret) + + def test_pool_deleted(self): + ''' + Test the pool_deleted state + ''' + # purge=False test case, stopped pool + with patch.dict(virt.__salt__, { + 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'stopped', 'type': 'dir'}}), + 'virt.pool_undefine': MagicMock(return_value=True) + }): + expected = { + 'name': 'test01', + 'changes': { + 'stopped': False, + 'deleted_volumes': [], + 'deleted': False, + 'undefined': True, + }, + 'result': True, + 'comment': '', + } + + with patch.dict(virt.__opts__, {'test': False}): + self.assertDictEqual(expected, virt.pool_deleted('test01')) + + with patch.dict(virt.__opts__, {'test': True}): + expected['result'] = None + self.assertDictEqual(expected, virt.pool_deleted('test01')) + + # purge=False test case + with patch.dict(virt.__salt__, { + 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'running', 'type': 'dir'}}), + 'virt.pool_undefine': MagicMock(return_value=True), + 'virt.pool_stop': MagicMock(return_value=True) + }): + expected = { + 'name': 'test01', + 'changes': { + 'stopped': True, + 'deleted_volumes': [], + 'deleted': False, + 'undefined': True, + }, + 'result': True, + 'comment': '', + } + + with patch.dict(virt.__opts__, {'test': False}): + self.assertDictEqual(expected, virt.pool_deleted('test01')) + + with patch.dict(virt.__opts__, {'test': True}): + expected['result'] = None + self.assertDictEqual(expected, virt.pool_deleted('test01')) + + # purge=True test case + + with patch.dict(virt.__salt__, { + 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'running', 'type': 'dir'}}), + 'virt.pool_list_volumes': MagicMock(return_value=['vm01.qcow2', 'vm02.qcow2']), + 'virt.pool_refresh': MagicMock(return_value=True), + 'virt.volume_delete': MagicMock(return_value=True), + 'virt.pool_stop': MagicMock(return_value=True), + 'virt.pool_delete': MagicMock(return_value=True), + 'virt.pool_undefine': MagicMock(return_value=True) + }): + expected = { + 'name': 'test01', + 'changes': { + 'stopped': True, + 'deleted_volumes': ['vm01.qcow2', 'vm02.qcow2'], + 'deleted': True, + 'undefined': True, + }, + 'result': True, + 'comment': '', + } + + with patch.dict(virt.__opts__, {'test': False}): + self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True)) + + with patch.dict(virt.__opts__, {'test': True}): + expected['result'] = None + self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True)) + + # Case of backend not unsupporting delete operations + with patch.dict(virt.__salt__, { + 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'running', 'type': 'iscsi'}}), + 'virt.pool_stop': MagicMock(return_value=True), + 'virt.pool_undefine': MagicMock(return_value=True) + }): + expected = { + 'name': 'test01', + 'changes': { + 'stopped': True, + 'deleted_volumes': [], + 'deleted': False, + 'undefined': True, + }, + 'result': True, + 'comment': 'Unsupported actions for pool of type "iscsi": deleting volume, deleting pool', + } + + with patch.dict(virt.__opts__, {'test': False}): + self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True)) + + with patch.dict(virt.__opts__, {'test': True}): + expected['result'] = None + self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True)) diff --git a/tests/unit/states/test_virtualenv_mod.py b/tests/unit/states/test_virtualenv_mod.py index e0721c1565fe..20472714f3ef 100644 --- a/tests/unit/states/test_virtualenv_mod.py +++ b/tests/unit/states/test_virtualenv_mod.py @@ -9,19 +9,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.virtualenv_mod as virtualenv_mod -@skipIf(NO_MOCK, NO_MOCK_REASON) class VirtualenvModTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the virtualenv_mod state diff --git a/tests/unit/states/test_webutil.py b/tests/unit/states/test_webutil.py index 5e9af2204832..1b0d1ccca800 100644 --- a/tests/unit/states/test_webutil.py +++ b/tests/unit/states/test_webutil.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.webutil as htpasswd -@skipIf(NO_MOCK, NO_MOCK_REASON) class HtpasswdTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.webutil diff --git a/tests/unit/states/test_win_dism.py b/tests/unit/states/test_win_dism.py index ed69743fa32c..28fc3f0df625 100644 --- a/tests/unit/states/test_win_dism.py +++ b/tests/unit/states/test_win_dism.py @@ -26,7 +26,7 @@ def test_capability_installed(self): ''' expected = { 'comment': "Installed Capa2", - 'changes': {'capability': {'new': 'Capa2'}, + 'changes': {'capability': {'new': ['Capa2']}, 'retcode': 0}, 'name': 'Capa2', 'result': True} @@ -106,7 +106,7 @@ def test_capability_removed(self): ''' expected = { 'comment': "Removed Capa2", - 'changes': {'capability': {'old': 'Capa2'}, + 'changes': {'capability': {'old': ['Capa2']}, 'retcode': 0}, 'name': 'Capa2', 'result': True} @@ -184,7 +184,7 @@ def test_feature_installed(self): ''' expected = { 'comment': "Installed Feat2", - 'changes': {'feature': {'new': 'Feat2'}, + 'changes': {'feature': {'new': ['Feat2']}, 'retcode': 0}, 'name': 'Feat2', 'result': True} @@ -263,7 +263,7 @@ def test_feature_removed(self): ''' expected = { 'comment': "Removed Feat2", - 'changes': {'feature': {'old': 'Feat2'}, + 'changes': {'feature': {'old': ['Feat2']}, 'retcode': 0}, 'name': 'Feat2', 'result': True} @@ -342,7 +342,7 @@ def test_package_installed(self): ''' expected = { 'comment': "Installed Pack2", - 'changes': {'package': {'new': 'Pack2'}, + 'changes': {'package': {'new': ['Pack2']}, 'retcode': 0}, 'name': 'Pack2', 'result': True} @@ -434,7 +434,7 @@ def test_package_removed(self): ''' expected = { 'comment': "Removed Pack2", - 'changes': {'package': {'old': 'Pack2'}, + 'changes': {'package': {'old': ['Pack2']}, 'retcode': 0}, 'name': 'Pack2', 'result': True} diff --git a/tests/unit/states/test_win_dns_client.py b/tests/unit/states/test_win_dns_client.py index a8552c046770..20d22cd30f1c 100644 --- a/tests/unit/states/test_win_dns_client.py +++ b/tests/unit/states/test_win_dns_client.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.win_dns_client as win_dns_client -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinDnsClientTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the win_dns_client state diff --git a/tests/unit/states/test_win_iis.py b/tests/unit/states/test_win_iis.py new file mode 100644 index 000000000000..ab3c74f4a91c --- /dev/null +++ b/tests/unit/states/test_win_iis.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +''' + :synopsis: Unit Tests for Windows iis Module 'state.win_iis' + :platform: Windows + .. versionadded:: 2019.2.2 +''' + +# Import Python Libs +from __future__ import absolute_import, unicode_literals, print_function + +# Import Salt Libs +import salt.states.win_iis as win_iis + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase +from tests.support.mock import ( + MagicMock, + patch +) + + +class WinIisTestCase(TestCase, LoaderModuleMockMixin): + ''' + Test cases for salt.states.win_pki + ''' + + def setup_loader_modules(self): + return {win_iis: {}} + + def __base_webconfiguration_ret(self, comment='', changes=None, name='', result=None): + return { + 'name': name, + 'changes': changes if changes else {}, + 'comment': comment, + 'result': result, + } + + def test_webconfiguration_settings_no_settings(self): + name = 'IIS' + settings = {} + expected_ret = self.__base_webconfiguration_ret(name=name, comment='No settings to change provided.', + result=True) + actual_ret = win_iis.webconfiguration_settings(name, settings) + self.assertEqual(expected_ret, actual_ret) + + def test_webconfiguration_settings_collection_failure(self): + name = 'IIS:\\' + settings = { + 'system.applicationHost/sites': { + 'Collection[{name: site0}].logFile.directory': 'C:\\logs\\iis\\site0', + }, + } + old_settings = [ + {'filter': 'system.applicationHost/sites', 'name': 'Collection[{name: site0}].logFile.directory', + 'value': 'C:\\logs\\iis\\old_site'}] + current_settings = old_settings + new_settings = old_settings + expected_ret = self.__base_webconfiguration_ret( + name=name, + result=False, + changes={ + 'changes': {old_settings[0]['filter'] + '.' + old_settings[0]['name']: { + 'old': old_settings[0]['value'], + 'new': settings[old_settings[0]['filter']][old_settings[0]['name']], + }}, + 'failures': {old_settings[0]['filter'] + '.' + old_settings[0]['name']: { + 'old': old_settings[0]['value'], + 'new': new_settings[0]['value'], + }}, + }, + comment='Some settings failed to change.' + ) + with patch.dict(win_iis.__salt__, { + 'win_iis.get_webconfiguration_settings': MagicMock( + side_effect=[old_settings, current_settings, new_settings]), + 'win_iis.set_webconfiguration_settings': MagicMock(return_value=True), + }), patch.dict(win_iis.__opts__, {'test': False}): + actual_ret = win_iis.webconfiguration_settings(name, settings) + self.assertEqual(expected_ret, actual_ret) + + def test_webconfiguration_settings_collection(self): + name = 'IIS:\\' + settings = { + 'system.applicationHost/sites': { + 'Collection[{name: site0}].logFile.directory': 'C:\\logs\\iis\\site0', + }, + } + old_settings = [ + {'filter': 'system.applicationHost/sites', 'name': 'Collection[{name: site0}].logFile.directory', + 'value': 'C:\\logs\\iis\\old_site'}] + current_settings = [ + {'filter': 'system.applicationHost/sites', 'name': 'Collection[{name: site0}].logFile.directory', + 'value': 'C:\\logs\\iis\\site0'}] + new_settings = current_settings + expected_ret = self.__base_webconfiguration_ret( + name=name, + result=True, + changes={old_settings[0]['filter'] + '.' + old_settings[0]['name']: { + 'old': old_settings[0]['value'], + 'new': new_settings[0]['value'], + }}, + comment='Set settings to contain the provided values.' + ) + with patch.dict(win_iis.__salt__, { + 'win_iis.get_webconfiguration_settings': MagicMock( + side_effect=[old_settings, current_settings, new_settings]), + 'win_iis.set_webconfiguration_settings': MagicMock(return_value=True), + }), patch.dict(win_iis.__opts__, {'test': False}): + actual_ret = win_iis.webconfiguration_settings(name, settings) + self.assertEqual(expected_ret, actual_ret) diff --git a/tests/unit/states/test_win_network.py b/tests/unit/states/test_win_network.py index a7cf0b21d2e9..03a047ecb328 100644 --- a/tests/unit/states/test_win_network.py +++ b/tests/unit/states/test_win_network.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.win_network as win_network -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinNetworkTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the nftables state diff --git a/tests/unit/states/test_win_path.py b/tests/unit/states/test_win_path.py index 6f32fb422bdc..027af1e7cdb8 100644 --- a/tests/unit/states/test_win_path.py +++ b/tests/unit/states/test_win_path.py @@ -9,13 +9,11 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( Mock, MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -24,7 +22,6 @@ NAME = 'salt' -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinPathTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the win_path state diff --git a/tests/unit/states/test_win_pki.py b/tests/unit/states/test_win_pki.py index ea07bbd2356a..de0b996eb399 100644 --- a/tests/unit/states/test_win_pki.py +++ b/tests/unit/states/test_win_pki.py @@ -14,12 +14,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, ) CERT_PATH = r'C:\certs\testdomain.local.cer' @@ -37,7 +35,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinPkiTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.win_pki diff --git a/tests/unit/states/test_win_powercfg.py b/tests/unit/states/test_win_powercfg.py index 41e409a46f3e..083a2c618765 100644 --- a/tests/unit/states/test_win_powercfg.py +++ b/tests/unit/states/test_win_powercfg.py @@ -8,16 +8,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PowerCfgTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the powercfg state diff --git a/tests/unit/states/test_win_servermanager.py b/tests/unit/states/test_win_servermanager.py index 77fb3c1a0f31..c14b03fc99a6 100644 --- a/tests/unit/states/test_win_servermanager.py +++ b/tests/unit/states/test_win_servermanager.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.win_servermanager as win_servermanager -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinServermanagerTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the win_servermanager state diff --git a/tests/unit/states/test_win_snmp.py b/tests/unit/states/test_win_snmp.py index f2f69d802b1a..a62d12eeeedf 100644 --- a/tests/unit/states/test_win_snmp.py +++ b/tests/unit/states/test_win_snmp.py @@ -15,16 +15,13 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinSnmpTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.win_snmp diff --git a/tests/unit/states/test_win_system.py b/tests/unit/states/test_win_system.py index f18ea32baa6c..835618581311 100644 --- a/tests/unit/states/test_win_system.py +++ b/tests/unit/states/test_win_system.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.win_system as win_system -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinSystemTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the win_system state diff --git a/tests/unit/states/test_winrepo.py b/tests/unit/states/test_winrepo.py index 16796c901a68..7495d7c89112 100644 --- a/tests/unit/states/test_winrepo.py +++ b/tests/unit/states/test_winrepo.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -51,7 +49,6 @@ def cmd(arg1, arg2): return [] -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinrepoTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the winrepo state diff --git a/tests/unit/states/test_xmpp.py b/tests/unit/states/test_xmpp.py index ee33587a359a..1c7bab91ec30 100644 --- a/tests/unit/states/test_xmpp.py +++ b/tests/unit/states/test_xmpp.py @@ -8,19 +8,16 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs import salt.states.xmpp as xmpp -@skipIf(NO_MOCK, NO_MOCK_REASON) class XmppTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the xmpp state diff --git a/tests/unit/states/test_zabbix_action.py b/tests/unit/states/test_zabbix_action.py index 757b6ba5885c..31e0dc62097e 100644 --- a/tests/unit/states/test_zabbix_action.py +++ b/tests/unit/states/test_zabbix_action.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) import salt.states.zabbix_action as zabbix_action @@ -70,7 +68,6 @@ 'actionid': '28'} -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZabbixActionTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.zabbix diff --git a/tests/unit/states/test_zabbix_template.py b/tests/unit/states/test_zabbix_template.py index 27112db52e18..f95918f9fd21 100644 --- a/tests/unit/states/test_zabbix_template.py +++ b/tests/unit/states/test_zabbix_template.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) import salt.states.zabbix_template as zabbix_template @@ -49,7 +47,6 @@ DIFF_PARAMS = {'old': {}, 'new': {'macros': [], 'templateid': '10206'}} -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZabbixTemplateTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.zabbix diff --git a/tests/unit/states/test_zabbix_valuemap.py b/tests/unit/states/test_zabbix_valuemap.py index fe0d7207f08b..58652ab1ec19 100644 --- a/tests/unit/states/test_zabbix_valuemap.py +++ b/tests/unit/states/test_zabbix_valuemap.py @@ -9,12 +9,10 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) import salt.states.zabbix_valuemap as zabbix_valuemap @@ -34,7 +32,6 @@ {'newvalue': 'Failure', 'value': '1'}]} -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZabbixActionTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.modules.zabbix diff --git a/tests/unit/states/test_zcbuildout.py b/tests/unit/states/test_zcbuildout.py index 064f07d8249f..67cb1729f39d 100644 --- a/tests/unit/states/test_zcbuildout.py +++ b/tests/unit/states/test_zcbuildout.py @@ -5,9 +5,9 @@ import os # Import Salt Testing libs -from tests.support.paths import FILES -from tests.support.unit import skipIf from tests.support.helpers import requires_network +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf # Import Salt libs import salt.utils.path @@ -16,8 +16,6 @@ import salt.states.zcbuildout as buildout import salt.modules.cmdmod as cmd -ROOT = os.path.join(FILES, 'file/base/buildout') - @skipIf(salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES) is None, "The 'virtualenv' packaged needs to be installed") @@ -36,26 +34,28 @@ def setup_loader_modules(self): } return {buildout: module_globals, modbuildout: module_globals} + # I don't have the time to invest in learning more about buildout, + # and given we don't have support yet, and there are other priorities + # I'm going to punt on this for now - WW @requires_network() + @skipIf(True, "Buildout is still in beta. Test needs fixing.") def test_quiet(self): c_dir = os.path.join(self.tdir, 'c') - cret = buildout.installed(c_dir, python=self.py_st, quiet=True) - self.assertTrue(cret['result']) - self.assertFalse('OUTPUT:' in cret['comment']) - self.assertFalse('Log summary:' in cret['comment']) + assert False, os.listdir(self.rdir) + modbuildout.upgrade_bootstrap(c_dir) + cret = buildout.installed(c_dir, python=self.py_st) + self.assertFalse('OUTPUT:' in cret['comment'], cret['comment']) + self.assertFalse('Log summary:' in cret['comment'], cret['comment']) + self.assertTrue(cret['result'], cret['comment']) @requires_network() def test_error(self): b_dir = os.path.join(self.tdir, 'e') ret = buildout.installed(b_dir, python=self.py_st) self.assertTrue( - 'We did not get any expectable ' - 'answer from buildout' - in ret['comment']) - self.assertTrue( - 'An internal error occurred due to a bug in' - ' either zc.buildout ' - in ret['comment']) + 'We did not get any expectable answer from buildout' + in ret['comment'] + ) self.assertFalse(ret['result']) @requires_network() @@ -63,17 +63,17 @@ def test_installed(self): b_dir = os.path.join(self.tdir, 'b') ret = buildout.installed(b_dir, python=self.py_st, - onlyif='/bin/false') + onlyif=RUNTIME_VARS.SHELL_FALSE_PATH) self.assertEqual(ret['comment'], '\nonlyif condition is false') self.assertEqual(ret['result'], True) - self.assertTrue('/b' in ret['name']) + self.assertTrue(os.sep + 'b' in ret['name']) b_dir = os.path.join(self.tdir, 'b') ret = buildout.installed(b_dir, python=self.py_st, - unless='/bin/true') + unless=RUNTIME_VARS.SHELL_TRUE_PATH) self.assertEqual(ret['comment'], '\nunless condition is true') self.assertEqual(ret['result'], True) - self.assertTrue('/b' in ret['name']) + self.assertTrue(os.sep + 'b' in ret['name']) ret = buildout.installed(b_dir, python=self.py_st) self.assertEqual(ret['result'], True) self.assertTrue('OUTPUT:' in ret['comment']) diff --git a/tests/unit/states/test_zfs.py b/tests/unit/states/test_zfs.py index 421db9225e5d..6504bebb19ac 100644 --- a/tests/unit/states/test_zfs.py +++ b/tests/unit/states/test_zfs.py @@ -12,17 +12,13 @@ from __future__ import absolute_import, unicode_literals, print_function # Import Salt Testing Libs +from tests.support.zfs import ZFSMockData from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) -# Import test data from salt.utils.zfs test -from tests.unit.utils.test_zfs import utils_patch - # Import Salt Execution module to test import salt.utils.zfs import salt.states.zfs as zfs @@ -32,13 +28,15 @@ from salt.utils.odict import OrderedDict -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZfsTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.zfs ''' def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() + self.utils_patch = ZFSMockData().get_patched_utils() + for key in ('opts', 'utils_patch'): + self.addCleanup(delattr, self, key) utils = salt.loader.utils(opts, whitelist=['zfs']) zfs_obj = { zfs: { @@ -61,7 +59,7 @@ def test_filesystem_absent_nofs(self): mock_exists = MagicMock(return_value=False) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.filesystem_absent('myzpool/filesystem')) def test_filesystem_absent_removed(self): @@ -77,7 +75,7 @@ def test_filesystem_absent_removed(self): mock_destroy = MagicMock(return_value=OrderedDict([('destroyed', True)])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.filesystem_absent('myzpool/filesystem')) def test_filesystem_absent_fail(self): @@ -104,7 +102,7 @@ def test_filesystem_absent_fail(self): ])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.filesystem_absent('myzpool/filesystem')) def test_volume_absent_novol(self): @@ -118,7 +116,7 @@ def test_volume_absent_novol(self): mock_exists = MagicMock(return_value=False) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.volume_absent('myzpool/volume')) def test_volume_absent_removed(self): @@ -134,7 +132,7 @@ def test_volume_absent_removed(self): mock_destroy = MagicMock(return_value=OrderedDict([('destroyed', True)])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.volume_absent('myzpool/volume')) def test_volume_absent_fail(self): @@ -161,7 +159,7 @@ def test_volume_absent_fail(self): ])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.volume_absent('myzpool/volume')) def test_snapshot_absent_nosnap(self): @@ -175,7 +173,7 @@ def test_snapshot_absent_nosnap(self): mock_exists = MagicMock(return_value=False) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.snapshot_absent('myzpool/filesystem@snap')) def test_snapshot_absent_removed(self): @@ -191,7 +189,7 @@ def test_snapshot_absent_removed(self): mock_destroy = MagicMock(return_value=OrderedDict([('destroyed', True)])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.snapshot_absent('myzpool/filesystem@snap')) def test_snapshot_absent_fail(self): @@ -210,7 +208,7 @@ def test_snapshot_absent_fail(self): ])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.snapshot_absent('myzpool/filesystem@snap')) def test_bookmark_absent_nobook(self): @@ -224,7 +222,7 @@ def test_bookmark_absent_nobook(self): mock_exists = MagicMock(return_value=False) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.bookmark_absent('myzpool/filesystem#book')) def test_bookmark_absent_removed(self): @@ -240,7 +238,7 @@ def test_bookmark_absent_removed(self): mock_destroy = MagicMock(return_value=OrderedDict([('destroyed', True)])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.bookmark_absent('myzpool/filesystem#book')) def test_hold_absent_nohold(self): @@ -254,7 +252,7 @@ def test_hold_absent_nohold(self): mock_holds = MagicMock(return_value=OrderedDict([])) with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.hold_absent('myhold', 'myzpool/filesystem@snap')) def test_hold_absent_removed(self): @@ -274,7 +272,7 @@ def test_hold_absent_removed(self): mock_release = MagicMock(return_value=OrderedDict([('released', True)])) with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ patch.dict(zfs.__salt__, {'zfs.release': mock_release}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.hold_absent('myhold', 'myzpool/filesystem@snap')) def test_hold_absent_fail(self): @@ -290,7 +288,7 @@ def test_hold_absent_fail(self): ('error', "cannot open 'myzpool/filesystem@snap': dataset does not exist"), ])) with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.hold_absent('myhold', 'myzpool/filesystem@snap')) def test_hold_present(self): @@ -304,7 +302,7 @@ def test_hold_present(self): mock_holds = MagicMock(return_value=OrderedDict([('myhold', 'Thu Feb 15 16:24 2018')])) with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.hold_present('myhold', 'myzpool/filesystem@snap')) def test_hold_present_new(self): @@ -320,7 +318,7 @@ def test_hold_present_new(self): mock_hold = MagicMock(return_value=OrderedDict([('held', True)])) with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ patch.dict(zfs.__salt__, {'zfs.hold': mock_hold}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.hold_present('myhold', 'myzpool/filesystem@snap')) def test_hold_present_fail(self): @@ -339,7 +337,7 @@ def test_hold_present_fail(self): ])) with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ patch.dict(zfs.__salt__, {'zfs.hold': mock_hold}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.hold_present('myhold', 'myzpool/filesystem@snap')) def test_filesystem_present(self): @@ -364,7 +362,7 @@ def test_filesystem_present(self): ])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.filesystem_present('myzpool/filesystem')) def test_filesystem_present_new(self): @@ -380,7 +378,7 @@ def test_filesystem_present_new(self): mock_create = MagicMock(return_value=OrderedDict([('created', True)])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.create': mock_create}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.filesystem_present('myzpool/filesystem')) def test_filesystem_present_update(self): @@ -407,7 +405,7 @@ def test_filesystem_present_update(self): with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ patch.dict(zfs.__salt__, {'zfs.set': mock_set}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.filesystem_present( name='myzpool/filesystem', properties={'compression': 'lz4'}, @@ -429,7 +427,7 @@ def test_filesystem_present_fail(self): ])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.create': mock_create}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.filesystem_present('myzpool/filesystem')) def test_volume_present(self): @@ -454,7 +452,7 @@ def test_volume_present(self): ])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.volume_present('myzpool/volume', volume_size='1G')) def test_volume_present_new(self): @@ -470,7 +468,7 @@ def test_volume_present_new(self): mock_create = MagicMock(return_value=OrderedDict([('created', True)])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.create': mock_create}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.volume_present('myzpool/volume', volume_size='1G')) def test_volume_present_update(self): @@ -497,7 +495,7 @@ def test_volume_present_update(self): with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ patch.dict(zfs.__salt__, {'zfs.set': mock_set}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.volume_present( name='myzpool/volume', volume_size='1G', @@ -520,7 +518,7 @@ def test_volume_present_fail(self): ])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.create': mock_create}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.volume_present('myzpool/volume', volume_size='1G')) def test_bookmark_present(self): @@ -534,7 +532,7 @@ def test_bookmark_present(self): mock_exists = MagicMock(return_value=True) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.bookmark_present('mybookmark', 'myzpool/filesystem@snap')) def test_bookmark_present_new(self): @@ -550,7 +548,7 @@ def test_bookmark_present_new(self): mock_bookmark = MagicMock(return_value=OrderedDict([('bookmarked', True)])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.bookmark': mock_bookmark}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.bookmark_present('mybookmark', 'myzpool/filesystem@snap')) def test_bookmark_present_fail(self): @@ -569,7 +567,7 @@ def test_bookmark_present_fail(self): ])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.bookmark': mock_bookmark}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.bookmark_present('mybookmark', 'myzpool/filesystem@snap')) def test_snapshot_present(self): @@ -583,7 +581,7 @@ def test_snapshot_present(self): mock_exists = MagicMock(return_value=True) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.snapshot_present('myzpool/filesystem@snap')) def test_snapshot_present_new(self): @@ -599,7 +597,7 @@ def test_snapshot_present_new(self): mock_snapshot = MagicMock(return_value=OrderedDict([('snapshotted', True)])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.snapshot': mock_snapshot}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.snapshot_present('myzpool/filesystem@snap')) def test_snapshot_present_fail(self): @@ -618,7 +616,7 @@ def test_snapshot_present_fail(self): ])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.snapshot': mock_snapshot}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.snapshot_present('myzpool/filesystem@snap')) def test_propmoted(self): @@ -640,7 +638,7 @@ def test_propmoted(self): ])) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.promoted('myzpool/filesystem')) def test_propmoted_clone(self): @@ -664,7 +662,7 @@ def test_propmoted_clone(self): with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ patch.dict(zfs.__salt__, {'zfs.promote': mock_promote}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.promoted('myzpool/filesystem')) def test_propmoted_fail(self): @@ -678,7 +676,7 @@ def test_propmoted_fail(self): mock_exists = MagicMock(return_value=False) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.promoted('myzpool/filesystem')) def test_scheduled_snapshot_fail(self): @@ -692,5 +690,5 @@ def test_scheduled_snapshot_fail(self): mock_exists = MagicMock(return_value=False) with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, utils_patch): + patch.dict(zfs.__utils__, self.utils_patch): self.assertEqual(ret, zfs.scheduled_snapshot('myzpool/filesystem', 'shadow', schedule={'hour': 6})) diff --git a/tests/unit/states/test_zk_concurrency.py b/tests/unit/states/test_zk_concurrency.py index 278320b541d8..0572e7e1c908 100644 --- a/tests/unit/states/test_zk_concurrency.py +++ b/tests/unit/states/test_zk_concurrency.py @@ -7,10 +7,8 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -18,7 +16,6 @@ import salt.states.zk_concurrency as zk_concurrency -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZkConcurrencyTestCase(TestCase, LoaderModuleMockMixin): ''' Validate the zk_concurrency state diff --git a/tests/unit/states/test_zpool.py b/tests/unit/states/test_zpool.py index 79621149d577..52cc544d100b 100644 --- a/tests/unit/states/test_zpool.py +++ b/tests/unit/states/test_zpool.py @@ -12,17 +12,13 @@ from __future__ import absolute_import, unicode_literals, print_function # Import Salt Testing Libs +from tests.support.zfs import ZFSMockData from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) -# Import test data from salt.utils.zfs test -from tests.unit.utils.test_zfs import utils_patch - # Import Salt Execution module to test import salt.utils.zfs import salt.states.zpool as zpool @@ -32,13 +28,15 @@ from salt.utils.odict import OrderedDict -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZpoolTestCase(TestCase, LoaderModuleMockMixin): ''' Test cases for salt.states.zpool ''' def setup_loader_modules(self): - self.opts = opts = salt.config.DEFAULT_MINION_OPTS + self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() + self.utils_patch = ZFSMockData().get_patched_utils() + for key in ('opts', 'utils_patch'): + self.addCleanup(delattr, self, key) utils = salt.loader.utils(opts, whitelist=['zfs']) zpool_obj = { zpool: { @@ -61,7 +59,7 @@ def test_absent_without_pool(self): mock_exists = MagicMock(return_value=False) with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual(zpool.absent('myzpool'), ret) def test_absent_destroy_pool(self): @@ -81,7 +79,7 @@ def test_absent_destroy_pool(self): ])) with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'zpool.destroy': mock_destroy}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual(zpool.absent('myzpool'), ret) def test_absent_exporty_pool(self): @@ -101,7 +99,7 @@ def test_absent_exporty_pool(self): ])) with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'zpool.export': mock_destroy}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual(zpool.absent('myzpool', export=True), ret) def test_absent_busy(self): @@ -128,7 +126,7 @@ def test_absent_busy(self): ])) with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'zpool.export': mock_destroy}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual(zpool.absent('myzpool', export=True), ret) def test_present_import_success(self): @@ -150,7 +148,7 @@ def test_present_import_success(self): ])) with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'zpool.import': mock_import}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual(zpool.present('myzpool', config=config), ret) def test_present_import_fail(self): @@ -172,7 +170,7 @@ def test_present_import_fail(self): ])) with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'zpool.import': mock_import}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual(zpool.present('myzpool', config=config), ret) def test_present_create_success(self): @@ -208,7 +206,7 @@ def test_present_create_success(self): ])) with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'zpool.create': mock_create}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual( zpool.present( 'myzpool', @@ -235,7 +233,7 @@ def test_present_create_fail(self): mock_exists = MagicMock(return_value=False) with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual(zpool.present('myzpool', config=config), ret) def test_present_create_passthrough_fail(self): @@ -279,7 +277,7 @@ def test_present_create_passthrough_fail(self): ])) with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'zpool.create': mock_create}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual( zpool.present( 'myzpool', @@ -361,7 +359,7 @@ def test_present_update_success(self): with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'zpool.get': mock_get}), \ patch.dict(zpool.__salt__, {'zpool.set': mock_set}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual( zpool.present( 'myzpool', @@ -438,7 +436,7 @@ def test_present_update_nochange_success(self): ])) with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ patch.dict(zpool.__salt__, {'zpool.get': mock_get}), \ - patch.dict(zpool.__utils__, utils_patch): + patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual( zpool.present( 'myzpool', diff --git a/tests/unit/test_auth.py b/tests/unit/test_auth.py index 54c3915144d8..11d34f73be1b 100644 --- a/tests/unit/test_auth.py +++ b/tests/unit/test_auth.py @@ -10,7 +10,7 @@ # Import Salt Testing libs from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, call, NO_MOCK, NO_MOCK_REASON, MagicMock +from tests.support.mock import patch, call, MagicMock # Import Salt libraries import salt.master @@ -20,7 +20,6 @@ import salt.utils.platform -@skipIf(NO_MOCK, NO_MOCK_REASON) class LoadAuthTestCase(TestCase): def setUp(self): # pylint: disable=W0221 diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 08f27f5bd5bf..c6ec5ee2dd17 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -7,9 +7,9 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs -import tests.integration as integration +from tests.support.mixins import SaltClientTestCaseMixin +from tests.support.mock import patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON # Import Salt libs from salt import client @@ -19,9 +19,8 @@ ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class LocalClientTestCase(TestCase, - integration.SaltClientTestCaseMixin): + SaltClientTestCaseMixin): def test_create_local_client(self): local_client = client.LocalClient(mopts=self.get_temp_config('master')) diff --git a/tests/unit/test_cloud.py b/tests/unit/test_cloud.py index 37a1d00595fd..cbf01181003e 100644 --- a/tests/unit/test_cloud.py +++ b/tests/unit/test_cloud.py @@ -5,18 +5,16 @@ # Import Python libs from __future__ import absolute_import +import os # Import Salt Testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch, - NO_MOCK, - NO_MOCK_REASON, -) +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.cloud +import salt.config EXAMPLE_PROVIDERS = { 'nyc_vcenter': {'vmware': {'driver': 'vmware', @@ -101,7 +99,6 @@ } -@skipIf(NO_MOCK, NO_MOCK_REASON) class MapConfTest(TestCase): ''' Validate evaluation of salt-cloud map configuration @@ -114,9 +111,12 @@ def test_cloud_map_merge_conf(self): with patch('salt.config.check_driver_dependencies', MagicMock(return_value=True)), \ patch('salt.cloud.Map.read', MagicMock(return_value=EXAMPLE_MAP)): self.maxDiff = None - opts = {'extension_modules': '/var/cache/salt/master/extmods', - 'optimization_order': [0, 1, 2], - 'providers': EXAMPLE_PROVIDERS, 'profiles': EXAMPLE_PROFILES} + opts = salt.config.cloud_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'cloud')) + opts.update({ + 'optimization_order': [0, 1, 2], + 'providers': EXAMPLE_PROVIDERS, + 'profiles': EXAMPLE_PROFILES + }) cloud_map = salt.cloud.Map(opts) merged_profile = { diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index 3b7fa32efc47..3bc42624dd74 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -10,13 +10,11 @@ import textwrap # Import Salt Testing libs -from tests.support.helpers import with_tempdir, with_tempfile, destructiveTest +from tests.support.helpers import with_tempdir, with_tempfile, patched_environ from tests.support.mixins import AdaptedConfigurationTestCaseMixin -from tests.support.paths import TMP from tests.support.unit import skipIf, TestCase +from tests.support.runtests import RUNTIME_VARS from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, Mock, MagicMock, patch @@ -32,7 +30,6 @@ import salt.utils.yaml from salt.ext import six from salt.syspaths import CONFIG_DIR -from salt import config as sconfig from salt.exceptions import ( CommandExecutionError, SaltConfigurationError, @@ -41,8 +38,7 @@ log = logging.getLogger(__name__) -SAMPLE_CONF_DIR = os.path.dirname(os.path.realpath(__file__)).split('tests')[0] + 'conf/' - +SAMPLE_CONF_DIR = os.path.join(RUNTIME_VARS.CODE_DIR, 'conf') + os.sep # mock hostname should be more complex than the systems FQDN MOCK_HOSTNAME = 'very.long.complex.fqdn.that.is.crazy.extra.long.example.com' @@ -62,26 +58,23 @@ fe00::0 ip6-localnet ff00::0 ip6-mcastprefix '''.format(hostname=MOCK_HOSTNAME)) -MOCK_ETC_HOSTNAME = '{0}\n'.format(MOCK_HOSTNAME) +MOCK_ETC_HOSTNAME = '{}\n'.format(MOCK_HOSTNAME) PATH = 'path/to/some/cloud/conf/file' DEFAULT = {'default_include': PATH} -MOCK_MASTER_DEFAULT_OPTS = { - 'log_file': '{0}/var/log/salt/master'.format(salt.syspaths.ROOT_DIR), - 'pidfile': '{0}/var/run/salt-master.pid'.format(salt.syspaths.ROOT_DIR), - 'root_dir': format(salt.syspaths.ROOT_DIR) -} -if salt.utils.platform.is_windows(): - MOCK_MASTER_DEFAULT_OPTS = { - 'log_file': '{0}\\var\\log\\salt\\master'.format( - salt.syspaths.ROOT_DIR), - 'pidfile': '{0}\\var\\run\\salt-master.pid'.format( - salt.syspaths.ROOT_DIR), - 'root_dir': format(salt.syspaths.ROOT_DIR) - } - - -class SampleConfTest(TestCase): + +class DefaultConfigsBase(object): + + @classmethod + def setUpClass(cls): + cls.mock_master_default_opts = dict( + root_dir=RUNTIME_VARS.TMP_ROOT_DIR, + log_file=os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'var', 'log', 'salt', 'master'), + pid_file=os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'var', 'run', 'salt-master.pid') + ) + + +class SampleConfTest(DefaultConfigsBase, TestCase): ''' Validate files in the salt/conf directory. ''' @@ -96,7 +89,7 @@ def test_conf_master_sample_is_commented(self): self.assertEqual( ret, {}, - 'Sample config file \'{0}\' must be commented out.'.format( + 'Sample config file \'{}\' must be commented out.'.format( master_config ) ) @@ -111,7 +104,7 @@ def test_conf_minion_sample_is_commented(self): self.assertEqual( ret, {}, - 'Sample config file \'{0}\' must be commented out.'.format( + 'Sample config file \'{}\' must be commented out.'.format( minion_config ) ) @@ -126,7 +119,7 @@ def test_conf_cloud_sample_is_commented(self): self.assertEqual( ret, {}, - 'Sample config file \'{0}\' must be commented out.'.format( + 'Sample config file \'{}\' must be commented out.'.format( cloud_config ) ) @@ -141,7 +134,7 @@ def test_conf_cloud_profiles_sample_is_commented(self): self.assertEqual( ret, {}, - 'Sample config file \'{0}\' must be commented out.'.format( + 'Sample config file \'{}\' must be commented out.'.format( cloud_profiles_config ) ) @@ -156,7 +149,7 @@ def test_conf_cloud_providers_sample_is_commented(self): self.assertEqual( ret, {}, - 'Sample config file \'{0}\' must be commented out.'.format( + 'Sample config file \'{}\' must be commented out.'.format( cloud_providers_config ) ) @@ -171,7 +164,7 @@ def test_conf_proxy_sample_is_commented(self): self.assertEqual( ret, {}, - 'Sample config file \'{0}\' must be commented out.'.format( + 'Sample config file \'{}\' must be commented out.'.format( proxy_config ) ) @@ -186,7 +179,7 @@ def test_conf_roster_sample_is_commented(self): self.assertEqual( ret, {}, - 'Sample config file \'{0}\' must be commented out.'.format( + 'Sample config file \'{}\' must be commented out.'.format( roster_config ) ) @@ -207,7 +200,7 @@ def test_conf_cloud_profiles_d_files_are_commented(self): self.assertEqual( ret, {}, - 'Sample config file \'{0}\' must be commented out.'.format( + 'Sample config file \'{}\' must be commented out.'.format( conf_file ) ) @@ -228,7 +221,7 @@ def test_conf_cloud_providers_d_files_are_commented(self): self.assertEqual( ret, {}, - 'Sample config file \'{0}\' must be commented out.'.format( + 'Sample config file \'{}\' must be commented out.'.format( conf_file ) ) @@ -249,7 +242,7 @@ def test_conf_cloud_maps_d_files_are_commented(self): self.assertEqual( ret, {}, - 'Sample config file \'{0}\' must be commented out.'.format( + 'Sample config file \'{}\' must be commented out.'.format( conf_file ) ) @@ -259,14 +252,14 @@ def _unhandled_mock_read(filename): ''' Raise an error because we should not be calling salt.utils.files.fopen() ''' - raise CommandExecutionError('Unhandled mock read for {0}'.format(filename)) + raise CommandExecutionError('Unhandled mock read for {}'.format(filename)) def _salt_configuration_error(filename): ''' Raise an error to indicate error in the Salt configuration file ''' - raise SaltConfigurationError('Configuration error in {0}'.format(filename)) + raise SaltConfigurationError('Configuration error in {}'.format(filename)) class ConfigTestCase(TestCase, AdaptedConfigurationTestCaseMixin): @@ -278,7 +271,7 @@ def test_sha256_is_default_for_master(self, fpath): "root_dir: /\n" "key_logfile: key\n" ) - config = sconfig.master_config(fpath) + config = salt.config.master_config(fpath) self.assertEqual(config['hash_type'], 'sha256') @with_tempfile() @@ -288,7 +281,7 @@ def test_sha256_is_default_for_minion(self, fpath): "root_dir: /\n" "key_logfile: key\n" ) - config = sconfig.minion_config(fpath) + config = salt.config.minion_config(fpath) self.assertEqual(config['hash_type'], 'sha256') @with_tempfile() @@ -301,7 +294,7 @@ def test_proper_path_joining(self, fpath): with salt.utils.files.fopen(fpath, 'w') as fp_: fp_.write(temp_config) - config = sconfig.master_config(fpath) + config = salt.config.master_config(fpath) expect_path_join = os.path.join('/', 'key') expect_sep_join = '//key' if salt.utils.platform.is_windows(): @@ -320,10 +313,10 @@ def test_common_prefix_stripping(self, tempdir): fpath = os.path.join(root_dir, 'config') with salt.utils.files.fopen(fpath, 'w') as fp_: fp_.write( - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(root_dir, fpath) + 'root_dir: {}\n' + 'log_file: {}\n'.format(root_dir, fpath) ) - config = sconfig.master_config(fpath) + config = salt.config.master_config(fpath) self.assertEqual(config['log_file'], fpath) @with_tempdir() @@ -333,11 +326,10 @@ def test_default_root_dir_included_in_config_root_dir(self, tempdir): fpath = os.path.join(root_dir, 'config') with salt.utils.files.fopen(fpath, 'w') as fp_: fp_.write( - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(root_dir, fpath) + 'root_dir: {}\n' + 'log_file: {}\n'.format(root_dir, fpath) ) - with patch('salt.syspaths.ROOT_DIR', TMP): - config = sconfig.master_config(fpath) + config = salt.config.master_config(fpath) self.assertEqual(config['log_file'], fpath) @skipIf( @@ -345,86 +337,74 @@ def test_default_root_dir_included_in_config_root_dir(self, tempdir): 'You can\'t set an environment dynamically in Windows') @with_tempdir() def test_load_master_config_from_environ_var(self, tempdir): - original_environ = os.environ.copy() env_root_dir = os.path.join(tempdir, 'foo', 'env') os.makedirs(env_root_dir) env_fpath = os.path.join(env_root_dir, 'config-env') with salt.utils.files.fopen(env_fpath, 'w') as fp_: fp_.write( - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(env_root_dir, env_fpath) + 'root_dir: {}\n' + 'log_file: {}\n'.format(env_root_dir, env_fpath) ) - - os.environ['SALT_MASTER_CONFIG'] = env_fpath - # Should load from env variable, not the default configuration file. - config = sconfig.master_config('{0}/master'.format(CONFIG_DIR)) - self.assertEqual(config['log_file'], env_fpath) - os.environ.clear() - os.environ.update(original_environ) + with patched_environ(SALT_MASTER_CONFIG=env_fpath): + # Should load from env variable, not the default configuration file. + config = salt.config.master_config('{}/master'.format(CONFIG_DIR)) + self.assertEqual(config['log_file'], env_fpath) root_dir = os.path.join(tempdir, 'foo', 'bar') os.makedirs(root_dir) fpath = os.path.join(root_dir, 'config') with salt.utils.files.fopen(fpath, 'w') as fp_: fp_.write( - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(root_dir, fpath) + 'root_dir: {}\n' + 'log_file: {}\n'.format(root_dir, fpath) ) # Let's set the environment variable, yet, since the configuration # file path is not the default one, i.e., the user has passed an # alternative configuration file form the CLI parser, the # environment variable will be ignored. - os.environ['SALT_MASTER_CONFIG'] = env_fpath - config = sconfig.master_config(fpath) - self.assertEqual(config['log_file'], fpath) - os.environ.clear() - os.environ.update(original_environ) + with patched_environ(SALT_MASTER_CONFIG=env_fpath): + config = salt.config.master_config(fpath) + self.assertEqual(config['log_file'], fpath) @skipIf( salt.utils.platform.is_windows(), 'You can\'t set an environment dynamically in Windows') @with_tempdir() def test_load_minion_config_from_environ_var(self, tempdir): - original_environ = os.environ.copy() env_root_dir = os.path.join(tempdir, 'foo', 'env') os.makedirs(env_root_dir) env_fpath = os.path.join(env_root_dir, 'config-env') with salt.utils.files.fopen(env_fpath, 'w') as fp_: fp_.write( - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(env_root_dir, env_fpath) + 'root_dir: {}\n' + 'log_file: {}\n'.format(env_root_dir, env_fpath) ) - os.environ['SALT_MINION_CONFIG'] = env_fpath - # Should load from env variable, not the default configuration file - config = sconfig.minion_config('{0}/minion'.format(CONFIG_DIR)) - self.assertEqual(config['log_file'], env_fpath) - os.environ.clear() - os.environ.update(original_environ) + with patched_environ(SALT_MINION_CONFIG=env_fpath): + # Should load from env variable, not the default configuration file + config = salt.config.minion_config('{}/minion'.format(CONFIG_DIR)) + self.assertEqual(config['log_file'], env_fpath) root_dir = os.path.join(tempdir, 'foo', 'bar') os.makedirs(root_dir) fpath = os.path.join(root_dir, 'config') with salt.utils.files.fopen(fpath, 'w') as fp_: fp_.write( - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(root_dir, fpath) + 'root_dir: {}\n' + 'log_file: {}\n'.format(root_dir, fpath) ) # Let's set the environment variable, yet, since the configuration # file path is not the default one, i.e., the user has passed an # alternative configuration file form the CLI parser, the # environment variable will be ignored. - os.environ['SALT_MINION_CONFIG'] = env_fpath - config = sconfig.minion_config(fpath) - self.assertEqual(config['log_file'], fpath) - os.environ.clear() - os.environ.update(original_environ) + with patched_environ(SALT_MINION_CONFIG=env_fpath): + config = salt.config.minion_config(fpath) + self.assertEqual(config['log_file'], fpath) @with_tempdir() def test_load_client_config_from_environ_var(self, tempdir): - original_environ = os.environ.copy() env_root_dir = os.path.join(tempdir, 'foo', 'env') os.makedirs(env_root_dir) @@ -436,44 +416,41 @@ def test_load_client_config_from_environ_var(self, tempdir): with salt.utils.files.fopen(master_config, 'w') as fp_: fp_.write( 'blah: true\n' - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(env_root_dir, master_config) + 'root_dir: {}\n' + 'log_file: {}\n'.format(env_root_dir, master_config) ) - os.environ['SALT_MASTER_CONFIG'] = master_config # Now the client configuration file env_fpath = os.path.join(env_root_dir, 'config-env') with salt.utils.files.fopen(env_fpath, 'w') as fp_: fp_.write( - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(env_root_dir, env_fpath) + 'root_dir: {}\n' + 'log_file: {}\n'.format(env_root_dir, env_fpath) ) - os.environ['SALT_CLIENT_CONFIG'] = env_fpath - # Should load from env variable, not the default configuration file - config = sconfig.client_config(os.path.expanduser('~/.salt')) - self.assertEqual(config['log_file'], env_fpath) - self.assertTrue('blah' not in config) - os.environ.clear() - os.environ.update(original_environ) + with patched_environ(SALT_MASTER_CONFIG=master_config, + SALT_CLIENT_CONFIG=env_fpath): + # Should load from env variable, not the default configuration file + config = salt.config.client_config(os.path.expanduser('~/.salt')) + self.assertEqual(config['log_file'], env_fpath) + self.assertTrue('blah' not in config) root_dir = os.path.join(tempdir, 'foo', 'bar') os.makedirs(root_dir) fpath = os.path.join(root_dir, 'config') with salt.utils.files.fopen(fpath, 'w') as fp_: fp_.write( - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(root_dir, fpath) + 'root_dir: {}\n' + 'log_file: {}\n'.format(root_dir, fpath) ) # Let's set the environment variable, yet, since the configuration # file path is not the default one, i.e., the user has passed an # alternative configuration file form the CLI parser, the # environment variable will be ignored. - os.environ['SALT_MASTER_CONFIG'] = env_fpath - config = sconfig.master_config(fpath) - self.assertEqual(config['log_file'], fpath) - os.environ.clear() - os.environ.update(original_environ) + with patched_environ(SALT_MASTER_CONFIG=env_fpath, + SALT_CLIENT_CONFIG=env_fpath): + config = salt.config.master_config(fpath) + self.assertEqual(config['log_file'], fpath) @with_tempdir() def test_issue_5970_minion_confd_inclusion(self, tempdir): @@ -486,8 +463,8 @@ def test_issue_5970_minion_confd_inclusion(self, tempdir): with salt.utils.files.fopen(minion_config, 'w') as fp_: fp_.write( 'blah: false\n' - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(tempdir, minion_config) + 'root_dir: {}\n' + 'log_file: {}\n'.format(tempdir, minion_config) ) # Now, let's populate an extra configuration file under minion.d @@ -500,7 +477,7 @@ def test_issue_5970_minion_confd_inclusion(self, tempdir): fp_.write('blah: true\n') # Let's load the configuration - config = sconfig.minion_config(minion_config) + config = salt.config.minion_config(minion_config) self.assertEqual(config['log_file'], minion_config) # As proven by the assertion below, blah is True @@ -517,8 +494,8 @@ def test_master_confd_inclusion(self, tempdir): with salt.utils.files.fopen(master_config, 'w') as fp_: fp_.write( 'blah: false\n' - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(tempdir, master_config) + 'root_dir: {}\n' + 'log_file: {}\n'.format(tempdir, master_config) ) # Now, let's populate an extra configuration file under master.d @@ -531,7 +508,7 @@ def test_master_confd_inclusion(self, tempdir): fp_.write('blah: true\n') # Let's load the configuration - config = sconfig.master_config(master_config) + config = salt.config.master_config(master_config) self.assertEqual(config['log_file'], master_config) # As proven by the assertion below, blah is True @@ -550,9 +527,9 @@ def test_master_file_roots_glob(self, tempdir, fpath): wfh.write( 'file_roots:\n' ' base:\n' - ' - {0}'.format(os.path.join(tempdir, '*')) + ' - {}'.format(os.path.join(tempdir, '*')) ) - config = sconfig.master_config(fpath) + config = salt.config.master_config(fpath) base = config['file_roots']['base'] self.assertEqual(set(base), set([ os.path.join(tempdir, 'a'), @@ -582,9 +559,9 @@ def test_master_pillar_roots_glob(self, tempdir, fpath): wfh.write( 'pillar_roots:\n' ' base:\n' - ' - {0}'.format(os.path.join(tempdir, '*')) + ' - {}'.format(os.path.join(tempdir, '*')) ) - config = sconfig.master_config(fpath) + config = salt.config.master_config(fpath) base = config['pillar_roots']['base'] self.assertEqual(set(base), set([ os.path.join(tempdir, 'a'), @@ -610,12 +587,12 @@ def test_master_id_function(self, tempdir): 'id_function:\n' ' test.echo:\n' ' text: hello_world\n' - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(tempdir, master_config) + 'root_dir: {}\n' + 'log_file: {}\n'.format(tempdir, master_config) ) # Let's load the configuration - config = sconfig.master_config(master_config) + config = salt.config.master_config(master_config) self.assertEqual(config['log_file'], master_config) # 'master_config' appends '_master' to the ID @@ -634,9 +611,9 @@ def test_minion_file_roots_glob(self, tempdir, fpath): wfh.write( 'file_roots:\n' ' base:\n' - ' - {0}'.format(os.path.join(tempdir, '*')) + ' - {}'.format(os.path.join(tempdir, '*')) ) - config = sconfig.minion_config(fpath) + config = salt.config.minion_config(fpath) base = config['file_roots']['base'] self.assertEqual(set(base), set([ os.path.join(tempdir, 'a'), @@ -657,9 +634,9 @@ def test_minion_pillar_roots_glob(self, tempdir, fpath): wfh.write( 'pillar_roots:\n' ' base:\n' - ' - {0}'.format(os.path.join(tempdir, '*')) + ' - {}'.format(os.path.join(tempdir, '*')) ) - config = sconfig.minion_config(fpath) + config = salt.config.minion_config(fpath) base = config['pillar_roots']['base'] self.assertEqual(set(base), set([ os.path.join(tempdir, 'a'), @@ -676,12 +653,12 @@ def test_minion_id_function(self, tempdir): 'id_function:\n' ' test.echo:\n' ' text: hello_world\n' - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(tempdir, minion_config) + 'root_dir: {}\n' + 'log_file: {}\n'.format(tempdir, minion_config) ) # Let's load the configuration - config = sconfig.minion_config(minion_config) + config = salt.config.minion_config(minion_config) self.assertEqual(config['log_file'], minion_config) self.assertEqual(config['id'], 'hello_world') @@ -702,11 +679,84 @@ def test_minion_id_lowercase(self, tempdir): minion_id_caching: False minion_id_lowercase: True ''')) - config = sconfig.minion_config(minion_config) # Load the configuration + config = salt.config.minion_config(minion_config) # Load the configuration self.assertEqual(config['minion_id_caching'], False) # Check the configuration self.assertEqual(config['minion_id_lowercase'], True) # Check the configuration self.assertEqual(config['id'], 'king_bob') + @with_tempdir() + def test_minion_id_remove_domain_string_positive(self, tempdir): + ''' + This tests that the values of `minion_id_remove_domain` is suppressed from a generated minion id, + effectivly generating a hostname minion_id. + ''' + minion_config = os.path.join(tempdir, 'minion') + with salt.utils.files.fopen(minion_config, 'w') as fp_: + fp_.write(textwrap.dedent('''\ + id_function: + test.echo: + text: king_bob.foo.org + minion_id_remove_domain: foo.org + minion_id_caching: False + ''')) + + # Let's load the configuration + config = salt.config.minion_config(minion_config) + self.assertEqual(config['minion_id_remove_domain'], 'foo.org') + self.assertEqual(config['id'], 'king_bob') + + @with_tempdir() + def test_minion_id_remove_domain_string_negative(self, tempdir): + ''' + See above + ''' + minion_config = os.path.join(tempdir, 'minion') + with salt.utils.files.fopen(minion_config, 'w') as fp_: + fp_.write(textwrap.dedent('''\ + id_function: + test.echo: + text: king_bob.foo.org + minion_id_remove_domain: bar.org + minion_id_caching: False + ''')) + + config = salt.config.minion_config(minion_config) + self.assertEqual(config['id'], 'king_bob.foo.org') + + @with_tempdir() + def test_minion_id_remove_domain_bool_true(self, tempdir): + ''' + See above + ''' + minion_config = os.path.join(tempdir, 'minion') + with salt.utils.files.fopen(minion_config, 'w') as fp_: + fp_.write(textwrap.dedent('''\ + id_function: + test.echo: + text: king_bob.foo.org + minion_id_remove_domain: True + minion_id_caching: False + ''')) + config = salt.config.minion_config(minion_config) + self.assertEqual(config['id'], 'king_bob') + + @with_tempdir() + def test_minion_id_remove_domain_bool_false(self, tempdir): + ''' + See above + ''' + minion_config = os.path.join(tempdir, 'minion') + with salt.utils.files.fopen(minion_config, 'w') as fp_: + fp_.write(textwrap.dedent('''\ + id_function: + test.echo: + text: king_bob.foo.org + minion_id_remove_domain: False + minion_id_caching: False + ''')) + config = salt.config.minion_config(minion_config) + self.assertEqual(config['id'], 'king_bob.foo.org') + @with_tempdir() def test_backend_rename(self, tempdir): ''' @@ -724,37 +774,34 @@ def test_backend_rename(self, tempdir): - minion ''')) - master_config = sconfig.master_config(fpath) - minion_config = sconfig.minion_config(fpath) + master_config = salt.config.master_config(fpath) + minion_config = salt.config.minion_config(fpath) expected = ['roots', 'gitfs', 'hgfs', 'svnfs', 'minionfs'] self.assertEqual(master_config['fileserver_backend'], expected) self.assertEqual(minion_config['fileserver_backend'], expected) def test_syndic_config(self): - syndic_conf_path = self.get_config_file_path('syndic') - minion_conf_path = self.get_config_file_path('minion') - syndic_opts = sconfig.syndic_config( - syndic_conf_path, minion_conf_path - ) - syndic_opts.update(salt.minion.resolve_dns(syndic_opts)) + minion_conf_path = self.get_config_file_path('syndic') + master_conf_path = os.path.join(os.path.dirname(minion_conf_path), 'master') + syndic_opts = salt.config.syndic_config(master_conf_path, minion_conf_path) root_dir = syndic_opts['root_dir'] # id & pki dir are shared & so configured on the minion side - self.assertEqual(syndic_opts['id'], 'minion') + self.assertEqual(syndic_opts['id'], 'syndic') self.assertEqual(syndic_opts['pki_dir'], os.path.join(root_dir, 'pki')) # the rest is configured master side - self.assertIn(syndic_opts['master_uri'], ['tcp://127.0.0.1:54506', 'tcp://[::1]:54506']) - self.assertEqual(syndic_opts['master_port'], 54506) - self.assertIn(syndic_opts['master_ip'], ['127.0.0.1', '[::1]']) + if RUNTIME_VARS.PYTEST_SESSION is False: + # Pytest assigns ports dynamically + self.assertEqual(syndic_opts['master_port'], 54506) self.assertEqual(syndic_opts['master'], 'localhost') - self.assertEqual(syndic_opts['sock_dir'], os.path.join(root_dir, 'minion_sock')) + self.assertEqual(syndic_opts['sock_dir'], os.path.join(root_dir, 'syndic_sock')) self.assertEqual(syndic_opts['cachedir'], os.path.join(root_dir, 'cache')) - self.assertEqual(syndic_opts['log_file'], os.path.join(root_dir, 'syndic.log')) - self.assertEqual(syndic_opts['pidfile'], os.path.join(root_dir, 'syndic.pid')) + self.assertEqual(syndic_opts['log_file'], os.path.join(root_dir, 'logs', 'syndic.log')) + self.assertEqual(syndic_opts['pidfile'], os.path.join(root_dir, 'run', 'syndic.pid')) # Show that the options of localclient that repub to local master # are not merged with syndic ones self.assertEqual(syndic_opts['_master_conf_file'], minion_conf_path) - self.assertEqual(syndic_opts['_minion_conf_file'], syndic_conf_path) + self.assertEqual(syndic_opts['_minion_conf_file'], master_conf_path) @with_tempfile() def _get_tally(self, fpath, conf_func): @@ -806,7 +853,7 @@ def _count_strings(config): - foo - bar - baz''')) - if conf_func is sconfig.master_config: + if conf_func is salt.config.master_config: wfh.write('\n\n') wfh.write(textwrap.dedent(''' rest_cherrypy: @@ -823,7 +870,7 @@ def test_conf_file_strings_are_unicode_for_master(self): ''' This ensures that any strings which are loaded are unicode strings ''' - tally = self._get_tally(sconfig.master_config) # pylint: disable=no-value-for-parameter + tally = self._get_tally(salt.config.master_config) # pylint: disable=no-value-for-parameter non_unicode = tally.get('non_unicode', []) self.assertEqual(len(non_unicode), 8 if six.PY2 else 0, non_unicode) self.assertTrue(tally['unicode'] > 0) @@ -832,7 +879,7 @@ def test_conf_file_strings_are_unicode_for_minion(self): ''' This ensures that any strings which are loaded are unicode strings ''' - tally = self._get_tally(sconfig.minion_config) # pylint: disable=no-value-for-parameter + tally = self._get_tally(salt.config.minion_config) # pylint: disable=no-value-for-parameter non_unicode = tally.get('non_unicode', []) self.assertEqual(len(non_unicode), 0, non_unicode) self.assertTrue(tally['unicode'] > 0) @@ -841,34 +888,30 @@ def test_conf_file_strings_are_unicode_for_minion(self): # cloud_config tests - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_cloud_config_double_master_path(self): ''' Tests passing in master_config_path and master_config kwargs. ''' with patch('salt.config.load_config', MagicMock(return_value={})): - self.assertRaises(SaltCloudConfigError, sconfig.cloud_config, PATH, + self.assertRaises(SaltCloudConfigError, salt.config.cloud_config, PATH, master_config_path='foo', master_config='bar') - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_cloud_config_double_providers_path(self): ''' Tests passing in providers_config_path and providers_config kwargs. ''' with patch('salt.config.load_config', MagicMock(return_value={})): - self.assertRaises(SaltCloudConfigError, sconfig.cloud_config, PATH, + self.assertRaises(SaltCloudConfigError, salt.config.cloud_config, PATH, providers_config_path='foo', providers_config='bar') - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_cloud_config_double_profiles_path(self): ''' Tests passing in profiles_config_path and profiles_config kwargs. ''' with patch('salt.config.load_config', MagicMock(return_value={})): - self.assertRaises(SaltCloudConfigError, sconfig.cloud_config, PATH, + self.assertRaises(SaltCloudConfigError, salt.config.cloud_config, PATH, profiles_config_path='foo', profiles_config='bar') - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_cloud_config_providers_in_opts(self): ''' Tests mixing old cloud providers with pre-configured providers configurations @@ -877,10 +920,9 @@ def test_cloud_config_providers_in_opts(self): with patch('salt.config.load_config', MagicMock(return_value={})): with patch('salt.config.apply_cloud_config', MagicMock(return_value={'providers': 'foo'})): - self.assertRaises(SaltCloudConfigError, sconfig.cloud_config, PATH, + self.assertRaises(SaltCloudConfigError, salt.config.cloud_config, PATH, providers_config='bar') - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_cloud_config_providers_in_opts_path(self): ''' Tests mixing old cloud providers with pre-configured providers configurations @@ -890,10 +932,9 @@ def test_cloud_config_providers_in_opts_path(self): with patch('salt.config.apply_cloud_config', MagicMock(return_value={'providers': 'foo'})): with patch('os.path.isfile', MagicMock(return_value=True)): - self.assertRaises(SaltCloudConfigError, sconfig.cloud_config, PATH, + self.assertRaises(SaltCloudConfigError, salt.config.cloud_config, PATH, providers_config_path='bar') - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_cloud_config_deploy_scripts_search_path(self): ''' Tests the contents of the 'deploy_scripts_search_path' tuple to ensure that @@ -906,7 +947,7 @@ def test_cloud_config_deploy_scripts_search_path(self): the path. ''' with patch('os.path.isdir', MagicMock(return_value=True)): - search_paths = sconfig.cloud_config('/etc/salt/cloud').get('deploy_scripts_search_path') + search_paths = salt.config.cloud_config('/etc/salt/cloud').get('deploy_scripts_search_path') etc_deploy_path = '/salt/cloud.deploy.d' deploy_path = '/salt/cloud/deploy' if salt.utils.platform.is_windows(): @@ -926,7 +967,7 @@ def test_apply_cloud_config_no_provider_detail_list(self): Tests when the provider is not contained in a list of details ''' overrides = {'providers': {'foo': [{'bar': 'baz'}]}} - self.assertRaises(SaltCloudConfigError, sconfig.apply_cloud_config, + self.assertRaises(SaltCloudConfigError, salt.config.apply_cloud_config, overrides, defaults=DEFAULT) def test_apply_cloud_config_no_provider_detail_dict(self): @@ -934,10 +975,9 @@ def test_apply_cloud_config_no_provider_detail_dict(self): Tests when the provider is not contained in the details dictionary ''' overrides = {'providers': {'foo': {'bar': 'baz'}}} - self.assertRaises(SaltCloudConfigError, sconfig.apply_cloud_config, + self.assertRaises(SaltCloudConfigError, salt.config.apply_cloud_config, overrides, defaults=DEFAULT) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_apply_cloud_config_success_list(self): ''' Tests success when valid data is passed into the function as a list @@ -951,9 +991,8 @@ def test_apply_cloud_config_success_list(self): overrides = {'providers': {'foo': [{'driver': 'bar'}]}} ret = {'default_include': 'path/to/some/cloud/conf/file', 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}}} - self.assertEqual(sconfig.apply_cloud_config(overrides, defaults=DEFAULT), ret) + self.assertEqual(salt.config.apply_cloud_config(overrides, defaults=DEFAULT), ret) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_apply_cloud_config_success_dict(self): ''' Tests success when valid data is passed into function as a dictionary @@ -967,7 +1006,7 @@ def test_apply_cloud_config_success_dict(self): overrides = {'providers': {'foo': {'driver': 'bar'}}} ret = {'default_include': 'path/to/some/cloud/conf/file', 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}}} - self.assertEqual(sconfig.apply_cloud_config(overrides, defaults=DEFAULT), ret) + self.assertEqual(salt.config.apply_cloud_config(overrides, defaults=DEFAULT), ret) # apply_vm_profiles_config tests @@ -976,7 +1015,7 @@ def test_apply_vm_profiles_config_bad_profile_format(self): Tests passing in a bad profile format in overrides ''' overrides = {'foo': 'bar', 'conf_file': PATH} - self.assertRaises(SaltCloudConfigError, sconfig.apply_vm_profiles_config, + self.assertRaises(SaltCloudConfigError, salt.config.apply_vm_profiles_config, PATH, overrides, defaults=DEFAULT) def test_apply_vm_profiles_config_success(self): @@ -996,7 +1035,7 @@ def test_apply_vm_profiles_config_success(self): 'provider': 'test-provider:digitalocean', 'image': 'Ubuntu 12.10 x64', 'size': '512MB'}} - self.assertEqual(sconfig.apply_vm_profiles_config(providers, + self.assertEqual(salt.config.apply_vm_profiles_config(providers, overrides, defaults=DEFAULT), ret) @@ -1023,7 +1062,7 @@ def test_apply_vm_profiles_config_extend_success(self): 'dev-instances': {'profile': 'dev-instances', 'ssh_username': 'test_user', 'provider': 'test-config:ec2'}} - self.assertEqual(sconfig.apply_vm_profiles_config(providers, + self.assertEqual(salt.config.apply_vm_profiles_config(providers, overrides, defaults=DEFAULT), ret) @@ -1050,7 +1089,7 @@ def test_apply_vm_profiles_config_extend_override_success(self): 'ssh_username': 'test_user', 'minion': {'grains': {'role': 'webserver'}}, 'provider': 'test-config:ec2'}} - self.assertEqual(sconfig.apply_vm_profiles_config(providers, + self.assertEqual(salt.config.apply_vm_profiles_config(providers, overrides, defaults=DEFAULT), ret) @@ -1069,7 +1108,7 @@ def test_apply_cloud_providers_config_same_providers(self): 'driver': 'ec2'}], 'conf_file': PATH} self.assertRaises(SaltCloudConfigError, - sconfig.apply_cloud_providers_config, + salt.config.apply_cloud_providers_config, overrides, DEFAULT) @@ -1116,7 +1155,7 @@ def test_apply_cloud_providers_config_extend(self): 'id': 'ABCDEFGHIJKLMNOP', 'user': 'user@mycorp.com'}}} self.assertEqual(ret, - sconfig.apply_cloud_providers_config( + salt.config.apply_cloud_providers_config( overrides, defaults=DEFAULT)) @@ -1169,7 +1208,7 @@ def test_apply_cloud_providers_config_extend_multiple(self): 'driver': 'ec2', 'id': 'ABCDEFGHIJKLMNOP', 'location': 'ap-southeast-1'}}} - self.assertEqual(ret, sconfig.apply_cloud_providers_config( + self.assertEqual(ret, salt.config.apply_cloud_providers_config( overrides, defaults=DEFAULT)) @@ -1189,7 +1228,7 @@ def test_apply_cloud_providers_config_extends_bad_alias(self): 'driver': 'ec2'}], 'conf_file': PATH} self.assertRaises(SaltCloudConfigError, - sconfig.apply_cloud_providers_config, + salt.config.apply_cloud_providers_config, overrides, DEFAULT) @@ -1209,7 +1248,7 @@ def test_apply_cloud_providers_config_extends_bad_provider(self): 'driver': 'ec2'}], 'conf_file': PATH} self.assertRaises(SaltCloudConfigError, - sconfig.apply_cloud_providers_config, + salt.config.apply_cloud_providers_config, overrides, DEFAULT) @@ -1229,7 +1268,7 @@ def test_apply_cloud_providers_config_extends_no_provider(self): 'driver': 'linode'}], 'conf_file': PATH} self.assertRaises(SaltCloudConfigError, - sconfig.apply_cloud_providers_config, + salt.config.apply_cloud_providers_config, overrides, DEFAULT) @@ -1249,7 +1288,7 @@ def test_apply_cloud_providers_extends_not_in_providers(self): 'driver': 'linode'}], 'conf_file': PATH} self.assertRaises(SaltCloudConfigError, - sconfig.apply_cloud_providers_config, + salt.config.apply_cloud_providers_config, overrides, DEFAULT) @@ -1261,7 +1300,7 @@ def test_is_provider_configured_no_alias(self): ''' opts = {'providers': 'test'} provider = 'foo:bar' - self.assertFalse(sconfig.is_provider_configured(opts, provider)) + self.assertFalse(salt.config.is_provider_configured(opts, provider)) def test_is_provider_configured_no_driver(self): ''' @@ -1269,7 +1308,7 @@ def test_is_provider_configured_no_driver(self): ''' opts = {'providers': {'foo': 'baz'}} provider = 'foo:bar' - self.assertFalse(sconfig.is_provider_configured(opts, provider)) + self.assertFalse(salt.config.is_provider_configured(opts, provider)) def test_is_provider_configured_key_is_none(self): ''' @@ -1278,7 +1317,7 @@ def test_is_provider_configured_key_is_none(self): opts = {'providers': {'foo': {'bar': {'api_key': None}}}} provider = 'foo:bar' self.assertFalse( - sconfig.is_provider_configured(opts, + salt.config.is_provider_configured(opts, provider, required_keys=('api_key',))) @@ -1290,7 +1329,7 @@ def test_is_provider_configured_success(self): provider = 'foo:bar' ret = {'api_key': 'baz'} self.assertEqual( - sconfig.is_provider_configured(opts, + salt.config.is_provider_configured(opts, provider, required_keys=('api_key',)), ret) @@ -1301,7 +1340,7 @@ def test_is_provider_configured_multiple_driver_not_provider(self): ''' opts = {'providers': {'foo': {'bar': {'api_key': 'baz'}}}} provider = 'foo' - self.assertFalse(sconfig.is_provider_configured(opts, provider)) + self.assertFalse(salt.config.is_provider_configured(opts, provider)) def test_is_provider_configured_multiple_key_is_none(self): ''' @@ -1311,7 +1350,7 @@ def test_is_provider_configured_multiple_key_is_none(self): opts = {'providers': {'foo': {'bar': {'api_key': None}}}} provider = 'bar' self.assertFalse( - sconfig.is_provider_configured(opts, + salt.config.is_provider_configured(opts, provider, required_keys=('api_key',))) @@ -1324,7 +1363,7 @@ def test_is_provider_configured_multiple_success(self): provider = 'bar' ret = {'api_key': 'baz'} self.assertEqual( - sconfig.is_provider_configured(opts, + salt.config.is_provider_configured(opts, provider, required_keys=('api_key',)), ret) @@ -1335,44 +1374,36 @@ def test_is_provider_configured_multiple_success(self): 'You can\'t set an environment dynamically in Windows') @with_tempdir() def test_load_cloud_config_from_environ_var(self, tempdir): - original_environ = os.environ.copy() - try: - env_root_dir = os.path.join(tempdir, 'foo', 'env') - os.makedirs(env_root_dir) - env_fpath = os.path.join(env_root_dir, 'config-env') - - with salt.utils.files.fopen(env_fpath, 'w') as fp_: - fp_.write( - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(env_root_dir, env_fpath) - ) + env_root_dir = os.path.join(tempdir, 'foo', 'env') + os.makedirs(env_root_dir) + env_fpath = os.path.join(env_root_dir, 'config-env') - os.environ['SALT_CLOUD_CONFIG'] = env_fpath + with salt.utils.files.fopen(env_fpath, 'w') as fp_: + fp_.write( + 'root_dir: {}\n' + 'log_file: {}\n'.format(env_root_dir, env_fpath) + ) + + with patched_environ(SALT_CLOUD_CONFIG=env_fpath): # Should load from env variable, not the default configuration file - config = sconfig.cloud_config('/etc/salt/cloud') + config = salt.config.cloud_config('/etc/salt/cloud') self.assertEqual(config['log_file'], env_fpath) - os.environ.clear() - os.environ.update(original_environ) - - root_dir = os.path.join(tempdir, 'foo', 'bar') - os.makedirs(root_dir) - fpath = os.path.join(root_dir, 'config') - with salt.utils.files.fopen(fpath, 'w') as fp_: - fp_.write( - 'root_dir: {0}\n' - 'log_file: {1}\n'.format(root_dir, fpath) - ) - # Let's set the environment variable, yet, since the configuration - # file path is not the default one, i.e., the user has passed an - # alternative configuration file form the CLI parser, the - # environment variable will be ignored. - os.environ['SALT_CLOUD_CONFIG'] = env_fpath - config = sconfig.cloud_config(fpath) + + root_dir = os.path.join(tempdir, 'foo', 'bar') + os.makedirs(root_dir) + fpath = os.path.join(root_dir, 'config') + with salt.utils.files.fopen(fpath, 'w') as fp_: + fp_.write( + 'root_dir: {}\n' + 'log_file: {}\n'.format(root_dir, fpath) + ) + # Let's set the environment variable, yet, since the configuration + # file path is not the default one, i.e., the user has passed an + # alternative configuration file form the CLI parser, the + # environment variable will be ignored. + with patched_environ(SALT_CLOUD_CONFIG=env_fpath): + config = salt.config.cloud_config(fpath) self.assertEqual(config['log_file'], fpath) - finally: - # Reset the environ - os.environ.clear() - os.environ.update(original_environ) @with_tempdir() def test_deploy_search_path_as_string(self, temp_conf_dir): @@ -1382,12 +1413,12 @@ def test_deploy_search_path_as_string(self, temp_conf_dir): if not os.path.isdir(directory): os.makedirs(directory) - default_config = sconfig.cloud_config(config_file_path) + default_config = salt.config.cloud_config(config_file_path) default_config['deploy_scripts_search_path'] = deploy_dir_path with salt.utils.files.fopen(config_file_path, 'w') as cfd: salt.utils.yaml.safe_dump(default_config, cfd, default_flow_style=False) - default_config = sconfig.cloud_config(config_file_path) + default_config = salt.config.cloud_config(config_file_path) # Our custom deploy scripts path was correctly added to the list self.assertIn( @@ -1406,13 +1437,13 @@ def test_includes_load(self): Tests that cloud.{providers,profiles}.d directories are loaded, even if not directly passed in through path ''' - config = sconfig.cloud_config(self.get_config_file_path('cloud')) + log.warning('Clound config file path: %s', self.get_config_file_path('cloud')) + config = salt.config.cloud_config(self.get_config_file_path('cloud')) self.assertIn('ec2-config', config['providers']) self.assertIn('ec2-test', config['profiles']) # <---- Salt Cloud Configuration Tests --------------------------------------------- - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_include_config_without_errors(self): ''' Tests that include_config function returns valid configuration @@ -1423,11 +1454,10 @@ def test_include_config_without_errors(self): with patch('glob.glob', MagicMock(return_value=include_file)): with patch('salt.config._read_conf_file', MagicMock(return_value=config_opts)): - configuration = sconfig.include_config(include_file, config_path, verbose=False) + configuration = salt.config.include_config(include_file, config_path, verbose=False) self.assertEqual(config_opts, configuration) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_include_config_with_errors(self): ''' Tests that include_config function returns valid configuration even on errors @@ -1438,11 +1468,10 @@ def test_include_config_with_errors(self): with patch('glob.glob', MagicMock(return_value=include_file)): with patch('salt.config._read_conf_file', _salt_configuration_error): - configuration = sconfig.include_config(include_file, config_path, verbose=False) + configuration = salt.config.include_config(include_file, config_path, verbose=False) self.assertEqual(config_opts, configuration) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_include_config_with_errors_exit(self): ''' Tests that include_config exits on errors @@ -1453,7 +1482,7 @@ def test_include_config_with_errors_exit(self): with patch('glob.glob', MagicMock(return_value=include_file)): with patch('salt.config._read_conf_file', _salt_configuration_error): with self.assertRaises(SystemExit): - sconfig.include_config(include_file, + salt.config.include_config(include_file, config_path, verbose=False, exit_on_config_errors=True) @@ -1480,41 +1509,40 @@ def _get_defaults(**kwargs): ret.update(kwargs) return ret - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_apply_config(self): ''' Ensure that the environment and saltenv options work properly ''' - with patch.object(sconfig, '_adjust_log_file_override', Mock()), \ - patch.object(sconfig, '_update_ssl_config', Mock()), \ - patch.object(sconfig, '_update_discovery_config', Mock()): + with patch.object(salt.config, '_adjust_log_file_override', Mock()), \ + patch.object(salt.config, '_update_ssl_config', Mock()), \ + patch.object(salt.config, '_update_discovery_config', Mock()): # MASTER CONFIG # Ensure that environment overrides saltenv when saltenv not # explicitly passed. defaults = self._get_defaults(environment='foo') - ret = sconfig.apply_master_config(defaults=defaults) + ret = salt.config.apply_master_config(defaults=defaults) self.assertEqual(ret['environment'], 'foo') self.assertEqual(ret['saltenv'], 'foo') # Ensure that environment overrides saltenv when saltenv not # explicitly passed. defaults = self._get_defaults(environment='foo', saltenv='bar') - ret = sconfig.apply_master_config(defaults=defaults) + ret = salt.config.apply_master_config(defaults=defaults) self.assertEqual(ret['environment'], 'bar') self.assertEqual(ret['saltenv'], 'bar') # If environment was not explicitly set, it should not be in the # opts at all. defaults = self._get_defaults() - ret = sconfig.apply_master_config(defaults=defaults) + ret = salt.config.apply_master_config(defaults=defaults) self.assertNotIn('environment', ret) self.assertEqual(ret['saltenv'], None) # Same test as above but with saltenv explicitly set defaults = self._get_defaults(saltenv='foo') - ret = sconfig.apply_master_config(defaults=defaults) + ret = salt.config.apply_master_config(defaults=defaults) self.assertNotIn('environment', ret) self.assertEqual(ret['saltenv'], 'foo') @@ -1523,33 +1551,32 @@ def test_apply_config(self): # Ensure that environment overrides saltenv when saltenv not # explicitly passed. defaults = self._get_defaults(environment='foo') - ret = sconfig.apply_minion_config(defaults=defaults) + ret = salt.config.apply_minion_config(defaults=defaults) self.assertEqual(ret['environment'], 'foo') self.assertEqual(ret['saltenv'], 'foo') # Ensure that environment overrides saltenv when saltenv not # explicitly passed. defaults = self._get_defaults(environment='foo', saltenv='bar') - ret = sconfig.apply_minion_config(defaults=defaults) + ret = salt.config.apply_minion_config(defaults=defaults) self.assertEqual(ret['environment'], 'bar') self.assertEqual(ret['saltenv'], 'bar') # If environment was not explicitly set, it should not be in the # opts at all. defaults = self._get_defaults() - ret = sconfig.apply_minion_config(defaults=defaults) + ret = salt.config.apply_minion_config(defaults=defaults) self.assertNotIn('environment', ret) self.assertEqual(ret['saltenv'], None) # Same test as above but with saltenv explicitly set defaults = self._get_defaults(saltenv='foo') - ret = sconfig.apply_minion_config(defaults=defaults) + ret = salt.config.apply_minion_config(defaults=defaults) self.assertNotIn('environment', ret) self.assertEqual(ret['saltenv'], 'foo') -@skipIf(NO_MOCK, NO_MOCK_REASON) -class APIConfigTestCase(TestCase): +class APIConfigTestCase(DefaultConfigsBase, TestCase): ''' TestCase for the api_config function in salt.config.__init__.py ''' @@ -1567,13 +1594,13 @@ def test_api_config_log_file_values(self): various default dict updates. 'log_file' should be updated to match the DEFAULT_API_OPTS 'api_logfile' value. ''' - with patch('salt.config.client_config', MagicMock(return_value=MOCK_MASTER_DEFAULT_OPTS)): + with patch('salt.config.client_config', MagicMock(return_value=self.mock_master_default_opts)): - expected = '{0}/var/log/salt/api'.format( - salt.syspaths.ROOT_DIR if salt.syspaths.ROOT_DIR != '/' else '') + expected = '{}/var/log/salt/api'.format( + RUNTIME_VARS.TMP_ROOT_DIR if RUNTIME_VARS.TMP_ROOT_DIR != '/' else '') if salt.utils.platform.is_windows(): - expected = '{0}\\var\\log\\salt\\api'.format( - salt.syspaths.ROOT_DIR) + expected = '{}\\var\\log\\salt\\api'.format( + RUNTIME_VARS.TMP_ROOT_DIR) ret = salt.config.api_config('/some/fake/path') self.assertEqual(ret['log_file'], expected) @@ -1584,36 +1611,35 @@ def test_api_config_pidfile_values(self): various default dict updates. 'pidfile' should be updated to match the DEFAULT_API_OPTS 'api_pidfile' value. ''' - with patch('salt.config.client_config', MagicMock(return_value=MOCK_MASTER_DEFAULT_OPTS)): + with patch('salt.config.client_config', MagicMock(return_value=self.mock_master_default_opts)): - expected = '{0}/var/run/salt-api.pid'.format( - salt.syspaths.ROOT_DIR if salt.syspaths.ROOT_DIR != '/' else '') + expected = '{}/var/run/salt-api.pid'.format( + RUNTIME_VARS.TMP_ROOT_DIR if RUNTIME_VARS.TMP_ROOT_DIR != '/' else '') if salt.utils.platform.is_windows(): - expected = '{0}\\var\\run\\salt-api.pid'.format( - salt.syspaths.ROOT_DIR) + expected = '{}\\var\\run\\salt-api.pid'.format( + RUNTIME_VARS.TMP_ROOT_DIR) ret = salt.config.api_config('/some/fake/path') self.assertEqual(ret['pidfile'], expected) - @destructiveTest def test_master_config_file_overrides_defaults(self): ''' Tests the opts value of the api config values after running through the various default dict updates that should be overridden by settings in the user's master config file. ''' - foo_dir = '/foo/bar/baz' - hello_dir = '/hello/world' + foo_dir = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'foo/bar/baz') + hello_dir = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'hello/world') if salt.utils.platform.is_windows(): - foo_dir = 'c:\\foo\\bar\\baz' - hello_dir = 'c:\\hello\\world' + foo_dir = 'c:\\{}'.format(foo_dir.replace('/', '\\')) + hello_dir = 'c:\\{}'.format(hello_dir.replace('/', '\\')) mock_master_config = { 'api_pidfile': foo_dir, 'api_logfile': hello_dir, 'rest_timeout': 5 } - mock_master_config.update(MOCK_MASTER_DEFAULT_OPTS.copy()) + mock_master_config.update(self.mock_master_default_opts.copy()) with patch('salt.config.client_config', MagicMock(return_value=mock_master_config)): @@ -1624,7 +1650,6 @@ def test_master_config_file_overrides_defaults(self): self.assertEqual(ret['api_logfile'], hello_dir) self.assertEqual(ret['log_file'], hello_dir) - @destructiveTest def test_api_config_prepend_root_dirs_return(self): ''' Tests the opts value of the api_logfile, log_file, api_pidfile, and pidfile @@ -1635,7 +1660,7 @@ def test_api_config_prepend_root_dirs_return(self): mock_log = '/mock/root/var/log/salt/api' mock_pid = '/mock/root/var/run/salt-api.pid' - mock_master_config = MOCK_MASTER_DEFAULT_OPTS.copy() + mock_master_config = self.mock_master_default_opts.copy() mock_master_config['root_dir'] = '/mock/root/' if salt.utils.platform.is_windows(): diff --git a/tests/unit/test_crypt.py b/tests/unit/test_crypt.py index 56d50b79b287..abf0e39aa939 100644 --- a/tests/unit/test_crypt.py +++ b/tests/unit/test_crypt.py @@ -11,8 +11,6 @@ from tests.support.mock import ( patch, mock_open, - NO_MOCK, - NO_MOCK_REASON, MagicMock, MockCall, ) @@ -98,7 +96,6 @@ b'\x14H\xb2\xc4>\xc3A\xed\x86x~\xcfU\xd5Q\xfe~\x10\xd2\x9b') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYCRYPTO_RSA, 'pycrypto >= 2.6 is not available') @skipIf(HAS_M2, 'm2crypto is used by salt.crypt if installed') class CryptTestCase(TestCase): @@ -160,7 +157,6 @@ def test_verify_signature(self): self.assertTrue(crypt.verify_signature('/keydir/keyname.pub', MSG, SIG)) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_M2, 'm2crypto is not available') class M2CryptTestCase(TestCase): @patch('os.umask', MagicMock()) diff --git a/tests/unit/test_doc.py b/tests/unit/test_doc.py index 6ce6159eb67a..4f8735f53456 100644 --- a/tests/unit/test_doc.py +++ b/tests/unit/test_doc.py @@ -11,8 +11,8 @@ import logging # Import Salt Testing libs -from tests.support.paths import CODE_DIR from tests.support.unit import TestCase +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.modules.cmdmod @@ -38,7 +38,7 @@ def test_check_for_doc_inline_markup(self): https://github.com/saltstack/salt/issues/12788 ''' - salt_dir = CODE_DIR + salt_dir = RUNTIME_VARS.CODE_DIR if salt.utils.platform.is_windows(): if salt.utils.path.which('bash'): diff --git a/tests/unit/test_fileclient.py b/tests/unit/test_fileclient.py index bb3945ce5039..ecc9ed40f1c5 100644 --- a/tests/unit/test_fileclient.py +++ b/tests/unit/test_fileclient.py @@ -11,11 +11,10 @@ import shutil # Import Salt Testing libs -from tests.integration import AdaptedConfigurationTestCaseMixin -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import patch, Mock, MagicMock, NO_MOCK, NO_MOCK_REASON -from tests.support.paths import TMP -from tests.support.unit import TestCase, skipIf +from tests.support.mixins import AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin +from tests.support.mock import patch, Mock, MagicMock +from tests.support.unit import TestCase +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.utils.files @@ -73,30 +72,29 @@ def test_extrn_path_with_long_filename(self): SALTENVS = ('base', 'dev') -FS_ROOT = os.path.join(TMP, 'fileclient_fs_root') -CACHE_ROOT = os.path.join(TMP, 'fileclient_cache_root') SUBDIR = 'subdir' SUBDIR_FILES = ('foo.txt', 'bar.txt', 'baz.txt') -def _get_file_roots(): +def _get_file_roots(fs_root): return dict( - [(x, [os.path.join(FS_ROOT, x)]) for x in SALTENVS] + [(x, [os.path.join(fs_root, x)]) for x in SALTENVS] ) -MOCKED_OPTS = { - 'file_roots': _get_file_roots(), - 'fileserver_backend': ['roots'], - 'cachedir': CACHE_ROOT, - 'file_client': 'local', -} - - -@skipIf(NO_MOCK, NO_MOCK_REASON) class FileClientTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin): def setup_loader_modules(self): + FS_ROOT = os.path.join(RUNTIME_VARS.TMP, 'fileclient_fs_root') + CACHE_ROOT = os.path.join(RUNTIME_VARS.TMP, 'fileclient_cache_root') + MOCKED_OPTS = { + 'file_roots': _get_file_roots(FS_ROOT), + 'fileserver_backend': ['roots'], + 'cachedir': CACHE_ROOT, + 'file_client': 'local', + } + self.addCleanup(shutil.rmtree, FS_ROOT, ignore_errors=True) + self.addCleanup(shutil.rmtree, CACHE_ROOT, ignore_errors=True) return {fileclient: {'__opts__': MOCKED_OPTS}} def setUp(self): @@ -127,7 +125,6 @@ def test_get_file_client(self): self.assertEqual('remote_client', ret) -@skipIf(NO_MOCK, NO_MOCK_REASON) class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin): ''' Tests for the fileclient caching. The LocalClient is the only thing we can @@ -136,7 +133,17 @@ class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderMod ''' def setup_loader_modules(self): - return {fileclient: {'__opts__': MOCKED_OPTS}} + self.FS_ROOT = os.path.join(RUNTIME_VARS.TMP, 'fileclient_fs_root') + self.CACHE_ROOT = os.path.join(RUNTIME_VARS.TMP, 'fileclient_cache_root') + self.MOCKED_OPTS = { + 'file_roots': _get_file_roots(self.FS_ROOT), + 'fileserver_backend': ['roots'], + 'cachedir': self.CACHE_ROOT, + 'file_client': 'local', + } + self.addCleanup(shutil.rmtree, self.FS_ROOT, ignore_errors=True) + self.addCleanup(shutil.rmtree, self.CACHE_ROOT, ignore_errors=True) + return {fileclient: {'__opts__': self.MOCKED_OPTS}} def setUp(self): ''' @@ -162,7 +169,7 @@ def _new_dir(path): # Crete the FS_ROOT for saltenv in SALTENVS: - saltenv_root = os.path.join(FS_ROOT, saltenv) + saltenv_root = os.path.join(self.FS_ROOT, saltenv) # Make sure we have a fresh root dir for this saltenv _new_dir(saltenv_root) @@ -184,21 +191,14 @@ def _new_dir(path): ) # Create the CACHE_ROOT - _new_dir(CACHE_ROOT) - - def tearDown(self): - ''' - Remove the directories created for these tests - ''' - shutil.rmtree(FS_ROOT) - shutil.rmtree(CACHE_ROOT) + _new_dir(self.CACHE_ROOT) def test_cache_dir(self): ''' Ensure entire directory is cached to correct location ''' patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) - patched_opts.update(MOCKED_OPTS) + patched_opts.update(self.MOCKED_OPTS) with patch.dict(fileclient.__opts__, patched_opts): client = fileclient.get_file_client(fileclient.__opts__, pillar=False) @@ -237,8 +237,8 @@ def test_cache_dir_with_alternate_cachedir_and_absolute_path(self): cachedir is specified and that cachedir is an absolute path ''' patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) - patched_opts.update(MOCKED_OPTS) - alt_cachedir = os.path.join(TMP, 'abs_cachedir') + patched_opts.update(self.MOCKED_OPTS) + alt_cachedir = os.path.join(RUNTIME_VARS.TMP, 'abs_cachedir') with patch.dict(fileclient.__opts__, patched_opts): client = fileclient.get_file_client(fileclient.__opts__, pillar=False) @@ -277,7 +277,7 @@ def test_cache_dir_with_alternate_cachedir_and_relative_path(self): cachedir is specified and that cachedir is a relative path ''' patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) - patched_opts.update(MOCKED_OPTS) + patched_opts.update(self.MOCKED_OPTS) alt_cachedir = 'foo' with patch.dict(fileclient.__opts__, patched_opts): @@ -317,7 +317,7 @@ def test_cache_file(self): Ensure file is cached to correct location ''' patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) - patched_opts.update(MOCKED_OPTS) + patched_opts.update(self.MOCKED_OPTS) with patch.dict(fileclient.__opts__, patched_opts): client = fileclient.get_file_client(fileclient.__opts__, pillar=False) @@ -346,8 +346,8 @@ def test_cache_file_with_alternate_cachedir_and_absolute_path(self): specified and that cachedir is an absolute path ''' patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) - patched_opts.update(MOCKED_OPTS) - alt_cachedir = os.path.join(TMP, 'abs_cachedir') + patched_opts.update(self.MOCKED_OPTS) + alt_cachedir = os.path.join(RUNTIME_VARS.TMP, 'abs_cachedir') with patch.dict(fileclient.__opts__, patched_opts): client = fileclient.get_file_client(fileclient.__opts__, pillar=False) @@ -380,7 +380,7 @@ def test_cache_file_with_alternate_cachedir_and_relative_path(self): specified and that cachedir is a relative path ''' patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) - patched_opts.update(MOCKED_OPTS) + patched_opts.update(self.MOCKED_OPTS) alt_cachedir = 'foo' with patch.dict(fileclient.__opts__, patched_opts): diff --git a/tests/unit/test_loader.py b/tests/unit/test_loader.py index dd0ce8e2e2f7..cd74b0e468c0 100644 --- a/tests/unit/test_loader.py +++ b/tests/unit/test_loader.py @@ -21,10 +21,10 @@ import textwrap # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.unit import TestCase from tests.support.mock import patch -from tests.support.paths import TMP # Import Salt libs import salt.config @@ -76,12 +76,16 @@ class LazyLoaderTest(TestCase): def setUpClass(cls): cls.opts = salt.config.minion_config(None) cls.opts['grains'] = salt.loader.grains(cls.opts) - if not os.path.isdir(TMP): - os.makedirs(TMP) + if not os.path.isdir(RUNTIME_VARS.TMP): + os.makedirs(RUNTIME_VARS.TMP) + cls.utils = salt.loader.utils(cls.opts) + cls.proxy = salt.loader.proxy(cls.opts) + cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) def setUp(self): # Setup the module - self.module_dir = tempfile.mkdtemp(dir=TMP) + self.module_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + self.addCleanup(shutil.rmtree, self.module_dir, ignore_errors=True) self.module_file = os.path.join(self.module_dir, '{0}.py'.format(self.module_name)) with salt.utils.files.fopen(self.module_file, 'w') as fh: @@ -90,12 +94,14 @@ def setUp(self): os.fsync(fh.fileno()) # Invoke the loader - self.loader = salt.loader.LazyLoader([self.module_dir], copy.deepcopy(self.opts), tag='module') + self.loader = salt.loader.LazyLoader([self.module_dir], + copy.deepcopy(self.opts), + pack={'__utils__': self.utils, + '__salt__': self.funcs, + '__proxy__': self.proxy}, + tag='module') def tearDown(self): - shutil.rmtree(self.module_dir) - if os.path.isdir(self.module_dir): - shutil.rmtree(self.module_dir) del self.module_dir del self.module_file del self.loader @@ -103,6 +109,9 @@ def tearDown(self): @classmethod def tearDownClass(cls): del cls.opts + del cls.funcs + del cls.utils + del cls.proxy def test_depends(self): ''' @@ -128,11 +137,17 @@ def setUpClass(cls): cls.opts = salt.config.minion_config(None) cls.opts['disable_modules'] = ['pillar'] cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) + cls.proxy = salt.loader.proxy(cls.opts) + cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) def setUp(self): self.loader = salt.loader.LazyLoader( salt.loader._module_dirs(copy.deepcopy(self.opts), 'modules', 'module'), copy.deepcopy(self.opts), + pack={'__utils__': self.utils, + '__salt__': self.funcs, + '__proxy__': self.proxy}, tag='module') def tearDown(self): @@ -141,6 +156,9 @@ def tearDown(self): @classmethod def tearDownClass(cls): del cls.opts + del cls.funcs + del cls.utils + del cls.proxy def test_basic(self): ''' @@ -234,12 +252,18 @@ class LazyLoaderVirtualDisabledTest(TestCase): def setUpClass(cls): cls.opts = salt.config.minion_config(None) cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) + cls.proxy = salt.loader.proxy(cls.opts) + cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) def setUp(self): self.loader = salt.loader.LazyLoader( salt.loader._module_dirs(copy.deepcopy(self.opts), 'modules', 'module'), copy.deepcopy(self.opts), tag='module', + pack={'__utils__': self.utils, + '__salt__': self.funcs, + '__proxy__': self.proxy}, virtual_enable=False) def tearDown(self): @@ -248,6 +272,9 @@ def tearDown(self): @classmethod def tearDownClass(cls): del cls.opts + del cls.utils + del cls.funcs + del cls.proxy def test_virtual(self): self.assertTrue(inspect.isfunction(self.loader['test_virtual.ping'])) @@ -261,12 +288,18 @@ class LazyLoaderWhitelistTest(TestCase): def setUpClass(cls): cls.opts = salt.config.minion_config(None) cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) + cls.proxy = salt.loader.proxy(cls.opts) + cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) def setUp(self): self.loader = salt.loader.LazyLoader( salt.loader._module_dirs(copy.deepcopy(self.opts), 'modules', 'module'), copy.deepcopy(self.opts), tag='module', + pack={'__utils__': self.utils, + '__salt__': self.funcs, + '__proxy__': self.proxy}, whitelist=['test', 'pillar']) def tearDown(self): @@ -275,6 +308,9 @@ def tearDown(self): @classmethod def tearDownClass(cls): del cls.opts + del cls.funcs + del cls.utils + del cls.proxy def test_whitelist(self): self.assertTrue(inspect.isfunction(self.loader['test.ping'])) @@ -283,6 +319,31 @@ def test_whitelist(self): self.assertNotIn('grains.get', self.loader) +class LazyLoaderGrainsBlacklistTest(TestCase): + ''' + Test the loader of grains with a blacklist + ''' + def setUp(self): + self.opts = salt.config.minion_config(None) + + def tearDown(self): + del self.opts + + def test_whitelist(self): + opts = copy.deepcopy(self.opts) + opts['grains_blacklist'] = [ + 'master', + 'os*', + 'ipv[46]' + ] + + grains = salt.loader.grains(opts) + self.assertNotIn('master', grains) + self.assertNotIn('os', set([g[:2] for g in list(grains)])) + self.assertNotIn('ipv4', grains) + self.assertNotIn('ipv6', grains) + + class LazyLoaderSingleItem(TestCase): ''' Test loading a single item via the _load() function @@ -291,11 +352,24 @@ class LazyLoaderSingleItem(TestCase): def setUpClass(cls): cls.opts = salt.config.minion_config(None) cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) + cls.proxy = salt.loader.proxy(cls.opts) + cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) + + @classmethod + def tearDownClass(cls): + del cls.opts + del cls.funcs + del cls.utils + del cls.proxy def setUp(self): self.loader = salt.loader.LazyLoader( salt.loader._module_dirs(copy.deepcopy(self.opts), 'modules', 'module'), copy.deepcopy(self.opts), + pack={'__utils__': self.utils, + '__salt__': self.funcs, + '__proxy__': self.proxy}, tag='module') def tearDown(self): @@ -349,11 +423,12 @@ class LazyLoaderReloadingTest(TestCase): def setUpClass(cls): cls.opts = salt.config.minion_config(None) cls.opts['grains'] = salt.loader.grains(cls.opts) - if not os.path.isdir(TMP): - os.makedirs(TMP) + if not os.path.isdir(RUNTIME_VARS.TMP): + os.makedirs(RUNTIME_VARS.TMP) def setUp(self): - self.tmp_dir = tempfile.mkdtemp(dir=TMP) + self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + self.addCleanup(shutil.rmtree, self.tmp_dir, ignore_errors=True) self.count = 0 opts = copy.deepcopy(self.opts) @@ -371,7 +446,6 @@ def setUp(self): '__salt__': self.minion_mods}) def tearDown(self): - shutil.rmtree(self.tmp_dir) for attrname in ('tmp_dir', 'utils', 'proxy', 'loader', 'minion_mods', 'utils'): try: delattr(self, attrname) @@ -492,11 +566,11 @@ class LazyLoaderVirtualAliasTest(TestCase): def setUpClass(cls): cls.opts = salt.config.minion_config(None) cls.opts['grains'] = salt.loader.grains(cls.opts) - if not os.path.isdir(TMP): - os.makedirs(TMP) + if not os.path.isdir(RUNTIME_VARS.TMP): + os.makedirs(RUNTIME_VARS.TMP) def setUp(self): - self.tmp_dir = tempfile.mkdtemp(dir=TMP) + self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) opts = copy.deepcopy(self.opts) dirs = salt.loader._module_dirs(opts, 'modules', 'module') dirs.append(self.tmp_dir) @@ -579,11 +653,12 @@ class LazyLoaderSubmodReloadingTest(TestCase): def setUpClass(cls): cls.opts = salt.config.minion_config(None) cls.opts['grains'] = salt.loader.grains(cls.opts) - if not os.path.isdir(TMP): - os.makedirs(TMP) + if not os.path.isdir(RUNTIME_VARS.TMP): + os.makedirs(RUNTIME_VARS.TMP) def setUp(self): - self.tmp_dir = tempfile.mkdtemp(dir=TMP) + self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + self.addCleanup(shutil.rmtree, self.tmp_dir, ignore_errors=True) os.makedirs(self.module_dir) self.count = 0 @@ -604,7 +679,6 @@ def setUp(self): '__salt__': self.minion_mods}) def tearDown(self): - shutil.rmtree(self.tmp_dir) del self.tmp_dir del self.utils del self.proxy @@ -752,27 +826,36 @@ class LazyLoaderModulePackageTest(TestCase): def setUpClass(cls): cls.opts = salt.config.minion_config(None) cls.opts['grains'] = salt.loader.grains(cls.opts) - if not os.path.isdir(TMP): - os.makedirs(TMP) + if not os.path.isdir(RUNTIME_VARS.TMP): + os.makedirs(RUNTIME_VARS.TMP) + cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) + cls.proxy = salt.loader.proxy(cls.opts) + cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) def setUp(self): - self.tmp_dir = tempfile.mkdtemp(dir=TMP) + self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + self.addCleanup(shutil.rmtree, self.tmp_dir, ignore_errors=True) dirs = salt.loader._module_dirs(copy.deepcopy(self.opts), 'modules', 'module') dirs.append(self.tmp_dir) self.loader = salt.loader.LazyLoader( dirs, copy.deepcopy(self.opts), + pack={'__utils__': self.utils, + '__salt__': self.funcs, + '__proxy__': self.proxy}, tag='module') def tearDown(self): - shutil.rmtree(self.tmp_dir) del self.tmp_dir del self.loader @classmethod def tearDownClass(cls): del cls.opts + del cls.funcs + del cls.utils + del cls.proxy def update_pyfile(self, pyfile, contents): dirname = os.path.dirname(pyfile) @@ -857,11 +940,12 @@ class LazyLoaderDeepSubmodReloadingTest(TestCase): def setUpClass(cls): cls.opts = salt.config.minion_config(None) cls.opts['grains'] = salt.loader.grains(cls.opts) - if not os.path.isdir(TMP): - os.makedirs(TMP) + if not os.path.isdir(RUNTIME_VARS.TMP): + os.makedirs(RUNTIME_VARS.TMP) def setUp(self): - self.tmp_dir = tempfile.mkdtemp(dir=TMP) + self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + self.addCleanup(shutil.rmtree, self.tmp_dir, ignore_errors=True) os.makedirs(self.module_dir) self.lib_count = collections.defaultdict(int) # mapping of path -> count @@ -897,12 +981,11 @@ def setUp(self): copy.deepcopy(opts), tag='module', pack={'__utils__': self.utils, - '__proxy__': self.proxy, - '__salt__': self.minion_mods}) + '__proxy__': self.proxy, + '__salt__': self.minion_mods}) self.assertIn('{0}.top'.format(self.module_name), self.loader) def tearDown(self): - shutil.rmtree(self.tmp_dir) del self.tmp_dir del self.lib_paths del self.utils @@ -1072,7 +1155,12 @@ def test_states(self): - __grains__ - __context__ ''' - self._verify_globals(salt.loader.states(self.master_opts, {}, {}, {})) + opts = salt.config.minion_config(None) + opts['grains'] = salt.loader.grains(opts) + utils = salt.loader.utils(opts) + proxy = salt.loader.proxy(opts) + funcs = salt.loader.minion_mods(opts, utils=utils, proxy=proxy) + self._verify_globals(salt.loader.states(opts, funcs, utils, {}, proxy=proxy)) def test_renderers(self): ''' @@ -1133,19 +1221,42 @@ def test(): def setUpClass(cls): cls.opts = salt.config.minion_config(None) cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) + cls.proxy = salt.loader.proxy(cls.opts) + cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) + + @classmethod + def tearDownClass(cls): + del cls.opts + del cls.funcs + del cls.utils + del cls.proxy def setUp(self): # Setup the module - self.module_dir = tempfile.mkdtemp(dir=TMP) + self.module_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + self.addCleanup(shutil.rmtree, self.module_dir, ignore_errors=True) self.module_file = os.path.join(self.module_dir, '{0}.py'.format(self.module_name)) + def tearDown(self): + try: + delattr(self, 'loader') + except AttributeError: + pass + def _get_loader(self, order=None): opts = copy.deepcopy(self.opts) if order is not None: opts['optimization_order'] = order # Return a loader - return salt.loader.LazyLoader([self.module_dir], opts, tag='module') + return salt.loader.LazyLoader( + [self.module_dir], + opts, + pack={'__utils__': self.utils, + '__salt__': self.funcs, + '__proxy__': self.proxy}, + tag='module') def _get_module_filename(self): # The act of referencing the loader entry forces the module to be diff --git a/tests/unit/test_log.py b/tests/unit/test_log.py index 501ace8cc5c1..abf0cfbab300 100644 --- a/tests/unit/test_log.py +++ b/tests/unit/test_log.py @@ -16,11 +16,11 @@ # Import Salt Testing libs from tests.support.case import TestCase -from tests.support.helpers import TestsLoggingHandler +from tests.support.helpers import TstSuiteLoggingHandler # Import Salt libs -from salt.log import setup as saltlog -from salt.log.handlers import StreamHandler +from salt._logging.impl import SaltLoggingClass +from salt._logging.handlers import StreamHandler class TestLog(TestCase): @@ -31,19 +31,19 @@ class TestLog(TestCase): def test_issue_2853_regex_TypeError(self): # Now, python's logging logger class is ours. # Let's make sure we have at least one instance - log = saltlog.SaltLoggingClass(__name__) + log = SaltLoggingClass(__name__) # Test for a format which includes digits in name formatting. log_format = '[%(name)-15s] %(message)s' - handler = TestsLoggingHandler(format=log_format) + handler = TstSuiteLoggingHandler(format=log_format) log.addHandler(handler) - # Trigger TestsLoggingHandler.__enter__ + # Trigger TstSuiteLoggingHandler.__enter__ with handler: # Let's create another log instance to trigger salt's logging class # calculations. try: - saltlog.SaltLoggingClass('{0}.with_digits'.format(__name__)) + SaltLoggingClass('{0}.with_digits'.format(__name__)) except Exception as err: raise AssertionError( 'No exception should have been raised: {0}'.format(err) @@ -54,15 +54,15 @@ def test_issue_2853_regex_TypeError(self): # Test for a format which does not include digits in name formatting. log_format = '[%(name)s] %(message)s' - handler = TestsLoggingHandler(format=log_format) + handler = TstSuiteLoggingHandler(format=log_format) log.addHandler(handler) - # Trigger TestsLoggingHandler.__enter__ + # Trigger TstSuiteLoggingHandler.__enter__ with handler: # Let's create another log instance to trigger salt's logging class # calculations. try: - saltlog.SaltLoggingClass('{0}.without_digits'.format(__name__)) + SaltLoggingClass('{0}.without_digits'.format(__name__)) except Exception as err: raise AssertionError( 'No exception should have been raised: {0}'.format(err) @@ -75,7 +75,7 @@ def test_exc_info_on_loglevel(self): def raise_exception_on_purpose(): 1/0 # pylint: disable=pointless-statement - log = saltlog.SaltLoggingClass(__name__) + log = SaltLoggingClass(__name__) # Only stream2 should contain the traceback stream1 = StringIO() diff --git a/tests/unit/test_minion.py b/tests/unit/test_minion.py index b78e0f6abd8d..5b5c84b16f7a 100644 --- a/tests/unit/test_minion.py +++ b/tests/unit/test_minion.py @@ -9,8 +9,8 @@ import os # Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import patch, MagicMock from tests.support.mixins import AdaptedConfigurationTestCaseMixin from tests.support.helpers import skip_if_not_root # Import salt libs @@ -23,14 +23,15 @@ from salt.ext.six.moves import range -__opts__ = {} +class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): + def setUp(self): + self.opts = {} + self.addCleanup(delattr, self, 'opts') -@skipIf(NO_MOCK, NO_MOCK_REASON) -class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): def test_invalid_master_address(self): - with patch.dict(__opts__, {'ipv6': False, 'master': float('127.0'), 'master_port': '4555', 'retry_dns': False}): - self.assertRaises(SaltSystemExit, salt.minion.resolve_dns, __opts__) + with patch.dict(self.opts, {'ipv6': False, 'master': float('127.0'), 'master_port': '4555', 'retry_dns': False}): + self.assertRaises(SaltSystemExit, salt.minion.resolve_dns, self.opts) def test_source_int_name_local(self): ''' @@ -43,14 +44,14 @@ def test_source_int_name_local(self): 'netmask': '111.1.0.0', 'label': 'bond0', 'address': '111.1.0.1'}]}} - with patch.dict(__opts__, {'ipv6': False, 'master': '127.0.0.1', + with patch.dict(self.opts, {'ipv6': False, 'master': '127.0.0.1', 'master_port': '4555', 'file_client': 'local', 'source_interface_name': 'bond0.1234', 'source_ret_port': 49017, 'source_publish_port': 49018}), \ patch('salt.utils.network.interfaces', MagicMock(return_value=interfaces)): - assert salt.minion.resolve_dns(__opts__) == {'master_ip': '127.0.0.1', + assert salt.minion.resolve_dns(self.opts) == {'master_ip': '127.0.0.1', 'source_ip': '111.1.0.1', 'source_ret_port': 49017, 'source_publish_port': 49018, @@ -68,14 +69,14 @@ def test_source_int_name_remote(self): 'netmask': '111.1.0.0', 'label': 'bond0', 'address': '111.1.0.1'}]}} - with patch.dict(__opts__, {'ipv6': False, 'master': '127.0.0.1', + with patch.dict(self.opts, {'ipv6': False, 'master': '127.0.0.1', 'master_port': '4555', 'file_client': 'remote', 'source_interface_name': 'bond0.1234', 'source_ret_port': 49017, 'source_publish_port': 49018}), \ patch('salt.utils.network.interfaces', MagicMock(return_value=interfaces)): - assert salt.minion.resolve_dns(__opts__) == {'master_ip': '127.0.0.1', + assert salt.minion.resolve_dns(self.opts) == {'master_ip': '127.0.0.1', 'source_ret_port': 49017, 'source_publish_port': 49018, 'master_uri': 'tcp://127.0.0.1:4555'} @@ -90,7 +91,7 @@ def test_source_address(self): 'netmask': '111.1.0.0', 'label': 'bond0', 'address': '111.1.0.1'}]}} - with patch.dict(__opts__, {'ipv6': False, 'master': '127.0.0.1', + with patch.dict(self.opts, {'ipv6': False, 'master': '127.0.0.1', 'master_port': '4555', 'file_client': 'local', 'source_interface_name': '', 'source_address': '111.1.0.1', @@ -98,7 +99,7 @@ def test_source_address(self): 'source_publish_port': 49018}), \ patch('salt.utils.network.interfaces', MagicMock(return_value=interfaces)): - assert salt.minion.resolve_dns(__opts__) == {'source_publish_port': 49018, + assert salt.minion.resolve_dns(self.opts) == {'source_publish_port': 49018, 'source_ret_port': 49017, 'master_uri': 'tcp://127.0.0.1:4555', 'source_ip': '111.1.0.1', @@ -116,7 +117,7 @@ def test_handle_decoded_payload_jid_match_in_jid_queue(self): return None BEFORE any of the processes are spun up because we should be avoiding firing duplicate jobs. ''' - mock_opts = salt.config.DEFAULT_MINION_OPTS + mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() mock_data = {'fun': 'foo.bar', 'jid': 123} mock_jid_queue = [123] @@ -134,10 +135,10 @@ def test_handle_decoded_payload_jid_queue_addition(self): jid isn't already present in the jid_queue. ''' with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.join', MagicMock(return_value=True)): + patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ + patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)): mock_jid = 11111 - mock_opts = salt.config.DEFAULT_MINION_OPTS + mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() mock_data = {'fun': 'foo.bar', 'jid': mock_jid} mock_jid_queue = [123, 456] @@ -163,9 +164,9 @@ def test_handle_decoded_payload_jid_queue_reduced_minion_jid_queue_hwm(self): minion's jid_queue high water mark (minion_jid_queue_hwm) is hit. ''' with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.join', MagicMock(return_value=True)): - mock_opts = salt.config.DEFAULT_MINION_OPTS + patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ + patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)): + mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() mock_opts['minion_jid_queue_hwm'] = 2 mock_data = {'fun': 'foo.bar', 'jid': 789} @@ -191,12 +192,13 @@ def test_process_count_max(self): as per process_count_max. ''' with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.join', MagicMock(return_value=True)), \ + patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ + patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)), \ patch('salt.utils.minion.running', MagicMock(return_value=[])), \ patch('tornado.gen.sleep', MagicMock(return_value=tornado.concurrent.Future())): process_count_max = 10 - mock_opts = salt.config.DEFAULT_MINION_OPTS + mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() + mock_opts['__role'] = 'minion' mock_opts['minion_jid_queue_hwm'] = 100 mock_opts["process_count_max"] = process_count_max @@ -215,7 +217,7 @@ class SleepCalledException(Exception): mock_data = {'fun': 'foo.bar', 'jid': i} io_loop.run_sync(lambda data=mock_data: minion._handle_decoded_payload(data)) - self.assertEqual(salt.utils.process.SignalHandlingMultiprocessingProcess.start.call_count, i + 1) + self.assertEqual(salt.utils.process.SignalHandlingProcess.start.call_count, i + 1) self.assertEqual(len(minion.jid_queue), i + 1) salt.utils.minion.running.return_value += [i] @@ -225,7 +227,7 @@ class SleepCalledException(Exception): self.assertRaises(SleepCalledException, lambda: io_loop.run_sync(lambda: minion._handle_decoded_payload(mock_data))) - self.assertEqual(salt.utils.process.SignalHandlingMultiprocessingProcess.start.call_count, + self.assertEqual(salt.utils.process.SignalHandlingProcess.start.call_count, process_count_max) self.assertEqual(len(minion.jid_queue), process_count_max + 1) finally: @@ -237,8 +239,8 @@ def test_beacons_before_connect(self): ''' with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ patch('salt.minion.Minion.sync_connect_master', MagicMock(side_effect=RuntimeError('stop execution'))), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.join', MagicMock(return_value=True)): + patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ + patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)): mock_opts = self.get_config('minion', from_scratch=True) mock_opts['beacons_before_connect'] = True io_loop = tornado.ioloop.IOLoop() @@ -263,8 +265,8 @@ def test_scheduler_before_connect(self): ''' with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ patch('salt.minion.Minion.sync_connect_master', MagicMock(side_effect=RuntimeError('stop execution'))), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.join', MagicMock(return_value=True)): + patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ + patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)): mock_opts = self.get_config('minion', from_scratch=True) mock_opts['scheduler_before_connect'] = True io_loop = tornado.ioloop.IOLoop() @@ -285,8 +287,8 @@ def test_scheduler_before_connect(self): def test_when_ping_interval_is_set_the_callback_should_be_added_to_periodic_callbacks(self): with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ patch('salt.minion.Minion.sync_connect_master', MagicMock(side_effect=RuntimeError('stop execution'))), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingMultiprocessingProcess.join', MagicMock(return_value=True)): + patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ + patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)): mock_opts = self.get_config('minion', from_scratch=True) mock_opts['ping_interval'] = 10 io_loop = tornado.ioloop.IOLoop() @@ -310,11 +312,11 @@ def test_minion_retry_dns_count(self): Tests that the resolve_dns will retry dns look ups for a maximum of 3 times before raising a SaltMasterUnresolvableError exception. ''' - with patch.dict(__opts__, {'ipv6': False, 'master': 'dummy', + with patch.dict(self.opts, {'ipv6': False, 'master': 'dummy', 'master_port': '4555', 'retry_dns': 1, 'retry_dns_count': 3}): self.assertRaises(SaltMasterUnresolvableError, - salt.minion.resolve_dns, __opts__) + salt.minion.resolve_dns, self.opts) def test_gen_modules_executors(self): ''' @@ -338,9 +340,13 @@ def compile_pillar(self): minion.destroy() -@skipIf(NO_MOCK, NO_MOCK_REASON) class MinionAsyncTestCase(TestCase, AdaptedConfigurationTestCaseMixin, tornado.testing.AsyncTestCase): + def setUp(self): + super(MinionAsyncTestCase, self).setUp() + self.opts = {} + self.addCleanup(delattr, self, 'opts') + @skip_if_not_root def test_sock_path_len(self): ''' @@ -356,9 +362,9 @@ def test_sock_path_len(self): 'sock_dir': os.path.join(salt.syspaths.SOCK_DIR, 'minion'), 'extension_modules': '' } - with patch.dict(__opts__, opts): + with patch.dict(self.opts, opts): try: - event_publisher = event.AsyncEventPublisher(__opts__) + event_publisher = event.AsyncEventPublisher(self.opts) result = True except ValueError: # There are rare cases where we operate a closed socket, especially in containers. diff --git a/tests/unit/test_mock.py b/tests/unit/test_mock.py index 96c86599b7ed..90cf041d5eb0 100644 --- a/tests/unit/test_mock.py +++ b/tests/unit/test_mock.py @@ -15,8 +15,8 @@ from salt.ext import six # Import Salt Testing Libs -from tests.support.mock import patch, mock_open, NO_MOCK, NO_MOCK_REASON -from tests.support.unit import TestCase, skipIf +from tests.support.mock import patch, mock_open +from tests.support.unit import TestCase log = logging.getLogger(__name__) @@ -285,7 +285,6 @@ def _test_readlines_multifile(self, binary=False, multifile=False): pass -@skipIf(NO_MOCK, NO_MOCK_REASON) class MockOpenTestCase(TestCase, MockOpenMixin): ''' Tests for our mock_open helper to ensure that it behaves as closely as diff --git a/tests/unit/test_module_names.py b/tests/unit/test_module_names.py index 0e3863eda18a..13bba8048cad 100644 --- a/tests/unit/test_module_names.py +++ b/tests/unit/test_module_names.py @@ -15,7 +15,8 @@ # Import Salt Testing libs from tests.support.unit import TestCase -from tests.support.paths import CODE_DIR, test_mods +from tests.support.paths import list_test_mods +from tests.support.runtests import RUNTIME_VARS EXCLUDED_DIRS = [ os.path.join('tests', 'pkg'), @@ -42,14 +43,12 @@ os.path.join('tests', 'runtests.py'), os.path.join('tests', 'jenkins.py'), os.path.join('tests', 'salt-tcpdump.py'), - os.path.join('tests', 'conftest.py'), os.path.join('tests', 'packdump.py'), os.path.join('tests', 'consist.py'), os.path.join('tests', 'modparser.py'), os.path.join('tests', 'virtualname.py'), os.path.join('tests', 'committer_parser.py'), os.path.join('tests', 'zypp_plugin.py'), - os.path.join('tests', 'tox-helper.py'), os.path.join('tests', 'unit', 'transport', 'mixins.py'), os.path.join('tests', 'integration', 'utils', 'testprogram.py'), ] @@ -70,15 +69,15 @@ def test_module_name(self): Make sure all test modules conform to the test_*.py naming scheme ''' excluded_dirs, included_dirs = tuple(EXCLUDED_DIRS), tuple(INCLUDED_DIRS) - tests_dir = os.path.join(CODE_DIR, 'tests') + tests_dir = os.path.join(RUNTIME_VARS.CODE_DIR, 'tests') bad_names = [] - for root, dirs, files in salt.utils.path.os_walk(tests_dir): - reldir = os.path.relpath(root, CODE_DIR) + for root, _, files in salt.utils.path.os_walk(tests_dir): + reldir = os.path.relpath(root, RUNTIME_VARS.CODE_DIR) if (reldir.startswith(excluded_dirs) and not self._match_dirs(reldir, included_dirs)) \ or reldir.endswith('__pycache__'): continue for fname in files: - if fname == '__init__.py' or not fname.endswith('.py'): + if fname in ('__init__.py', 'conftest.py') or not fname.endswith('.py'): continue relpath = os.path.join(reldir, fname) if relpath in EXCLUDED_FILES: @@ -89,7 +88,7 @@ def test_module_name(self): error_msg = '\n\nPlease rename the following files:\n' for path in bad_names: directory, filename = path.rsplit(os.sep, 1) - filename, ext = os.path.splitext(filename) + filename, _ = os.path.splitext(filename) error_msg += ' {} -> {}/test_{}.py\n'.format(path, directory, filename.split('_test')[0]) error_msg += '\nIf you believe one of the entries above should be ignored, please add it to either\n' @@ -135,6 +134,7 @@ def test_module_name_source_match(self): 'integration.master.test_event_return', 'integration.minion.test_blackout', 'integration.minion.test_pillar', + 'integration.minion.test_executor', 'integration.minion.test_timeout', 'integration.modules.test_decorators', 'integration.modules.test_pkg', @@ -205,7 +205,7 @@ def _format_errors(errors): msg += ''.join(errors) return msg - for mod_name in test_mods(): + for mod_name in list_test_mods(): if mod_name in ignore: # Test module is being ignored, skip it continue @@ -227,7 +227,7 @@ def _format_errors(errors): '.'.join((flower[5:], 'py'))) # The full path to the file we expect to find - abspath = salt.utils.path.join(CODE_DIR, relpath) + abspath = salt.utils.path.join(RUNTIME_VARS.CODE_DIR, relpath) if not os.path.isfile(abspath): # Maybe this is in a dunder init? diff --git a/tests/unit/test_payload.py b/tests/unit/test_payload.py index 463835d4a6f0..099edb6bd7b7 100644 --- a/tests/unit/test_payload.py +++ b/tests/unit/test_payload.py @@ -16,7 +16,6 @@ # Import Salt Testing libs from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON # Import Salt libs from salt.utils import immutabletypes @@ -33,7 +32,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PayloadTestCase(TestCase): def assertNoOrderedDict(self, data): @@ -153,6 +151,17 @@ def test_mixed_dump_load(self): odata = payload.loads(sdata) self.assertEqual(edata, odata) + def test_recursive_dump_load(self): + ''' + Test recursive payloads are (mostly) serialized + ''' + payload = salt.payload.Serial('msgpack') + data = {'name': 'roscivs'} + data['data'] = data # Data all the things! + sdata = payload.dumps(data) + odata = payload.loads(sdata) + self.assertTrue('recursion' in odata['data'].lower()) + class SREQTestCase(TestCase): port = 8845 # TODO: dynamically assign a port? diff --git a/tests/unit/test_pillar.py b/tests/unit/test_pillar.py index 487101819108..4db02132a19c 100644 --- a/tests/unit/test_pillar.py +++ b/tests/unit/test_pillar.py @@ -13,10 +13,10 @@ import tempfile # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.helpers import with_tempdir -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch -from tests.support.paths import TMP +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.fileclient @@ -46,7 +46,6 @@ def list_states(*args, **kwargs): # pylint: enable=unused-argument,no-method-argument,method-hidden -@skipIf(NO_MOCK, NO_MOCK_REASON) class PillarTestCase(TestCase): def tearDown(self): @@ -351,7 +350,7 @@ def test_ext_pillar_first(self): 'kernel': 'Linux' } - tempdir = tempfile.mkdtemp(dir=TMP) + tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) try: sls_files = self._setup_test_topfile_sls_pillar_match( tempdir,) @@ -599,7 +598,7 @@ def test_topfile_order(self): } def _run_test(nodegroup_order, glob_order, expected): - tempdir = tempfile.mkdtemp(dir=TMP) + tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) try: sls_files = self._setup_test_topfile_sls( tempdir, @@ -820,7 +819,6 @@ def _setup_test_include_sls(self, tempdir): } -@skipIf(NO_MOCK, NO_MOCK_REASON) @patch('salt.transport.client.ReqChannel.factory', MagicMock()) class RemotePillarTestCase(TestCase): ''' @@ -938,7 +936,6 @@ def test_pillar_send_extra_minion_data_from_config(self): dictkey='pillar') -@skipIf(NO_MOCK, NO_MOCK_REASON) @patch('salt.transport.client.AsyncReqChannel.factory', MagicMock()) class AsyncRemotePillarTestCase(TestCase): ''' diff --git a/tests/unit/test_proxy_minion.py b/tests/unit/test_proxy_minion.py index d79bbcb790e6..c676a807fd6a 100644 --- a/tests/unit/test_proxy_minion.py +++ b/tests/unit/test_proxy_minion.py @@ -12,8 +12,7 @@ import tornado.testing # Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase from tests.support.mixins import AdaptedConfigurationTestCaseMixin # Import salt libs @@ -25,13 +24,12 @@ __opts__ = {} -@skipIf(NO_MOCK, NO_MOCK_REASON) class ProxyMinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): def test_post_master_init_metaproxy_called(self): ''' Tests that when the _post_master_ini function is called, _metaproxy_call is also called. ''' - mock_opts = salt.config.DEFAULT_MINION_OPTS + mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() mock_jid_queue = [123] proxy_minion = salt.minion.ProxyMinion(mock_opts, jid_queue=copy.copy(mock_jid_queue), io_loop=tornado.ioloop.IOLoop()) try: @@ -44,7 +42,7 @@ def test_handle_decoded_payload_metaproxy_called(self): ''' Tests that when the _handle_decoded_payload function is called, _metaproxy_call is also called. ''' - mock_opts = salt.config.DEFAULT_MINION_OPTS + mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() mock_data = {'fun': 'foo.bar', 'jid': 123} mock_jid_queue = [123] @@ -61,7 +59,7 @@ def test_handle_payload_metaproxy_called(self): ''' Tests that when the _handle_payload function is called, _metaproxy_call is also called. ''' - mock_opts = salt.config.DEFAULT_MINION_OPTS + mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() mock_data = {'fun': 'foo.bar', 'jid': 123} mock_jid_queue = [123] diff --git a/tests/unit/test_simple.py b/tests/unit/test_simple.py deleted file mode 100644 index 58a53cee3fd2..000000000000 --- a/tests/unit/test_simple.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- - -# Import Python libs -from __future__ import absolute_import - -# Import Salt Testing libs -from tests.support.unit import TestCase, expectedFailure - - -class SimpleTest(TestCase): - def test_success(self): - assert True - - @expectedFailure - def test_fail(self): - assert False diff --git a/tests/unit/test_state.py b/tests/unit/test_state.py index d6555a03bb2d..d5814d70e01c 100644 --- a/tests/unit/test_state.py +++ b/tests/unit/test_state.py @@ -10,15 +10,12 @@ import tempfile # Import Salt Testing libs -import tests.integration as integration from tests.support.unit import TestCase, skipIf from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) from tests.support.mixins import AdaptedConfigurationTestCaseMixin -from tests.support.paths import BASE_FILES +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.exceptions @@ -32,7 +29,6 @@ pytest = None -@skipIf(NO_MOCK, NO_MOCK_REASON) class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): ''' TestCase for the state compiler. @@ -76,7 +72,7 @@ def test_render_error_on_invalid_requisite(self): class HighStateTestCase(TestCase, AdaptedConfigurationTestCaseMixin): def setUp(self): - root_dir = tempfile.mkdtemp(dir=integration.TMP) + root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.state_tree_dir = os.path.join(root_dir, 'state_tree') cache_dir = os.path.join(root_dir, 'cachedir') for dpath in (root_dir, self.state_tree_dir, cache_dir): @@ -155,7 +151,7 @@ def test_find_sls_ids_with_exclude(self): ''' sls_dir = 'issue-47182' shutil.copytree( - os.path.join(BASE_FILES, sls_dir), + os.path.join(RUNTIME_VARS.BASE_FILES, sls_dir), os.path.join(self.state_tree_dir, sls_dir) ) shutil.move( @@ -172,7 +168,6 @@ def test_find_sls_ids_with_exclude(self): self.assertEqual(ret, [('somestuff', 'cmd')]) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(pytest is None, 'PyTest is missing') class StateReturnsTestCase(TestCase): ''' @@ -281,6 +276,60 @@ def test_format_slots_arg(self): mock.assert_called_once_with('fun_arg', fun_key='fun_val') self.assertEqual(cdata, {'args': ['fun_return'], 'kwargs': {'key': 'val'}}) + def test_format_slots_dict_arg(self): + ''' + Test the format slots is calling a slot specified in dict arg. + ''' + cdata = { + 'args': [ + {'subarg': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)'}, + ], + 'kwargs': { + 'key': 'val', + } + } + mock = MagicMock(return_value='fun_return') + with patch.dict(self.state_obj.functions, {'mod.fun': mock}): + self.state_obj.format_slots(cdata) + mock.assert_called_once_with('fun_arg', fun_key='fun_val') + self.assertEqual(cdata, {'args': [{'subarg': 'fun_return'}], 'kwargs': {'key': 'val'}}) + + def test_format_slots_listdict_arg(self): + ''' + Test the format slots is calling a slot specified in list containing a dict. + ''' + cdata = { + 'args': [[ + {'subarg': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)'}, + ]], + 'kwargs': { + 'key': 'val', + } + } + mock = MagicMock(return_value='fun_return') + with patch.dict(self.state_obj.functions, {'mod.fun': mock}): + self.state_obj.format_slots(cdata) + mock.assert_called_once_with('fun_arg', fun_key='fun_val') + self.assertEqual(cdata, {'args': [[{'subarg': 'fun_return'}]], 'kwargs': {'key': 'val'}}) + + def test_format_slots_liststr_arg(self): + ''' + Test the format slots is calling a slot specified in list containing a dict. + ''' + cdata = { + 'args': [[ + '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)', + ]], + 'kwargs': { + 'key': 'val', + } + } + mock = MagicMock(return_value='fun_return') + with patch.dict(self.state_obj.functions, {'mod.fun': mock}): + self.state_obj.format_slots(cdata) + mock.assert_called_once_with('fun_arg', fun_key='fun_val') + self.assertEqual(cdata, {'args': [['fun_return']], 'kwargs': {'key': 'val'}}) + def test_format_slots_kwarg(self): ''' Test the format slots is calling a slot specified in kwargs with corresponding arguments. @@ -360,3 +409,41 @@ def test_format_slots_malformed(self): self.state_obj.format_slots(cdata) mock.assert_not_called() self.assertEqual(cdata, sls_data) + + def test_slot_traverse_dict(self): + ''' + Test the slot parsing of dict response. + ''' + cdata = { + 'args': [ + 'arg', + ], + 'kwargs': { + 'key': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val).key1', + } + } + return_data = {'key1': 'value1'} + mock = MagicMock(return_value=return_data) + with patch.dict(self.state_obj.functions, {'mod.fun': mock}): + self.state_obj.format_slots(cdata) + mock.assert_called_once_with('fun_arg', fun_key='fun_val') + self.assertEqual(cdata, {'args': ['arg'], 'kwargs': {'key': 'value1'}}) + + def test_slot_append(self): + ''' + Test the slot parsing of dict response. + ''' + cdata = { + 'args': [ + 'arg', + ], + 'kwargs': { + 'key': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val).key1 ~ thing~', + } + } + return_data = {'key1': 'value1'} + mock = MagicMock(return_value=return_data) + with patch.dict(self.state_obj.functions, {'mod.fun': mock}): + self.state_obj.format_slots(cdata) + mock.assert_called_once_with('fun_arg', fun_key='fun_val') + self.assertEqual(cdata, {'args': ['arg'], 'kwargs': {'key': 'value1thing~'}}) diff --git a/tests/unit/test_template.py b/tests/unit/test_template.py index 97c7f533ee46..dea646c70357 100644 --- a/tests/unit/test_template.py +++ b/tests/unit/test_template.py @@ -7,8 +7,8 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock +from tests.support.unit import TestCase +from tests.support.mock import MagicMock # Import Salt libs from salt import template @@ -28,7 +28,6 @@ def test_compile_template_bad_type(self): ret = template.compile_template(['1', '2', '3'], None, None, None, None) self.assertDictEqual(ret, {}) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_compile_template_preserves_windows_newlines(self): ''' Test to ensure that a file with Windows newlines, when rendered by a diff --git a/tests/unit/test_version.py b/tests/unit/test_version.py index afe9051e254a..25f4e95ea4fc 100644 --- a/tests/unit/test_version.py +++ b/tests/unit/test_version.py @@ -17,7 +17,7 @@ from tests.support.unit import TestCase # Import Salt libs -from salt.version import SaltStackVersion +from salt.version import SaltStackVersion, versions_report class VersionTestCase(TestCase): @@ -71,3 +71,15 @@ def test_unparsable_version(self): with self.assertRaises(ValueError): SaltStackVersion.parse('Drunk') + + def test_version_report_lines(self): + ''' + Validate padding in versions report is correct + ''' + # Get a set of all version report name lenghts including padding + line_lengths = set([ + len(line.split(':')[0]) for line in list(versions_report())[4:] + if line != ' ' and line != 'System Versions:' + ]) + # Check that they are all the same size (only one element in the set) + assert len(line_lengths) == 1 diff --git a/tests/unit/test_virtualname.py b/tests/unit/test_virtualname.py index 4bc91fdf7155..85b62d110e40 100644 --- a/tests/unit/test_virtualname.py +++ b/tests/unit/test_virtualname.py @@ -18,8 +18,8 @@ import salt.ext.six as six # Import Salt Testing libs -from tests.support.paths import CODE_DIR from tests.support.unit import TestCase +from tests.support.runtests import RUNTIME_VARS log = logging.getLogger(__name__) @@ -86,14 +86,14 @@ def test_check_virtualname(self): Test that the virtualname is in __name__ of the module ''' errors = [] - for entry in os.listdir(os.path.join(CODE_DIR, 'salt/')): + for entry in os.listdir(os.path.join(RUNTIME_VARS.CODE_DIR, 'salt/')): name, path = os.path.splitext(os.path.basename(entry))[0], entry if name.startswith('.') or name.startswith('_') or not os.path.isdir(path): continue if name in ('cli', 'defaults', 'spm', 'daemons', 'ext', 'templates'): continue if name == 'cloud': - entry = os.path.join(CODE_DIR, 'salt', 'cloud', 'clouds') + entry = os.path.join(RUNTIME_VARS.CODE_DIR, 'salt', 'cloud', 'clouds') errors.extend(self._check_modules(entry)) for error in errors: log.critical(error) diff --git a/tests/unit/test_zypp_plugins.py b/tests/unit/test_zypp_plugins.py index a18f0484892c..86c89c6c5039 100644 --- a/tests/unit/test_zypp_plugins.py +++ b/tests/unit/test_zypp_plugins.py @@ -7,12 +7,10 @@ from __future__ import absolute_import # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) import os @@ -31,7 +29,6 @@ ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZyppPluginsTestCase(TestCase): ''' Test shipped libzypp plugins. diff --git a/tests/unit/tokens/test_localfs.py b/tests/unit/tokens/test_localfs.py index 45ed8a93521d..ac5113f013f8 100644 --- a/tests/unit/tokens/test_localfs.py +++ b/tests/unit/tokens/test_localfs.py @@ -10,9 +10,9 @@ import salt.tokens.localfs import salt.utils.files -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.helpers import with_tempdir -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch +from tests.support.mock import patch class CalledWith(object): @@ -29,7 +29,6 @@ def __call__(self, *args, **kwargs): return self.func(*args, **kwargs) -@skipIf(NO_MOCK, NO_MOCK_REASON) class WriteTokenTest(TestCase): @with_tempdir() diff --git a/tests/unit/tops/test_ext_nodes.py b/tests/unit/tops/test_ext_nodes.py index e74c87a5ca7f..3acef0aa7dd2 100644 --- a/tests/unit/tops/test_ext_nodes.py +++ b/tests/unit/tops/test_ext_nodes.py @@ -9,16 +9,15 @@ import textwrap # Import Salt Testing libs -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import patch, MagicMock, NO_MOCK, NO_MOCK_REASON +from tests.support.mock import patch, MagicMock # Import Salt libs import salt.utils.stringutils import salt.tops.ext_nodes as ext_nodes -@skipIf(NO_MOCK, NO_MOCK_REASON) class ExtNodesTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { diff --git a/tests/unit/transport/mixins.py b/tests/unit/transport/mixins.py index 322b7d9be30b..485c9e1ae985 100644 --- a/tests/unit/transport/mixins.py +++ b/tests/unit/transport/mixins.py @@ -100,4 +100,5 @@ def handle_pub(ret): self.wait(timeout=0.5) # close our pub_channel, to pass our FD checks + self.pub_channel.close() del self.pub_channel diff --git a/tests/unit/transport/test_ipc.py b/tests/unit/transport/test_ipc.py index d7495b93c70c..4deddf4a459f 100644 --- a/tests/unit/transport/test_ipc.py +++ b/tests/unit/transport/test_ipc.py @@ -26,8 +26,8 @@ from salt.ext.six.moves import range # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.mock import MagicMock -from tests.support.paths import TMP from tests.support.unit import skipIf log = logging.getLogger(__name__) @@ -41,7 +41,7 @@ class BaseIPCReqCase(tornado.testing.AsyncTestCase): def setUp(self): super(BaseIPCReqCase, self).setUp() #self._start_handlers = dict(self.io_loop._handlers) - self.socket_path = os.path.join(TMP, 'ipc_test.ipc') + self.socket_path = os.path.join(RUNTIME_VARS.TMP, 'ipc_test.ipc') self.server_channel = salt.transport.ipc.IPCMessageServer( self.socket_path, @@ -181,7 +181,7 @@ class IPCMessagePubSubCase(tornado.testing.AsyncTestCase): def setUp(self): super(IPCMessagePubSubCase, self).setUp() self.opts = {'ipc_write_buffer': 0} - self.socket_path = os.path.join(TMP, 'ipc_test.ipc') + self.socket_path = os.path.join(RUNTIME_VARS.TMP, 'ipc_test.ipc') self.pub_channel = self._get_pub_channel() self.sub_channel = self._get_sub_channel() diff --git a/tests/unit/transport/test_tcp.py b/tests/unit/transport/test_tcp.py index 525016f65a9d..bad6092a4a4a 100644 --- a/tests/unit/transport/test_tcp.py +++ b/tests/unit/transport/test_tcp.py @@ -22,7 +22,7 @@ import salt.transport.client import salt.exceptions from salt.ext.six.moves import range -from salt.transport.tcp import SaltMessageClientPool, SaltMessageClient +from salt.transport.tcp import SaltMessageClientPool, SaltMessageClient, TCPPubServerChannel # Import Salt Testing libs from tests.support.unit import TestCase, skipIf @@ -107,6 +107,7 @@ def setUp(self): self.channel = salt.transport.client.ReqChannel.factory(self.minion_config, crypt='clear') def tearDown(self): + self.channel.close() del self.channel @classmethod @@ -124,6 +125,7 @@ def setUp(self): self.channel = salt.transport.client.ReqChannel.factory(self.minion_config) def tearDown(self): + self.channel.close() del self.channel @classmethod @@ -223,7 +225,7 @@ def tearDown(self): for k, v in six.iteritems(self.io_loop._handlers): if self._start_handlers.get(k) != v: failures.append((k, v)) - if len(failures) > 0: + if failures: raise Exception('FDs still attached to the IOLoop: {0}'.format(failures)) del self.channel del self._start_handlers @@ -359,3 +361,73 @@ def stop(*args, **kwargs): orig_loop.stop = orig_loop.real_stop del orig_loop.real_stop del orig_loop.stop_called + + +class TCPPubServerChannelTest(TestCase, AdaptedConfigurationTestCaseMixin): + @patch('salt.master.SMaster.secrets') + @patch('salt.crypt.Crypticle') + @patch('salt.utils.asynchronous.SyncWrapper') + def test_publish_filtering(self, sync_wrapper, crypticle, secrets): + opts = self.get_temp_config('master') + opts["sign_pub_messages"] = False + channel = TCPPubServerChannel(opts) + + wrap = MagicMock() + crypt = MagicMock() + crypt.dumps.return_value = {"test": "value"} + + secrets.return_value = {"aes": {"secret": None}} + crypticle.return_value = crypt + sync_wrapper.return_value = wrap + + # try simple publish with glob tgt_type + channel.publish({"test": "value", "tgt_type": "glob", "tgt": "*"}) + payload = wrap.send.call_args[0][0] + + # verify we send it without any specific topic + assert "topic_lst" not in payload + + # try simple publish with list tgt_type + channel.publish({"test": "value", "tgt_type": "list", "tgt": ["minion01"]}) + payload = wrap.send.call_args[0][0] + + # verify we send it with correct topic + assert "topic_lst" in payload + self.assertEqual(payload["topic_lst"], ["minion01"]) + + # try with syndic settings + opts['order_masters'] = True + channel.publish({"test": "value", "tgt_type": "list", "tgt": ["minion01"]}) + payload = wrap.send.call_args[0][0] + + # verify we send it without topic for syndics + assert "topic_lst" not in payload + + @patch('salt.utils.minions.CkMinions.check_minions') + @patch('salt.master.SMaster.secrets') + @patch('salt.crypt.Crypticle') + @patch('salt.utils.asynchronous.SyncWrapper') + def test_publish_filtering_str_list(self, sync_wrapper, crypticle, secrets, check_minions): + opts = self.get_temp_config('master') + opts["sign_pub_messages"] = False + channel = TCPPubServerChannel(opts) + + wrap = MagicMock() + crypt = MagicMock() + crypt.dumps.return_value = {"test": "value"} + + secrets.return_value = {"aes": {"secret": None}} + crypticle.return_value = crypt + sync_wrapper.return_value = wrap + check_minions.return_value = {"minions": ["minion02"]} + + # try simple publish with list tgt_type + channel.publish({"test": "value", "tgt_type": "list", "tgt": "minion02"}) + payload = wrap.send.call_args[0][0] + + # verify we send it with correct topic + assert "topic_lst" in payload + self.assertEqual(payload["topic_lst"], ["minion02"]) + + # verify it was correctly calling check_minions + check_minions.assert_called_with("minion02", tgt_type="list") diff --git a/tests/unit/transport/test_zeromq.py b/tests/unit/transport/test_zeromq.py index bbf0441d80f9..c8cf3a930af0 100644 --- a/tests/unit/transport/test_zeromq.py +++ b/tests/unit/transport/test_zeromq.py @@ -39,7 +39,7 @@ from salt.transport.zeromq import AsyncReqMessageClientPool # Import test support libs -from tests.support.paths import TMP_CONF_DIR +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase, skipIf from tests.support.helpers import flaky, get_unused_localhost_port from tests.support.mixins import AdaptedConfigurationTestCaseMixin @@ -133,6 +133,7 @@ def setUp(self): self.channel = salt.transport.client.ReqChannel.factory(self.minion_config, crypt='clear') def tearDown(self): + self.channel.close() del self.channel @classmethod @@ -162,6 +163,7 @@ def setUp(self): self.channel = salt.transport.client.ReqChannel.factory(self.minion_config) def tearDown(self): + self.channel.close() del self.channel @classmethod @@ -217,7 +219,7 @@ def setUpClass(cls): 'tcp_master_workers': tcp_master_workers} ) - cls.minion_config = salt.config.minion_config(os.path.join(TMP_CONF_DIR, 'minion')) + cls.minion_config = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion')) cls.minion_config = cls.get_temp_config( 'minion', **{'transport': 'zeromq', @@ -461,7 +463,6 @@ def _gather_results(opts, pub_uri, results, timeout=120, messages=None): break last_msg = time.time() results.append(payload['jid']) - return results @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') def test_publish_to_pubserv_ipc(self): @@ -613,6 +614,7 @@ def _send_small(opts, sid, num=10): for i in range(num): load = {'tgt_type': 'glob', 'tgt': '*', 'jid': '{}-{}'.format(sid, i)} server_channel.publish(load) + server_channel.close() @staticmethod def _send_large(opts, sid, num=10, size=250000 * 3): @@ -620,6 +622,7 @@ def _send_large(opts, sid, num=10, size=250000 * 3): for i in range(num): load = {'tgt_type': 'glob', 'tgt': '*', 'jid': '{}-{}'.format(sid, i), 'xdata': '0' * size} server_channel.publish(load) + server_channel.close() def test_issue_36469_tcp(self): ''' @@ -646,37 +649,6 @@ def test_issue_36469_tcp(self): executor.submit(self._send_small, opts, 3) executor.submit(self._send_large, opts, 4) expect = ['{}-{}'.format(a, b) for a in range(10) for b in (1, 2, 3, 4)] - server_channel.publish({'tgt_type': 'glob', 'tgt': '*', 'stop': True}) - gather.join() - server_channel.pub_close() - assert len(results) == send_num, (len(results), set(expect).difference(results)) - - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') - def test_issue_36469_udp(self): - ''' - Test sending both large and small messags to publisher using UDP - - https://github.com/saltstack/salt/issues/36469 - ''' - opts = dict(self.master_config, ipc_mode='udp', pub_hwm=0) - server_channel = salt.transport.zeromq.ZeroMQPubServerChannel(opts) - server_channel.pre_fork(self.process_manager, kwargs={ - 'log_queue': salt.log.setup.get_multiprocessing_logging_queue() - }) - send_num = 10 * 4 - expect = [] - results = [] - pub_uri = 'tcp://{interface}:{publish_port}'.format(**opts) - # Allow time for server channel to start, especially on windows - time.sleep(2) - gather = threading.Thread(target=self._gather_results, args=(self.minion_config, pub_uri, results,)) - gather.start() - with ThreadPoolExecutor(max_workers=4) as executor: - executor.submit(self._send_small, opts, 1) - executor.submit(self._send_small, opts, 2) - executor.submit(self._send_small, opts, 3) - executor.submit(self._send_large, opts, 4) - expect = ['{}-{}'.format(a, b) for a in range(10) for b in (1, 2, 3, 4)] time.sleep(0.1) server_channel.publish({'tgt_type': 'glob', 'tgt': '*', 'stop': True}) gather.join() diff --git a/tests/unit/utils/test_args.py b/tests/unit/utils/test_args.py index e4f110db3ed3..b68a37854af8 100644 --- a/tests/unit/utils/test_args.py +++ b/tests/unit/utils/test_args.py @@ -11,11 +11,9 @@ import salt.utils.args # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( DEFAULT, - NO_MOCK, - NO_MOCK_REASON, patch ) @@ -65,7 +63,6 @@ def dummy_func(first, second, third, fourth='fifth'): ret = salt.utils.args.arg_lookup(dummy_func) self.assertEqual(expected_dict, ret) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_format_call(self): with patch('salt.utils.args.arg_lookup') as arg_lookup: def dummy_func(first=None, second=None, third=None): @@ -120,7 +117,6 @@ def foo2(one, two, three=3): r'foo2 takes at least 2 arguments \(1 given\)'): salt.utils.args.format_call(foo2, dict(one=1)) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_argspec_report(self): def _test_spec(arg1, arg2, kwarg1=None): pass diff --git a/tests/unit/utils/test_botomod.py b/tests/unit/utils/test_botomod.py index 1aa45c5a815b..7cd080e5b517 100644 --- a/tests/unit/utils/test_botomod.py +++ b/tests/unit/utils/test_botomod.py @@ -7,8 +7,8 @@ # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock -from tests.support.paths import TESTS_DIR +from tests.support.mock import patch, MagicMock +from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.utils.botomod as botomod @@ -21,7 +21,7 @@ # pylint: disable=import-error try: import boto - boto.ENDPOINTS_PATH = os.path.join(TESTS_DIR, 'unit/files/endpoints.json') + boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') import boto.exception from boto.exception import BotoServerError @@ -168,7 +168,6 @@ def test_partial(self): self.assertEqual(cache_id(resource_name), resource_id) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(HAS_BOTO is False, 'The boto module must be installed.') @skipIf(HAS_MOTO is False, 'The moto module must be installed.') @skipIf(_has_required_boto() is False, 'The boto module must be greater than' diff --git a/tests/unit/utils/test_cache.py b/tests/unit/utils/test_cache.py index 65e83408dbae..598dd63ef788 100644 --- a/tests/unit/utils/test_cache.py +++ b/tests/unit/utils/test_cache.py @@ -14,8 +14,7 @@ import shutil # Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase # Import salt libs import salt.config @@ -68,7 +67,7 @@ def test_smoke_context(self): if os.path.exists(os.path.join(tempfile.gettempdir(), 'context')): self.skipTest('Context dir already exists') else: - opts = salt.config.DEFAULT_MINION_OPTS + opts = salt.config.DEFAULT_MINION_OPTS.copy() opts['cachedir'] = tempfile.gettempdir() context_cache = cache.ContextCache(opts, 'cache_test') @@ -84,7 +83,7 @@ def test_context_wrapper(self): with a context cache can store and retrieve its contextual data ''' - opts = salt.config.DEFAULT_MINION_OPTS + opts = salt.config.DEFAULT_MINION_OPTS.copy() opts['cachedir'] = tempfile.gettempdir() ll_ = salt.loader.LazyLoader( @@ -103,7 +102,6 @@ def test_context_wrapper(self): __opts__ = {'cachedir': '/tmp'} -@skipIf(NO_MOCK, NO_MOCK_REASON) class ContextCacheTest(TestCase): ''' Test case for salt.utils.cache.ContextCache diff --git a/tests/unit/utils/test_cloud.py b/tests/unit/utils/test_cloud.py index 101a8a5b016a..392838147e65 100644 --- a/tests/unit/utils/test_cloud.py +++ b/tests/unit/utils/test_cloud.py @@ -12,63 +12,70 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals import os +import shutil import tempfile # Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.paths import TMP, CODE_DIR +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf, SkipTest # Import salt libs import salt.utils.cloud as cloud import salt.utils.platform from salt.ext import six -GPG_KEYDIR = os.path.join(TMP, 'gpg-keydir') - -# The keyring library uses `getcwd()`, let's make sure we in a good directory -# before importing keyring -if not os.path.isdir(GPG_KEYDIR): - os.makedirs(GPG_KEYDIR) - -os.chdir(GPG_KEYDIR) - -# Import external deps -try: - import keyring - import keyring.backend - - class TestKeyring(keyring.backend.KeyringBackend): - ''' - A test keyring which always outputs same password - ''' - def __init__(self): - self.__storage = {} - - def supported(self): - return 0 - - def set_password(self, servicename, username, password): - self.__storage.setdefault(servicename, {}).update({username: password}) - return 0 - - def get_password(self, servicename, username): - return self.__storage.setdefault(servicename, {}).get(username, None) - - def delete_password(self, servicename, username): - self.__storage.setdefault(servicename, {}).pop(username, None) - return 0 - - # set the keyring for keyring lib - keyring.set_keyring(TestKeyring()) - HAS_KEYRING = True -except ImportError: - HAS_KEYRING = False - -os.chdir(CODE_DIR) - class CloudUtilsTestCase(TestCase): + @classmethod + def setUpClass(cls): + old_cwd = os.getcwd() + cls.gpg_keydir = gpg_keydir = os.path.join(RUNTIME_VARS.TMP, 'gpg-keydir') + try: + # The keyring library uses `getcwd()`, let's make sure we in a good directory + # before importing keyring + if not os.path.isdir(gpg_keydir): + os.makedirs(gpg_keydir) + os.chdir(gpg_keydir) + + # Late import because of the above reason + import keyring + import keyring.backend + + class CustomKeyring(keyring.backend.KeyringBackend): + ''' + A test keyring which always outputs same password + ''' + def __init__(self): + self.__storage = {} + + def supported(self): + return 0 + + def set_password(self, servicename, username, password): + self.__storage.setdefault(servicename, {}).update({username: password}) + return 0 + + def get_password(self, servicename, username): + return self.__storage.setdefault(servicename, {}).get(username, None) + + def delete_password(self, servicename, username): + self.__storage.setdefault(servicename, {}).pop(username, None) + return 0 + + # set the keyring for keyring lib + keyring.set_keyring(CustomKeyring()) + except ImportError: + raise SkipTest('The "keyring" python module is not installed') + finally: + os.chdir(old_cwd) + + @classmethod + def tearDownClass(cls): + if os.path.exists(cls.gpg_keydir): + shutil.rmtree(cls.gpg_keydir) + del cls.gpg_keydir + def test_ssh_password_regex(self): '''Test matching ssh password patterns''' for pattern in ('Password for root@127.0.0.1:', @@ -87,11 +94,12 @@ def test_ssh_password_regex(self): cloud.SSH_PASSWORD_PROMP_RE.match(pattern.lower().strip()), None ) - @skipIf(HAS_KEYRING is False, 'The "keyring" python module is not installed') def test__save_password_in_keyring(self): ''' Test storing password in the keyring ''' + # Late import + import keyring cloud._save_password_in_keyring( 'salt.cloud.provider.test_case_provider', 'fake_username', @@ -107,8 +115,9 @@ def test__save_password_in_keyring(self): ) self.assertEqual(stored_pw, 'fake_password_c8231') - @skipIf(HAS_KEYRING is False, 'The "keyring" python module is not installed') def test_retrieve_password_from_keyring(self): + # Late import + import keyring keyring.set_password( 'salt.cloud.provider.test_case_provider', 'fake_username', diff --git a/tests/unit/utils/test_configparser.py b/tests/unit/utils/test_configparser.py index dc8cf4339df7..52864d9c2535 100644 --- a/tests/unit/utils/test_configparser.py +++ b/tests/unit/utils/test_configparser.py @@ -15,8 +15,8 @@ log = logging.getLogger(__name__) # Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase -from tests.support.paths import TMP # Import salt libs import salt.utils.files @@ -56,8 +56,8 @@ class TestGitConfigParser(TestCase): Tests for salt.utils.configparser.GitConfigParser ''' maxDiff = None - orig_config = os.path.join(TMP, 'test_gitconfig.orig') - new_config = os.path.join(TMP, 'test_gitconfig.new') + orig_config = os.path.join(RUNTIME_VARS.TMP, 'test_gitconfig.orig') + new_config = os.path.join(RUNTIME_VARS.TMP, 'test_gitconfig.new') remote = 'remote "origin"' def tearDown(self): diff --git a/tests/unit/utils/test_data.py b/tests/unit/utils/test_data.py index 030f22b202cc..892ef029c186 100644 --- a/tests/unit/utils/test_data.py +++ b/tests/unit/utils/test_data.py @@ -11,8 +11,8 @@ import salt.utils.data import salt.utils.stringutils from salt.utils.odict import OrderedDict -from tests.support.unit import TestCase, skipIf, LOREM_IPSUM -from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase, LOREM_IPSUM +from tests.support.mock import patch from salt.ext.six.moves import builtins # pylint: disable=import-error,redefined-builtin from salt.ext import six @@ -244,6 +244,30 @@ def test_compare_dicts(self): expected_ret = {'foo': {'new': 'woz', 'old': 'bar'}} self.assertDictEqual(ret, expected_ret) + def test_compare_lists_no_change(self): + ret = salt.utils.data.compare_lists(old=[1, 2, 3, 'a', 'b', 'c'], + new=[1, 2, 3, 'a', 'b', 'c']) + expected = {} + self.assertDictEqual(ret, expected) + + def test_compare_lists_changes(self): + ret = salt.utils.data.compare_lists(old=[1, 2, 3, 'a', 'b', 'c'], + new=[1, 2, 4, 'x', 'y', 'z']) + expected = {'new': [4, 'x', 'y', 'z'], 'old': [3, 'a', 'b', 'c']} + self.assertDictEqual(ret, expected) + + def test_compare_lists_changes_new(self): + ret = salt.utils.data.compare_lists(old=[1, 2, 3], + new=[1, 2, 3, 'x', 'y', 'z']) + expected = {'new': ['x', 'y', 'z']} + self.assertDictEqual(ret, expected) + + def test_compare_lists_changes_old(self): + ret = salt.utils.data.compare_lists(old=[1, 2, 3, 'a', 'b', 'c'], + new=[1, 2, 3]) + expected = {'old': ['a', 'b', 'c']} + self.assertDictEqual(ret, expected) + def test_decode(self): ''' Companion to test_decode_to_str, they should both be kept up-to-date @@ -418,7 +442,6 @@ def test_decode_to_str(self): keep=False, to_str=True) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_decode_fallback(self): ''' Test fallback to utf-8 @@ -563,7 +586,6 @@ def test_encode_keep(self): keep=False, preserve_tuples=True) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_encode_fallback(self): ''' Test fallback to utf-8 diff --git a/tests/unit/utils/test_dateutils.py b/tests/unit/utils/test_dateutils.py index 1f750b1968fc..af23927ceaa0 100644 --- a/tests/unit/utils/test_dateutils.py +++ b/tests/unit/utils/test_dateutils.py @@ -11,8 +11,6 @@ from tests.support.unit import TestCase, skipIf from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt libs @@ -28,7 +26,6 @@ class DateutilsTestCase(TestCase): - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_date_cast(self): now = datetime.datetime.now() with patch('datetime.datetime'): diff --git a/tests/unit/utils/test_decorators.py b/tests/unit/utils/test_decorators.py index ceb1167508ff..9a0f20c8d9a9 100644 --- a/tests/unit/utils/test_decorators.py +++ b/tests/unit/utils/test_decorators.py @@ -11,8 +11,8 @@ import salt.utils.decorators as decorators from salt.version import SaltStackVersion from salt.exceptions import CommandExecutionError, SaltConfigurationError -from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch +from tests.support.unit import TestCase +from tests.support.mock import patch class DummyLogger(object): @@ -29,7 +29,6 @@ def _log(self, msg): self._messages.append(msg) -@skipIf(NO_MOCK, NO_MOCK_REASON) class DecoratorsTest(TestCase): ''' Testing decorators. diff --git a/tests/unit/utils/test_dictupdate.py b/tests/unit/utils/test_dictupdate.py index 4e8a7a540846..222f8fe321fa 100644 --- a/tests/unit/utils/test_dictupdate.py +++ b/tests/unit/utils/test_dictupdate.py @@ -9,6 +9,8 @@ # Import Salt libs import salt.utils.dictupdate as dictupdate +from salt.utils.odict import OrderedDict +from salt.exceptions import SaltInvocationError class UtilDictupdateTestCase(TestCase): @@ -91,7 +93,8 @@ def test_update(self): mdict = copy.deepcopy(self.dict1) mdict['C']['F']['G'] = ['a', 'b'] res = dictupdate.update(copy.deepcopy(mdict), - {'C': {'F': {'G': ['c', 'd']}}}, merge_lists=False) + {'C': {'F': {'G': ['c', 'd']}}}, + merge_lists=False) mdict['C']['F']['G'] = ['c', 'd'] self.assertEqual(res, mdict) @@ -99,7 +102,8 @@ def test_update(self): mdict = copy.deepcopy(self.dict1) mdict['C']['F']['G'] = ['a', 'b'] res = dictupdate.update(copy.deepcopy(mdict), - {'C': {'F': {'G': ['c', 'd']}}}, merge_lists=True) + {'C': {'F': {'G': ['c', 'd']}}}, + merge_lists=True) mdict['C']['F']['G'] = ['a', 'b', 'c', 'd'] self.assertEqual(res, mdict) @@ -107,7 +111,8 @@ def test_update(self): mdict = copy.deepcopy(self.dict1) mdict['C']['F']['G'] = ['a', 'b'] res = dictupdate.update(copy.deepcopy(mdict), - {'C': {'F': {'G': ['d', 'c', 'b', 'a']}}}, merge_lists=True) + {'C': {'F': {'G': ['d', 'c', 'b', 'a']}}}, + merge_lists=True) mdict['C']['F']['G'] = ['a', 'b', 'd', 'c'] self.assertEqual(res, mdict) @@ -195,3 +200,127 @@ def test_merge_list_append(self): mdict1['A'] = ['B'] ret = dictupdate.merge_list(mdict1, {'A': ['b', 'c']}) self.assertEqual({'A': [['B'], ['b', 'c']], 'C': {'D': 'E', 'F': {'I': 'J', 'G': 'H'}}}, ret) + + +class UtilDeepDictUpdateTestCase(TestCase): + + dict1 = {'A': 'B', 'C': {'D': 'E', 'F': {'G': 'H', 'I': 'J'}}} + + def test_deep_set_overwrite(self): + ''' + Test overwriting an existing value. + ''' + mdict = copy.deepcopy(self.dict1) + res = dictupdate.set_dict_key_value(mdict, 'C:F', 'foo') + self.assertEqual({'A': 'B', 'C': {'D': 'E', 'F': 'foo'}}, res) + # Verify modify-in-place + self.assertEqual({'A': 'B', 'C': {'D': 'E', 'F': 'foo'}}, mdict) + + # Test using alternative delimiter + res = dictupdate.set_dict_key_value(mdict, 'C/F', {'G': 'H', 'I': 'J'}, delimiter='/') + self.assertEqual(self.dict1, res) + + # Test without using a delimiter in the keys + res = dictupdate.set_dict_key_value(mdict, 'C', None) + self.assertEqual({'A': 'B', 'C': None}, res) + + def test_deep_set_create(self): + ''' + Test creating new nested keys. + ''' + mdict = copy.deepcopy(self.dict1) + res = dictupdate.set_dict_key_value(mdict, 'K:L:M', 'Q') + self.assertEqual({'A': 'B', 'C': {'D': 'E', 'F': {'G': 'H', 'I': 'J'}}, 'K': {'L': {'M': 'Q'}}}, res) + + def test_deep_set_ordered_dicts(self): + ''' + Test creating new nested ordereddicts. + ''' + res = dictupdate.set_dict_key_value({}, 'A:B', 'foo', ordered_dict=True) + self.assertEqual({'A': OrderedDict([('B', 'foo')])}, res) + + def test_deep_append(self): + ''' + Test appending to a list. + ''' + sdict = {'bar': {'baz': [1, 2]}} + res = dictupdate.append_dict_key_value(sdict, 'bar:baz', 42) + self.assertEqual({'bar': {'baz': [1, 2, 42]}}, res) + # Append with alternate delimiter + res = dictupdate.append_dict_key_value(sdict, 'bar~baz', 43, delimiter='~') + self.assertEqual({'bar': {'baz': [1, 2, 42, 43]}}, res) + # Append to a not-yet existing list + res = dictupdate.append_dict_key_value({}, 'foo:bar:baz', 42) + self.assertEqual({'foo': {'bar': {'baz': [42]}}}, res) + + def test_deep_extend(self): + ''' + Test extending a list. + Note that the provided value (to extend with) will be coerced to a list + if this is not already a list. This can cause unexpected behaviour. + ''' + sdict = {'bar': {'baz': [1, 2]}} + res = dictupdate.extend_dict_key_value(sdict, 'bar:baz', [42, 42]) + self.assertEqual({'bar': {'baz': [1, 2, 42, 42]}}, res) + + # Extend a not-yet existing list + res = dictupdate.extend_dict_key_value({}, 'bar:baz:qux', [42]) + self.assertEqual({'bar': {'baz': {'qux': [42]}}}, res) + + # Extend with a dict (remember, foo has been updated in the first test) + res = dictupdate.extend_dict_key_value(sdict, 'bar:baz', {'qux': 'quux'}) + self.assertEqual({'bar': {'baz': [1, 2, 42, 42, 'qux']}}, res) + + def test_deep_extend_illegal_addition(self): + ''' + Test errorhandling extending lists with illegal types. + ''' + # Extend with an illegal type + for extend_with in [42, None]: + with self.assertRaisesRegex(SaltInvocationError, + r"Cannot extend {} with a {}." + "".format(type([]), type(extend_with))): + dictupdate.extend_dict_key_value({}, 'foo', extend_with) + + def test_deep_extend_illegal_source(self): + ''' + Test errorhandling extending things that are not a list. + ''' + # Extend an illegal type + for extend_this in [{}, 42, 'bar']: + with self.assertRaisesRegex(SaltInvocationError, + r"The last key contains a {}, which cannot extend." + "".format(type(extend_this))): + dictupdate.extend_dict_key_value({'foo': extend_this}, 'foo', [42]) + + def test_deep_update(self): + ''' + Test updating a (sub)dict. + ''' + mdict = copy.deepcopy(self.dict1) + res = dictupdate.update_dict_key_value(mdict, 'C:F', {'foo': 'bar', 'qux': 'quux'}) + self.assertEqual({'A': 'B', 'C': {'D': 'E', 'F': {'G': 'H', 'I': 'J', 'foo': 'bar', 'qux': 'quux'}}}, res) + + # Test updating a non-existing subkey + res = dictupdate.update_dict_key_value({}, 'foo:bar:baz', {'qux': 'quux'}) + self.assertEqual({'foo': {'bar': {'baz': {'qux': 'quux'}}}}, res) + # Test updating a non-existing subkey, with a different delimiter + res = dictupdate.update_dict_key_value({}, 'foo bar baz', {'qux': 'quux'}, delimiter=' ') + self.assertEqual({'foo': {'bar': {'baz': {'qux': 'quux'}}}}, res) + + def test_deep_update_illegal_update(self): + ''' + Test errorhandling updating a (sub)dict with illegal types. + ''' + # Update with an illegal type + for update_with in [42, None, [42], 'bar']: + with self.assertRaisesRegex(SaltInvocationError, + r"Cannot update {} with a {}." + "".format(type({}), type(update_with))): + dictupdate.update_dict_key_value({}, 'foo', update_with) + # Again, but now using OrderedDicts + for update_with in [42, None, [42], 'bar']: + with self.assertRaisesRegex(SaltInvocationError, + r"Cannot update {} with a {}." + "".format(type(OrderedDict()), type(update_with))): + dictupdate.update_dict_key_value({}, 'foo', update_with, ordered_dict=True) diff --git a/tests/unit/utils/test_dns.py b/tests/unit/utils/test_dns.py index b7c46446e532..7608cfbcdbe1 100644 --- a/tests/unit/utils/test_dns.py +++ b/tests/unit/utils/test_dns.py @@ -20,7 +20,7 @@ # Testing from tests.support.unit import skipIf, TestCase -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, MagicMock, patch +from tests.support.mock import MagicMock, patch class DNShelpersCase(TestCase): @@ -153,7 +153,6 @@ def test_data2group(self): self.assertEqual(group, res) -@skipIf(NO_MOCK, NO_MOCK_REASON) class DNSlookupsCase(TestCase): ''' Test the lookup result parsers diff --git a/tests/unit/utils/test_etcd_util.py b/tests/unit/utils/test_etcd_util.py index ce7f58b97f63..90ad36182118 100644 --- a/tests/unit/utils/test_etcd_util.py +++ b/tests/unit/utils/test_etcd_util.py @@ -11,8 +11,6 @@ from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -32,7 +30,6 @@ @skipIf(HAS_URLLIB3 is False, 'urllib3 module must be installed.') @skipIf(HAS_ETCD is False, 'python-etcd module must be installed.') -@skipIf(NO_MOCK, NO_MOCK_REASON) class EtcdUtilTestCase(TestCase): ''' Test cases for salt.utils.etcd_util diff --git a/tests/unit/utils/test_event.py b/tests/unit/utils/test_event.py index a06982212b54..e3a1d079fcb6 100644 --- a/tests/unit/utils/test_event.py +++ b/tests/unit/utils/test_event.py @@ -12,86 +12,41 @@ import os import hashlib import time +import shutil import warnings -from tornado.testing import AsyncTestCase -import zmq -import zmq.eventloop.ioloop -# support pyzmq 13.0.x, TODO: remove once we force people to 14.0.x -if not hasattr(zmq.eventloop.ioloop, 'ZMQIOLoop'): - zmq.eventloop.ioloop.ZMQIOLoop = zmq.eventloop.ioloop.IOLoop -from contextlib import contextmanager -from multiprocessing import Process # Import Salt Testing libs from tests.support.unit import expectedFailure, skipIf, TestCase +from tests.support.runtests import RUNTIME_VARS +from tests.support.events import eventpublisher_process, eventsender_process # Import salt libs import salt.config import salt.utils.event import salt.utils.stringutils -import tests.integration as integration -from salt.utils.process import clean_proc # Import 3rd-+arty libs +from tornado.testing import AsyncTestCase +import zmq +import zmq.eventloop.ioloop +# support pyzmq 13.0.x, TODO: remove once we force people to 14.0.x +if not hasattr(zmq.eventloop.ioloop, 'ZMQIOLoop'): + zmq.eventloop.ioloop.ZMQIOLoop = zmq.eventloop.ioloop.IOLoop from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin from tests.support.processes import terminate_process -SOCK_DIR = os.path.join(integration.TMP, 'test-socks') - NO_LONG_IPC = False if getattr(zmq, 'IPC_PATH_MAX_LEN', 103) <= 103: NO_LONG_IPC = True -@contextmanager -def eventpublisher_process(): - proc = salt.utils.event.EventPublisher({'sock_dir': SOCK_DIR}) - proc.start() - try: - if os.environ.get('TRAVIS_PYTHON_VERSION', None) is not None: - # Travis is slow - time.sleep(10) - else: - time.sleep(2) - yield - finally: - clean_proc(proc) - - -class EventSender(Process): - def __init__(self, data, tag, wait): - super(EventSender, self).__init__() - self.data = data - self.tag = tag - self.wait = wait - - def run(self): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=False) - time.sleep(self.wait) - me.fire_event(self.data, self.tag) - # Wait a few seconds before tearing down the zmq context - if os.environ.get('TRAVIS_PYTHON_VERSION', None) is not None: - # Travis is slow - time.sleep(10) - else: - time.sleep(2) - - -@contextmanager -def eventsender_process(data, tag, wait=0): - proc = EventSender(data, tag, wait) - proc.start() - try: - yield - finally: - clean_proc(proc) - - @skipIf(NO_LONG_IPC, "This system does not support long IPC paths. Skipping event tests!") class TestSaltEvent(TestCase): def setUp(self): - if not os.path.exists(SOCK_DIR): - os.makedirs(SOCK_DIR) + self.sock_dir = os.path.join(RUNTIME_VARS.TMP, 'test-socks') + if not os.path.exists(self.sock_dir): + os.makedirs(self.sock_dir) + self.addCleanup(shutil.rmtree, self.sock_dir, ignore_errors=True) def assertGotEvent(self, evt, data, msg=None): self.assertIsNotNone(evt, msg) @@ -102,28 +57,28 @@ def assertGotEvent(self, evt, data, msg=None): self.assertEqual(data[key], evt[key], assertMsg) def test_master_event(self): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=False) + me = salt.utils.event.MasterEvent(self.sock_dir, listen=False) self.assertEqual( me.puburi, '{0}'.format( - os.path.join(SOCK_DIR, 'master_event_pub.ipc') + os.path.join(self.sock_dir, 'master_event_pub.ipc') ) ) self.assertEqual( me.pulluri, '{0}'.format( - os.path.join(SOCK_DIR, 'master_event_pull.ipc') + os.path.join(self.sock_dir, 'master_event_pull.ipc') ) ) def test_minion_event(self): - opts = dict(id='foo', sock_dir=SOCK_DIR) + opts = dict(id='foo', sock_dir=self.sock_dir) id_hash = hashlib.sha256(salt.utils.stringutils.to_bytes(opts['id'])).hexdigest()[:10] me = salt.utils.event.MinionEvent(opts, listen=False) self.assertEqual( me.puburi, '{0}'.format( os.path.join( - SOCK_DIR, 'minion_event_{0}_pub.ipc'.format(id_hash) + self.sock_dir, 'minion_event_{0}_pub.ipc'.format(id_hash) ) ) ) @@ -131,7 +86,7 @@ def test_minion_event(self): me.pulluri, '{0}'.format( os.path.join( - SOCK_DIR, 'minion_event_{0}_pull.ipc'.format(id_hash) + self.sock_dir, 'minion_event_{0}_pull.ipc'.format(id_hash) ) ) ) @@ -143,13 +98,13 @@ def test_minion_event_tcp_ipc_mode(self): self.assertEqual(me.pulluri, 4511) def test_minion_event_no_id(self): - me = salt.utils.event.MinionEvent(dict(sock_dir=SOCK_DIR), listen=False) + me = salt.utils.event.MinionEvent(dict(sock_dir=self.sock_dir), listen=False) id_hash = hashlib.sha256(salt.utils.stringutils.to_bytes('')).hexdigest()[:10] self.assertEqual( me.puburi, '{0}'.format( os.path.join( - SOCK_DIR, 'minion_event_{0}_pub.ipc'.format(id_hash) + self.sock_dir, 'minion_event_{0}_pub.ipc'.format(id_hash) ) ) ) @@ -157,23 +112,23 @@ def test_minion_event_no_id(self): me.pulluri, '{0}'.format( os.path.join( - SOCK_DIR, 'minion_event_{0}_pull.ipc'.format(id_hash) + self.sock_dir, 'minion_event_{0}_pull.ipc'.format(id_hash) ) ) ) def test_event_single(self): '''Test a single event is received''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me.fire_event({'data': 'foo1'}, 'evt1') evt1 = me.get_event(tag='evt1') self.assertGotEvent(evt1, {'data': 'foo1'}) def test_event_single_no_block(self): '''Test a single event is received, no block''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) start = time.time() finish = start + 5 evt1 = me.get_event(wait=0, tag='evt1', no_block=True) @@ -188,8 +143,8 @@ def test_event_single_no_block(self): def test_event_single_wait_0_no_block_False(self): '''Test a single event is received with wait=0 and no_block=False and doesn't spin the while loop''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me.fire_event({'data': 'foo1'}, 'evt1') # This is too fast and will be None but assures we're not blocking evt1 = me.get_event(wait=0, tag='evt1', no_block=False) @@ -197,8 +152,8 @@ def test_event_single_wait_0_no_block_False(self): def test_event_timeout(self): '''Test no event is received if the timeout is reached''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me.fire_event({'data': 'foo1'}, 'evt1') evt1 = me.get_event(tag='evt1') self.assertGotEvent(evt1, {'data': 'foo1'}) @@ -207,48 +162,48 @@ def test_event_timeout(self): def test_event_no_timeout(self): '''Test no wait timeout, we should block forever, until we get one ''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) - with eventsender_process({'data': 'foo2'}, 'evt2', 5): + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) + with eventsender_process({'data': 'foo2'}, 'evt2', self.sock_dir, 5): evt = me.get_event(tag='evt2', wait=0, no_block=False) self.assertGotEvent(evt, {'data': 'foo2'}) def test_event_matching(self): '''Test a startswith match''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me.fire_event({'data': 'foo1'}, 'evt1') evt1 = me.get_event(tag='ev') self.assertGotEvent(evt1, {'data': 'foo1'}) def test_event_matching_regex(self): '''Test a regex match''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me.fire_event({'data': 'foo1'}, 'evt1') evt1 = me.get_event(tag='^ev', match_type='regex') self.assertGotEvent(evt1, {'data': 'foo1'}) def test_event_matching_all(self): '''Test an all match''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me.fire_event({'data': 'foo1'}, 'evt1') evt1 = me.get_event(tag='') self.assertGotEvent(evt1, {'data': 'foo1'}) def test_event_matching_all_when_tag_is_None(self): '''Test event matching all when not passing a tag''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me.fire_event({'data': 'foo1'}, 'evt1') evt1 = me.get_event() self.assertGotEvent(evt1, {'data': 'foo1'}) def test_event_not_subscribed(self): '''Test get_event drops non-subscribed events''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me.fire_event({'data': 'foo1'}, 'evt1') me.fire_event({'data': 'foo2'}, 'evt2') evt2 = me.get_event(tag='evt2') @@ -258,8 +213,8 @@ def test_event_not_subscribed(self): def test_event_subscription_cache(self): '''Test subscriptions cache a message until requested''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me.subscribe('evt1') me.fire_event({'data': 'foo1'}, 'evt1') me.fire_event({'data': 'foo2'}, 'evt2') @@ -270,8 +225,8 @@ def test_event_subscription_cache(self): def test_event_subscriptions_cache_regex(self): '''Test regex subscriptions cache a message until requested''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me.subscribe('e..1$', 'regex') me.fire_event({'data': 'foo1'}, 'evt1') me.fire_event({'data': 'foo2'}, 'evt2') @@ -282,9 +237,9 @@ def test_event_subscriptions_cache_regex(self): def test_event_multiple_clients(self): '''Test event is received by multiple clients''' - with eventpublisher_process(): - me1 = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) - me2 = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me1 = salt.utils.event.MasterEvent(self.sock_dir, listen=True) + me2 = salt.utils.event.MasterEvent(self.sock_dir, listen=True) # We need to sleep here to avoid a race condition wherein # the second socket may not be connected by the time the first socket # sends the event. @@ -299,8 +254,8 @@ def test_event_multiple_clients(self): def test_event_nested_sub_all(self): '''Test nested event subscriptions do not drop events, get event for all tags''' # Show why not to call get_event(tag='') - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me.fire_event({'data': 'foo1'}, 'evt1') me.fire_event({'data': 'foo2'}, 'evt2') evt2 = me.get_event(tag='') @@ -310,8 +265,8 @@ def test_event_nested_sub_all(self): def test_event_many(self): '''Test a large number of events, one at a time''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) for i in range(500): me.fire_event({'data': '{0}'.format(i)}, 'testevents') evt = me.get_event(tag='testevents') @@ -319,8 +274,8 @@ def test_event_many(self): def test_event_many_backlog(self): '''Test a large number of events, send all then recv all''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) # Must not exceed zmq HWM for i in range(500): me.fire_event({'data': '{0}'.format(i)}, 'testevents') @@ -332,8 +287,8 @@ def test_event_many_backlog(self): # we don't need to perform extensive testing. def test_send_master_event(self): '''Tests that sending an event through fire_master generates expected event''' - with eventpublisher_process(): - me = salt.utils.event.MasterEvent(SOCK_DIR, listen=True) + with eventpublisher_process(self.sock_dir): + me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) data = {'data': 'foo1'} me.fire_master(data, 'test_master') @@ -347,7 +302,11 @@ def get_new_ioloop(self): def setUp(self): super(TestAsyncEventPublisher, self).setUp() - self.opts = {'sock_dir': SOCK_DIR} + self.sock_dir = os.path.join(RUNTIME_VARS.TMP, 'test-socks') + if not os.path.exists(self.sock_dir): + os.makedirs(self.sock_dir) + self.addCleanup(shutil.rmtree, self.sock_dir, ignore_errors=True) + self.opts = {'sock_dir': self.sock_dir} self.publisher = salt.utils.event.AsyncEventPublisher( self.opts, self.io_loop, diff --git a/tests/unit/utils/test_extend.py b/tests/unit/utils/test_extend.py index 9cbb767ca503..27dbbf387fff 100644 --- a/tests/unit/utils/test_extend.py +++ b/tests/unit/utils/test_extend.py @@ -16,9 +16,9 @@ # Import Salt Testing libs from tests.support.unit import TestCase, skipIf from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS # Import salt libs -import tests.integration as integration import salt.utils.extend import salt.utils.files @@ -26,7 +26,7 @@ class ExtendTestCase(TestCase): def setUp(self): self.starting_dir = os.getcwd() - os.chdir(integration.CODE_DIR) + os.chdir(RUNTIME_VARS.CODE_DIR) self.out = None def tearDown(self): @@ -35,11 +35,11 @@ def tearDown(self): shutil.rmtree(self.out, True) os.chdir(self.starting_dir) - @skipIf(not os.path.exists(os.path.join(integration.CODE_DIR, 'templates')), + @skipIf(not os.path.exists(os.path.join(RUNTIME_VARS.CODE_DIR, 'templates')), "Test template directory 'templates/' missing.") def test_run(self): with patch('sys.exit', MagicMock): - out = salt.utils.extend.run('test', 'test', 'this description', integration.CODE_DIR, False) + out = salt.utils.extend.run('test', 'test', 'this description', RUNTIME_VARS.CODE_DIR, False) self.out = out year = date.today().strftime('%Y') self.assertTrue(os.path.exists(out)) diff --git a/tests/unit/utils/test_files.py b/tests/unit/utils/test_files.py index 5e8cc7625f61..095a21e009dc 100644 --- a/tests/unit/utils/test_files.py +++ b/tests/unit/utils/test_files.py @@ -17,8 +17,6 @@ from tests.support.unit import TestCase, skipIf from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) @@ -27,7 +25,6 @@ class FilesTestCase(TestCase): Test case for files util. ''' - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_safe_rm(self): with patch('os.remove') as os_remove_mock: salt.utils.files.safe_rm('dummy_tgt') diff --git a/tests/unit/utils/test_find.py b/tests/unit/utils/test_find.py index 2b558a66e3ca..ac78fe596b6f 100644 --- a/tests/unit/utils/test_find.py +++ b/tests/unit/utils/test_find.py @@ -9,8 +9,8 @@ import stat # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import skipIf, TestCase -from tests.support.paths import TMP # Import salt libs import salt.utils.files @@ -286,7 +286,7 @@ class TestGrepOption(TestCase): def setUp(self): super(TestGrepOption, self).setUp() - self.tmpdir = tempfile.mkdtemp(dir=TMP) + self.tmpdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) def tearDown(self): shutil.rmtree(self.tmpdir) @@ -331,7 +331,7 @@ class TestPrintOption(TestCase): def setUp(self): super(TestPrintOption, self).setUp() - self.tmpdir = tempfile.mkdtemp(dir=TMP) + self.tmpdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) def tearDown(self): shutil.rmtree(self.tmpdir) @@ -435,7 +435,7 @@ class TestFinder(TestCase): def setUp(self): super(TestFinder, self).setUp() - self.tmpdir = tempfile.mkdtemp(dir=TMP) + self.tmpdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) def tearDown(self): shutil.rmtree(self.tmpdir) diff --git a/tests/unit/utils/test_gitfs.py b/tests/unit/utils/test_gitfs.py index a9ed6f8f1475..7c1686f4f45e 100644 --- a/tests/unit/utils/test_gitfs.py +++ b/tests/unit/utils/test_gitfs.py @@ -8,8 +8,8 @@ from __future__ import absolute_import, unicode_literals, print_function # Import Salt Testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch # Import salt libs import salt.utils.gitfs @@ -19,7 +19,6 @@ OPTS = {'cachedir': '/tmp/gitfs-test-cache'} -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestGitFSProvider(TestCase): def test_provider_case_insensitive(self): diff --git a/tests/unit/utils/test_hashutils.py b/tests/unit/utils/test_hashutils.py index b37cd6ae2733..8cfb58009a87 100644 --- a/tests/unit/utils/test_hashutils.py +++ b/tests/unit/utils/test_hashutils.py @@ -23,6 +23,7 @@ class HashutilsTestCase(TestCase): str_sha256 = '095291ffa3d361436d4617879e22c1da06c6ab61a3fb081321ec854a27a091ac' str_sha512 = '12efd90e507289f1f21e5dcfe2e92cf0bb4904abccb55c3ce9177670c711981501054b32b807c37058675590d1c484bd2b72a4215a2fa397aa4f2b12f298b1f0' str_hmac_challenge = b'qz2k0t1aevKEme3JGsNQJX/xpmf+/w3q6qmWDk1ZqbY=' + str_hmac_compute = 'ab3da4d2dd5a7af28499edc91ac350257ff1a667feff0deaeaa9960e4d59a9b6' # 16 bytes of random data bytes = b'b\x19\xf6\x86\x0e\x1a\x1cs\x0c\xda&zv\xfc\xa2\xdd' @@ -32,6 +33,7 @@ class HashutilsTestCase(TestCase): bytes_sha256 = '25711a31c2673a48f3d1f29b25add574697872968e546d266f441de63b17954a' bytes_sha512 = '69f1524e602c1599fc374e1e3e2941e6f6949f4f7fe7321304e4e67bb850f3204dd5cbf9c13e231814540c2f5cd370c24ea257771d9fbf311d8f6085bad12b24' bytes_hmac_challenge = b'lQibiD9r1Hpo+5JYknaudIKfTx1L5J3U58M9yQOd04c=' + bytes_hmac_compute = '95089b883f6bd47a68fb92589276ae74829f4f1d4be49dd4e7c33dc9039dd387' def test_base64_b64encode(self): ''' @@ -157,6 +159,21 @@ def test_hmac_signature(self): ) ) + def test_hmac_compute(self): + ''' + Ensure that this function converts the value passed to bytes before + attempting to encode, avoiding a UnicodeEncodeError on Python 2 and a + TypeError on Python 3. + ''' + self.assertEqual( + salt.utils.hashutils.hmac_compute(self.str, self.hmac_secret), + self.str_hmac_compute + ) + self.assertEqual( + salt.utils.hashutils.hmac_compute(self.bytes, self.hmac_secret), + self.bytes_hmac_compute + ) + def test_get_hash_exception(self): self.assertRaises( ValueError, diff --git a/tests/unit/utils/test_http.py b/tests/unit/utils/test_http.py index ab40c2a14413..861457f2e93e 100644 --- a/tests/unit/utils/test_http.py +++ b/tests/unit/utils/test_http.py @@ -3,22 +3,33 @@ :codeauthor: Nicole Thomas ''' -# Import Salt Libs +# Import Python Libs from __future__ import absolute_import, unicode_literals, print_function +import socket +from contextlib import closing # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.helpers import MirrorPostHandler, Webserver # Import Salt Libs import salt.utils.http as http -@skipIf(NO_MOCK, NO_MOCK_REASON) class HTTPTestCase(TestCase): ''' Unit TestCase for the salt.utils.http module. ''' + @classmethod + def setUpClass(cls): + cls.post_webserver = Webserver(handler=MirrorPostHandler) + cls.post_webserver.start() + cls.post_web_root = cls.post_webserver.web_root + + @classmethod + def tearDownClass(cls): + cls.post_webserver.stop() + del cls.post_webserver # sanitize_url tests @@ -90,3 +101,36 @@ def test_sanitize_components_multiple_elements(self): mock_ret = 'foo=XXXXXXXXXX&foo=XXXXXXXXXX&api_key=testing&' ret = http._sanitize_url_components(mock_component_list, 'foo') self.assertEqual(ret, mock_ret) + + def test_query_null_response(self): + ''' + This tests that we get a null response when raise_error=False and the + host/port cannot be reached. + ''' + host = '127.0.0.1' + + # Find unused port + with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock: + sock.bind((host, 0)) + port = sock.getsockname()[1] + + url = 'http://{host}:{port}/'.format(host=host, port=port) + result = http.query(url, raise_error=False) + assert result == {'body': None}, result + + def test_requests_multipart_formdata_post(self): + ''' + Test handling of a multipart/form-data POST using the requests backend + ''' + match_this = '{0}\r\nContent-Disposition: form-data; name="fieldname_here"\r\n\r\nmydatahere\r\n{0}--\r\n' + ret = http.query( + self.post_web_root, + method='POST', + data='mydatahere', + formdata=True, + formdata_fieldname='fieldname_here', + backend='requests' + ) + body = ret.get('body', '') + boundary = body[:body.find('\r')] + self.assertEqual(body, match_this.format(boundary)) diff --git a/tests/unit/utils/test_jid.py b/tests/unit/utils/test_jid.py index a988c3bcfe83..3bb5d2baa6d3 100644 --- a/tests/unit/utils/test_jid.py +++ b/tests/unit/utils/test_jid.py @@ -10,12 +10,8 @@ # Import Salt libs import salt.utils.jid -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - NO_MOCK, - NO_MOCK_REASON -) +from tests.support.unit import TestCase +from tests.support.mock import patch class JidTestCase(TestCase): @@ -33,11 +29,9 @@ def test_is_jid(self): self.assertFalse(salt.utils.jid.is_jid(20131219110700123489)) # int self.assertFalse(salt.utils.jid.is_jid('2013121911070012348911111')) # Wrong length - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_gen_jid(self): - now = datetime.datetime(2002, 12, 25, 12, 00, 00, 00) - with patch('datetime.datetime'): - datetime.datetime.now.return_value = now + now = datetime.datetime(2002, 12, 25, 12, 0, 0, 0) + with patch('salt.utils.jid._utc_now', return_value=now): ret = salt.utils.jid.gen_jid({}) self.assertEqual(ret, '20021225120000000000') salt.utils.jid.LAST_JID_DATETIME = None @@ -45,3 +39,18 @@ def test_gen_jid(self): self.assertEqual(ret, '20021225120000000000_{0}'.format(os.getpid())) ret = salt.utils.jid.gen_jid({'unique_jid': True}) self.assertEqual(ret, '20021225120000000001_{0}'.format(os.getpid())) + + def test_gen_jid_utc(self): + utcnow = datetime.datetime(2002, 12, 25, 12, 7, 0, 0) + with patch('salt.utils.jid._utc_now', return_value=utcnow): + ret = salt.utils.jid.gen_jid({'utc_jid': True}) + self.assertEqual(ret, '20021225120700000000') + + def test_gen_jid_utc_unique(self): + utcnow = datetime.datetime(2002, 12, 25, 12, 7, 0, 0) + with patch('salt.utils.jid._utc_now', return_value=utcnow): + salt.utils.jid.LAST_JID_DATETIME = None + ret = salt.utils.jid.gen_jid({'utc_jid': True, 'unique_jid': True}) + self.assertEqual(ret, '20021225120700000000_{0}'.format(os.getpid())) + ret = salt.utils.jid.gen_jid({'utc_jid': True, 'unique_jid': True}) + self.assertEqual(ret, '20021225120700000001_{0}'.format(os.getpid())) diff --git a/tests/unit/utils/test_jinja.py b/tests/unit/utils/test_jinja.py index 48d824e1cc4d..01e87bbc805f 100644 --- a/tests/unit/utils/test_jinja.py +++ b/tests/unit/utils/test_jinja.py @@ -14,11 +14,11 @@ import tempfile # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import skipIf, TestCase from tests.support.case import ModuleCase from tests.support.helpers import flaky -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, Mock -from tests.support.paths import BASE_FILES, TMP, TMP_CONF_DIR +from tests.support.mock import patch, MagicMock, Mock # Import Salt libs import salt.config @@ -52,7 +52,6 @@ except ImportError: HAS_TIMELIB = False -CACHEDIR = os.path.join(TMP, 'jinja-template-cache') BLINESEP = salt.utils.stringutils.to_bytes(os.linesep) @@ -64,7 +63,9 @@ def test_tojson(self): ''' data = {'Non-ascii words': ['süß', 'спам', 'яйца']} result = tojson(data) - expected = '{"Non-ascii words": ["s\\u00fc\\u00df", "\\u0441\\u043f\\u0430\\u043c", "\\u044f\\u0439\\u0446\\u0430"]}' + expected = ('{"Non-ascii words": ["s\\u00fc\\u00df", ' + '"\\u0441\\u043f\\u0430\\u043c", ' + '"\\u044f\\u0439\\u0446\\u0430"]}') assert result == expected, result @@ -112,7 +113,7 @@ def setUp(self): self.tempdir = tempfile.mkdtemp() self.template_dir = os.path.join(self.tempdir, 'files', 'test') _setup_test_dir( - os.path.join(BASE_FILES, 'templates'), + os.path.join(RUNTIME_VARS.BASE_FILES, 'templates'), self.template_dir ) self.opts = { @@ -167,6 +168,7 @@ def get_loader(self, opts=None, saltenv='base'): opts = self.opts with patch.object(SaltCacheLoader, 'file_client', Mock()): loader = SaltCacheLoader(opts, saltenv) + self.addCleanup(setattr, SaltCacheLoader, '_cached_client', None) # Create a mock file client and attach it to the loader MockFileClient(loader) return loader @@ -264,7 +266,7 @@ def setUp(self): self.tempdir = tempfile.mkdtemp() self.template_dir = os.path.join(self.tempdir, 'files', 'test') _setup_test_dir( - os.path.join(BASE_FILES, 'templates'), + os.path.join(RUNTIME_VARS.BASE_FILES, 'templates'), self.template_dir ) self.local_opts = { @@ -534,7 +536,6 @@ def test_render_with_syntax_error(self): ) @skipIf(six.PY3, 'Not applicable to Python 3') - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_render_with_unicode_syntax_error(self): with patch.object(builtins, '__salt_system_encoding__', 'utf-8'): template = 'hello\n\n{{ bad\n\nfoo한' @@ -547,7 +548,6 @@ def test_render_with_unicode_syntax_error(self): dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) ) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_render_with_utf8_syntax_error(self): with patch.object(builtins, '__salt_system_encoding__', 'utf-8'): template = 'hello\n\n{{ bad\n\nfoo한' @@ -601,16 +601,16 @@ class TestJinjaDefaultOptions(TestCase): def __init__(self, *args, **kws): TestCase.__init__(self, *args, **kws) self.local_opts = { - 'cachedir': CACHEDIR, + 'cachedir': os.path.join(RUNTIME_VARS.TMP, 'jinja-template-cache'), 'file_buffer_size': 1048576, 'file_client': 'local', 'file_ignore_regex': None, 'file_ignore_glob': None, 'file_roots': { - 'test': [os.path.join(BASE_FILES, 'templates')] + 'test': [os.path.join(RUNTIME_VARS.BASE_FILES, 'templates')] }, 'pillar_roots': { - 'test': [os.path.join(BASE_FILES, 'templates')] + 'test': [os.path.join(RUNTIME_VARS.BASE_FILES, 'templates')] }, 'fileserver_backend': ['roots'], 'hash_type': 'md5', @@ -663,16 +663,16 @@ class TestCustomExtensions(TestCase): def __init__(self, *args, **kws): super(TestCustomExtensions, self).__init__(*args, **kws) self.local_opts = { - 'cachedir': CACHEDIR, + 'cachedir': os.path.join(RUNTIME_VARS.TMP, 'jinja-template-cache'), 'file_buffer_size': 1048576, 'file_client': 'local', 'file_ignore_regex': None, 'file_ignore_glob': None, 'file_roots': { - 'test': [os.path.join(BASE_FILES, 'templates')] + 'test': [os.path.join(RUNTIME_VARS.BASE_FILES, 'templates')] }, 'pillar_roots': { - 'test': [os.path.join(BASE_FILES, 'templates')] + 'test': [os.path.join(RUNTIME_VARS.BASE_FILES, 'templates')] }, 'fileserver_backend': ['roots'], 'hash_type': 'md5', @@ -804,20 +804,20 @@ def test_load_yaml(self): with self.assertRaises((TypeError, exceptions.TemplateRuntimeError)): env.from_string('{% set document = document|load_yaml %}' - '{{ document.foo }}').render(document={"foo": "it works"}) + '{{ document.foo }}').render(document={"foo": "it works"}) def test_load_tag(self): env = Environment(extensions=[SerializerExtension]) source = '{{ bar }}, ' + \ '{% load_yaml as docu %}{foo: it works, {{ bar }}: baz}{% endload %}' + \ - '{{ docu.foo }}' + '{{ docu.foo }}' rendered = env.from_string(source).render(bar="barred") self.assertEqual(rendered, "barred, it works") source = '{{ bar }}, {% load_json as docu %}{"foo": "it works", "{{ bar }}": "baz"}{% endload %}' + \ - '{{ docu.foo }}' + '{{ docu.foo }}' rendered = env.from_string(source).render(bar="barred") self.assertEqual(rendered, "barred, it works") @@ -932,15 +932,14 @@ def test_nested_structures(self): ('foo', OrderedDict([ ('bar', 'baz'), ('qux', 42) - ]) - ) + ])) ]) rendered = env.from_string('{{ data }}').render(data=data) self.assertEqual( rendered, "{u'foo': {u'bar': u'baz', u'qux': 42}}" if six.PY2 - else "{'foo': {'bar': 'baz', 'qux': 42}}" + else "{'foo': {'bar': 'baz', 'qux': 42}}" ) rendered = env.from_string('{{ data }}').render(data=[ @@ -954,7 +953,117 @@ def test_nested_structures(self): self.assertEqual( rendered, "[{'foo': u'bar'}, {'baz': 42}]" if six.PY2 - else "[{'foo': 'bar'}, {'baz': 42}]" + else "[{'foo': 'bar'}, {'baz': 42}]" + ) + + def test_set_dict_key_value(self): + ''' + Test the `set_dict_key_value` Jinja filter. + ''' + rendered = render_jinja_tmpl("{{ {} | set_dict_key_value('foo:bar:baz', 42) }}", + dict(opts=self.local_opts, + saltenv='test', + salt=self.local_salt)) + self.assertEqual(rendered, "{'foo': {'bar': {'baz': 42}}}") + + rendered = render_jinja_tmpl("{{ {} | set_dict_key_value('foo.bar.baz', 42, delimiter='.') }}", + dict(opts=self.local_opts, + saltenv='test', + salt=self.local_salt)) + self.assertEqual(rendered, "{'foo': {'bar': {'baz': 42}}}") + + def test_update_dict_key_value(self): + ''' + Test the `update_dict_key_value` Jinja filter. + ''' + # Use OrderedDicts to avoid random key-order-switches in the rendered string. + expected = OrderedDict([('bar', OrderedDict([('baz', OrderedDict([('qux', 1), ('quux', 3)]))]))]) + dataset = OrderedDict([('bar', OrderedDict([('baz', OrderedDict([('qux', 1)]))]))]) + dataset_exp = OrderedDict([('quux', 3)]) + rendered = render_jinja_tmpl("{{ foo | update_dict_key_value('bar:baz', exp) }}", + dict(foo=dataset, + exp=dataset_exp, + opts=self.local_opts, + saltenv='test', + salt=self.local_salt)) + self.assertEqual( + rendered, + "{u'bar': {u'baz': {u'qux': 1, u'quux': 3}}}" if six.PY2 + else "{'bar': {'baz': {'qux': 1, 'quux': 3}}}") + + # Test incorrect usage + for update_with in [42, 'foo', [42]]: + template = "{{ {} | update_dict_key_value('bar:baz', update_with) }}" + expected = r"Cannot update {} with a {}.".format(type({}), type(update_with)) + self.assertRaisesRegex( + SaltRenderError, + expected, + render_jinja_tmpl, + template, + dict(update_with=update_with, + opts=self.local_opts, + saltenv='test', + salt=self.local_salt) + ) + + def test_append_dict_key_value(self): + ''' + Test the `append_dict_key_value` Jinja filter. + ''' + rendered = render_jinja_tmpl("{{ {} | append_dict_key_value('foo:bar:baz', 42) }}", + dict(opts=self.local_opts, + saltenv='test', + salt=self.local_salt)) + self.assertEqual(rendered, "{'foo': {'bar': {'baz': [42]}}}") + + rendered = render_jinja_tmpl("{{ foo | append_dict_key_value('bar:baz', 42) }}", + dict(foo={'bar': {'baz': [1, 2]}}, + opts=self.local_opts, + saltenv='test', + salt=self.local_salt)) + self.assertEqual( + rendered, + "{u'bar': {u'baz': [1, 2, 42]}}" if six.PY2 + else "{'bar': {'baz': [1, 2, 42]}}" + ) + + def test_extend_dict_key_value(self): + ''' + Test the `extend_dict_key_value` Jinja filter. + ''' + rendered = render_jinja_tmpl("{{ {} | extend_dict_key_value('foo:bar:baz', [42]) }}", + dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) + self.assertEqual(rendered, "{'foo': {'bar': {'baz': [42]}}}") + + rendered = render_jinja_tmpl("{{ foo | extend_dict_key_value('bar:baz', [42, 43]) }}", + dict(foo={'bar': {'baz': [1, 2]}}, + opts=self.local_opts, + saltenv='test', + salt=self.local_salt)) + self.assertEqual( + rendered, + "{u'bar': {u'baz': [1, 2, 42, 43]}}" if six.PY2 + else "{'bar': {'baz': [1, 2, 42, 43]}}" + ) + # Edge cases + rendered = render_jinja_tmpl("{{ {} | extend_dict_key_value('foo:bar:baz', 'quux') }}", + dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) + self.assertEqual(rendered, "{'foo': {'bar': {'baz': ['q', 'u', 'u', 'x']}}}") + # Beware! When supplying a dict, the list gets extended with the dict coerced to a list, + # which will only contain the keys of the dict. + rendered = render_jinja_tmpl("{{ {} | extend_dict_key_value('foo:bar:baz', {'foo': 'bar'}) }}", + dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) + self.assertEqual(rendered, "{'foo': {'bar': {'baz': ['foo']}}}") + + # Test incorrect usage + template = "{{ {} | extend_dict_key_value('bar:baz', 42) }}" + expected = r"Cannot extend {} with a {}.".format(type([]), type(42)) + self.assertRaisesRegex( + SaltRenderError, + expected, + render_jinja_tmpl, + template, + dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) ) def test_sequence(self): @@ -1343,7 +1452,7 @@ def setUp(self, *args, **kwargs): 'mocktest.ping': lambda: True, 'mockgrains.get': lambda x: 'jerry', } - minion_opts = salt.config.minion_config(os.path.join(TMP_CONF_DIR, 'minion')) + minion_opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion')) render = salt.loader.render(minion_opts, functions) self.jinja = render.get('jinja') diff --git a/tests/unit/utils/test_json.py b/tests/unit/utils/test_json.py index 4c3e49a0d149..7bb428a110ab 100644 --- a/tests/unit/utils/test_json.py +++ b/tests/unit/utils/test_json.py @@ -8,7 +8,7 @@ # Import Salt Testing libs from tests.support.helpers import with_tempfile -from tests.support.mock import patch, MagicMock, NO_MOCK, NO_MOCK_REASON +from tests.support.mock import patch, MagicMock from tests.support.unit import TestCase, LOREM_IPSUM, skipIf # Import Salt libs @@ -97,7 +97,6 @@ def test_find_json(self): @skipIf(salt.utils.platform.is_windows(), 'skip until we figure out what to do about decoding unicode on windows') @skipIf(not six.PY2, 'Test only needed on Python 2') - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_find_json_unicode_splitlines(self): ''' Tests a case in salt-ssh where a unicode string is split into a list of diff --git a/tests/unit/utils/test_locales.py b/tests/unit/utils/test_locales.py index 7045b8b2bfda..627e3162b011 100644 --- a/tests/unit/utils/test_locales.py +++ b/tests/unit/utils/test_locales.py @@ -6,14 +6,13 @@ # Import Salt libs import salt.utils.locales as locales -from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.mock import patch # Import 3rd-part libs from salt.ext.six.moves import reload_module -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestLocales(TestCase): def test_get_encodings(self): # reload locales modules before and after to defeat memoization of diff --git a/tests/unit/utils/test_mac_utils.py b/tests/unit/utils/test_mac_utils.py index a6fd850bde3f..955999512d92 100644 --- a/tests/unit/utils/test_mac_utils.py +++ b/tests/unit/utils/test_mac_utils.py @@ -15,8 +15,6 @@ call, MagicMock, mock_open, - NO_MOCK, - NO_MOCK_REASON, patch ) from tests.support.mixins import LoaderModuleMockMixin @@ -31,7 +29,7 @@ from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(not salt.utils.platform.is_darwin(), 'These tests run only on mac') class MacUtilsTestCase(TestCase, LoaderModuleMockMixin): ''' test mac_utils salt utility diff --git a/tests/unit/utils/test_msgpack.py b/tests/unit/utils/test_msgpack.py new file mode 100644 index 000000000000..4f1fc66313b9 --- /dev/null +++ b/tests/unit/utils/test_msgpack.py @@ -0,0 +1,440 @@ +# -*- coding: utf-8 -*- +''' +Test the MessagePack utility +''' + +# Import Python Libs +from __future__ import absolute_import +from io import BytesIO +import inspect +import os +import pprint +import sys +import struct + +try: + import msgpack +except ImportError: + import msgpack_pure as msgpack # pylint: disable=import-error + +# Import Salt Testing Libs +from tests.support.unit import skipIf +from tests.support.unit import TestCase + +# Import Salt Libs +from salt.utils.odict import OrderedDict +from salt.ext.six.moves import range +import salt.utils.msgpack + +# A keyword to pass to tests that use `raw`, which was added in msgpack 0.5.2 +raw = {'raw': False} if msgpack.version > (0, 5, 2) else {} + + +@skipIf(not salt.utils.msgpack.HAS_MSGPACK, 'msgpack module required for these tests') +class TestMsgpack(TestCase): + ''' + In msgpack, the following aliases exist: + load = unpack + loads = unpackb + dump = pack + dumps = packb + The salt.utils.msgpack versions of these functions are not aliases, + verify that they pass the same relevant tests from: + https://github.com/msgpack/msgpack-python/blob/master/test/ + ''' + test_data = [ + 0, + 1, + 127, + 128, + 255, + 256, + 65535, + 65536, + 4294967295, + 4294967296, + -1, + -32, + -33, + -128, + -129, + -32768, + -32769, + -4294967296, + -4294967297, + 1.0, + b"", + b"a", + b"a" * 31, + b"a" * 32, + None, + True, + False, + (), + ((),), + ((), None,), + {None: 0}, + (1 << 23), + ] + + def test_version(self): + ''' + Verify that the version exists and returns a value in the expected format + ''' + version = salt.utils.msgpack.version + self.assertTrue(isinstance(version, tuple)) + self.assertGreater(version, (0, 0, 0)) + + def test_Packer(self): + data = os.urandom(1024) + packer = salt.utils.msgpack.Packer() + unpacker = msgpack.Unpacker(None) + + packed = packer.pack(data) + # Sanity Check + self.assertTrue(packed) + self.assertNotEqual(data, packed) + + # Reverse the packing and the result should be equivalent to the original data + unpacker.feed(packed) + unpacked = msgpack.unpackb(packed) + self.assertEqual(data, unpacked) + + def test_Unpacker(self): + data = os.urandom(1024) + packer = msgpack.Packer() + unpacker = salt.utils.msgpack.Unpacker(None) + + packed = packer.pack(data) + # Sanity Check + self.assertTrue(packed) + self.assertNotEqual(data, packed) + + # Reverse the packing and the result should be equivalent to the original data + unpacker.feed(packed) + unpacked = msgpack.unpackb(packed) + self.assertEqual(data, unpacked) + + def test_array_size(self): + sizes = [0, 5, 50, 1000] + bio = BytesIO() + packer = salt.utils.msgpack.Packer() + for size in sizes: + bio.write(packer.pack_array_header(size)) + for i in range(size): + bio.write(packer.pack(i)) + + bio.seek(0) + unpacker = salt.utils.msgpack.Unpacker(bio, use_list=True) + for size in sizes: + self.assertEqual(unpacker.unpack(), list(range(size))) + + def test_manual_reset(self): + sizes = [0, 5, 50, 1000] + packer = salt.utils.msgpack.Packer(autoreset=False) + for size in sizes: + packer.pack_array_header(size) + for i in range(size): + packer.pack(i) + + bio = BytesIO(packer.bytes()) + unpacker = salt.utils.msgpack.Unpacker(bio, use_list=True) + for size in sizes: + self.assertEqual(unpacker.unpack(), list(range(size))) + + packer.reset() + self.assertEqual(packer.bytes(), b'') + + def test_map_size(self): + sizes = [0, 5, 50, 1000] + bio = BytesIO() + packer = salt.utils.msgpack.Packer() + for size in sizes: + bio.write(packer.pack_map_header(size)) + for i in range(size): + bio.write(packer.pack(i)) # key + bio.write(packer.pack(i * 2)) # value + + bio.seek(0) + if salt.utils.msgpack.version > (0, 6, 0): + unpacker = salt.utils.msgpack.Unpacker(bio, strict_map_key=False) + else: + unpacker = salt.utils.msgpack.Unpacker(bio) + for size in sizes: + self.assertEqual(unpacker.unpack(), dict((i, i * 2) for i in range(size))) + + def test_exceptions(self): + # Verify that this exception exists + self.assertTrue(salt.utils.msgpack.exceptions.PackValueError) + self.assertTrue(salt.utils.msgpack.exceptions.UnpackValueError) + self.assertTrue(salt.utils.msgpack.exceptions.PackValueError) + self.assertTrue(salt.utils.msgpack.exceptions.UnpackValueError) + + def test_function_aliases(self): + ''' + Fail if core functionality from msgpack is missing in the utility + ''' + + def sanitized(item): + if inspect.isfunction(getattr(msgpack, item)): + # Only check objects that exist in the same file as msgpack + return inspect.getfile(getattr(msgpack, item)) == inspect.getfile(msgpack) + + msgpack_items = set(x for x in dir(msgpack) if not x.startswith('_') and sanitized(x)) + msgpack_util_items = set(dir(salt.utils.msgpack)) + self.assertFalse(msgpack_items - msgpack_util_items, 'msgpack functions with no alias in `salt.utils.msgpack`') + + def _test_base(self, pack_func, unpack_func): + ''' + In msgpack, 'dumps' is an alias for 'packb' and 'loads' is an alias for 'unpackb'. + Verify that both salt.utils.msgpack function variations pass the exact same test + ''' + data = os.urandom(1024) + + packed = pack_func(data) + # Sanity Check + self.assertTrue(packed) + self.assertIsInstance(packed, bytes) + self.assertNotEqual(data, packed) + + # Reverse the packing and the result should be equivalent to the original data + unpacked = unpack_func(packed) + self.assertEqual(data, unpacked) + + def _test_buffered_base(self, pack_func, unpack_func): + data = os.urandom(1024).decode(errors='ignore') + buffer = BytesIO() + # Sanity check, we are not borking the BytesIO read function + self.assertNotEqual(BytesIO.read, buffer.read) + buffer.read = buffer.getvalue + + pack_func(data, buffer) + # Sanity Check + self.assertTrue(buffer.getvalue()) + self.assertIsInstance(buffer.getvalue(), bytes) + self.assertNotEqual(data, buffer.getvalue()) + + # Reverse the packing and the result should be equivalent to the original data + unpacked = unpack_func(buffer) + self.assertEqual(data, unpacked.decode()) + + def test_buffered_base_pack(self): + self._test_buffered_base(pack_func=salt.utils.msgpack.pack, unpack_func=msgpack.unpack) + + def test_buffered_base_unpack(self): + self._test_buffered_base(pack_func=msgpack.pack, unpack_func=salt.utils.msgpack.unpack) + + def _test_unpack_array_header_from_file(self, pack_func, **kwargs): + f = BytesIO(pack_func([1, 2, 3, 4])) + unpacker = salt.utils.msgpack.Unpacker(f) + self.assertEqual(unpacker.read_array_header(), 4) + self.assertEqual(unpacker.unpack(), 1) + self.assertEqual(unpacker.unpack(), 2) + self.assertEqual(unpacker.unpack(), 3) + self.assertEqual(unpacker.unpack(), 4) + self.assertRaises(salt.utils.msgpack.exceptions.OutOfData, unpacker.unpack) + + @skipIf(not hasattr(sys, 'getrefcount'), 'sys.getrefcount() is needed to pass this test') + def _test_unpacker_hook_refcnt(self, pack_func, **kwargs): + result = [] + + def hook(x): + result.append(x) + return x + + basecnt = sys.getrefcount(hook) + + up = salt.utils.msgpack.Unpacker(object_hook=hook, list_hook=hook) + + self.assertGreaterEqual(sys.getrefcount(hook), basecnt + 2) + + up.feed(pack_func([{}])) + up.feed(pack_func([{}])) + self.assertEqual(up.unpack(), [{}]) + self.assertEqual(up.unpack(), [{}]) + self.assertEqual(result, [{}, [{}], {}, [{}]]) + + del up + + self.assertEqual(sys.getrefcount(hook), basecnt) + + def _test_unpacker_ext_hook(self, pack_func, **kwargs): + class MyUnpacker(salt.utils.msgpack.Unpacker): + def __init__(self): + my_kwargs = {} + super(MyUnpacker, self).__init__(ext_hook=self._hook, **raw) + + def _hook(self, code, data): + if code == 1: + return int(data) + else: + return salt.utils.msgpack.ExtType(code, data) + + unpacker = MyUnpacker() + unpacker.feed(pack_func({"a": 1})) + self.assertEqual(unpacker.unpack(), {'a': 1}) + unpacker.feed(pack_func({'a': salt.utils.msgpack.ExtType(1, b'123')})) + self.assertEqual(unpacker.unpack(), {'a': 123}) + unpacker.feed(pack_func({'a': salt.utils.msgpack.ExtType(2, b'321')})) + self.assertEqual(unpacker.unpack(), {'a': salt.utils.msgpack.ExtType(2, b'321')}) + + def _check(self, data, pack_func, unpack_func, use_list=False, strict_map_key=False): + my_kwargs = {} + if salt.utils.msgpack.version >= (0, 6, 0): + my_kwargs['strict_map_key'] = strict_map_key + ret = unpack_func(pack_func(data), use_list=use_list, **my_kwargs) + self.assertEqual(ret, data) + + def _test_pack_unicode(self, pack_func, unpack_func): + test_data = [u'', u'abcd', [u'defgh'], u'Русский текст'] + for td in test_data: + ret = unpack_func(pack_func(td), use_list=True, **raw) + self.assertEqual(ret, td) + packer = salt.utils.msgpack.Packer() + data = packer.pack(td) + ret = salt.utils.msgpack.Unpacker(BytesIO(data), use_list=True, **raw).unpack() + self.assertEqual(ret, td) + + def _test_pack_bytes(self, pack_func, unpack_func): + test_data = [ + b'', + b'abcd', + (b'defgh',), + ] + for td in test_data: + self._check(td, pack_func, unpack_func) + + def _test_pack_byte_arrays(self, pack_func, unpack_func): + test_data = [ + bytearray(b''), + bytearray(b'abcd'), + (bytearray(b'defgh'),), + ] + for td in test_data: + self._check(td, pack_func, unpack_func) + + @skipIf(sys.version_info < (3, 0), 'Python 2 passes invalid surrogates') + def _test_ignore_unicode_errors(self, pack_func, unpack_func): + ret = unpack_func( + pack_func(b'abc\xeddef', use_bin_type=False), unicode_errors='ignore', **raw + ) + self.assertEqual(u'abcdef', ret) + + def _test_strict_unicode_unpack(self, pack_func, unpack_func): + packed = pack_func(b'abc\xeddef', use_bin_type=False) + self.assertRaises(UnicodeDecodeError, unpack_func, packed, use_list=True, **raw) + + @skipIf(sys.version_info < (3, 0), 'Python 2 passes invalid surrogates') + def _test_ignore_errors_pack(self, pack_func, unpack_func): + ret = unpack_func( + pack_func(u'abc\uDC80\uDCFFdef', use_bin_type=True, unicode_errors='ignore'), use_list=True, **raw + ) + self.assertEqual(u'abcdef', ret) + + def _test_decode_binary(self, pack_func, unpack_func): + ret = unpack_func(pack_func(b'abc'), use_list=True) + self.assertEqual(b'abc', ret) + + @skipIf(salt.utils.msgpack.version < (0, 2, 2), 'use_single_float was added in msgpack==0.2.2') + def _test_pack_float(self, pack_func, **kwargs): + self.assertEqual(b'\xca' + struct.pack(str('>f'), 1.0), pack_func(1.0, use_single_float=True)) + self.assertEqual(b'\xcb' + struct.pack(str('>d'), 1.0), pack_func(1.0, use_single_float=False)) + + def _test_odict(self, pack_func, unpack_func): + seq = [(b'one', 1), (b'two', 2), (b'three', 3), (b'four', 4)] + + od = OrderedDict(seq) + self.assertEqual(dict(seq), unpack_func(pack_func(od), use_list=True)) + + def pair_hook(seq): + return list(seq) + + self.assertEqual(seq, unpack_func(pack_func(od), object_pairs_hook=pair_hook, use_list=True)) + + def _test_pair_list(self, unpack_func, **kwargs): + pairlist = [(b'a', 1), (2, b'b'), (b'foo', b'bar')] + packer = salt.utils.msgpack.Packer() + packed = packer.pack_map_pairs(pairlist) + if salt.utils.msgpack.version > (0, 6, 0): + unpacked = unpack_func(packed, object_pairs_hook=list, strict_map_key=False) + else: + unpacked = unpack_func(packed, object_pairs_hook=list) + self.assertEqual(pairlist, unpacked) + + @skipIf(salt.utils.msgpack.version < (0, 6, 0), 'getbuffer() was added to Packer in msgpack 0.6.0') + def _test_get_buffer(self, pack_func, **kwargs): + packer = msgpack.Packer(autoreset=False, use_bin_type=True) + packer.pack([1, 2]) + strm = BytesIO() + strm.write(packer.getbuffer()) + written = strm.getvalue() + + expected = pack_func([1, 2], use_bin_type=True) + self.assertEqual(expected, written) + + @staticmethod + def no_fail_run(test, *args, **kwargs): + ''' + Run a test without failure and return any exception it raises + ''' + try: + test(*args, **kwargs) + except Exception as e: + return e + + def test_binary_function_compatibility(self): + functions = [ + {'pack_func': salt.utils.msgpack.packb, 'unpack_func': msgpack.unpackb}, + {'pack_func': msgpack.packb, 'unpack_func': salt.utils.msgpack.unpackb}, + ] + # These functions are equivalent but could potentially be overwritten + if salt.utils.msgpack.dumps is not salt.utils.msgpack.packb: + functions.append({'pack_func': salt.utils.msgpack.dumps, 'unpack_func': msgpack.unpackb}) + if salt.utils.msgpack.loads is not salt.utils.msgpack.unpackb: + functions.append({'pack_func': msgpack.packb, 'unpack_func': salt.utils.msgpack.loads}) + + test_funcs = ( + self._test_base, + self._test_unpack_array_header_from_file, + self._test_unpacker_hook_refcnt, + self._test_unpacker_ext_hook, + self._test_pack_unicode, + self._test_pack_bytes, + self._test_pack_byte_arrays, + self._test_ignore_unicode_errors, + self._test_strict_unicode_unpack, + self._test_ignore_errors_pack, + self._test_decode_binary, + self._test_pack_float, + self._test_odict, + self._test_pair_list, + self._test_get_buffer, + ) + errors = {} + for test_func in test_funcs: + # Run the test without the salt.utils.msgpack module for comparison + vanilla_run = self.no_fail_run(test_func, **{'pack_func': msgpack.packb, 'unpack_func': msgpack.unpackb}) + + for func_args in functions: + func_name = func_args['pack_func'] if func_args['pack_func'].__module__.startswith('salt.utils') \ + else func_args['unpack_func'] + if hasattr(TestCase, 'subTest'): + with self.subTest(test=test_func.__name__, func=func_name.__name__): + # Run the test with the salt.utils.msgpack module + run = self.no_fail_run(test_func, **func_args) + # If the vanilla msgpack module errored, then skip if we got the same error + if run: + if str(vanilla_run) == str(run): + self.skipTest('Failed the same way as the vanilla msgpack module:\n{}'.format(run)) + else: + # If subTest isn't available then run the tests collect the errors of all the tests before failing + run = self.no_fail_run(test_func, **func_args) + if run: + # If the vanilla msgpack module errored, then skip if we got the same error + if str(vanilla_run) == str(run): + self.skipTest('Test failed the same way the vanilla msgpack module fails:\n{}'.format(run)) + else: + errors[(test_func.__name__, func_name.__name__)] = run + + if errors: + self.fail(pprint.pformat(errors)) diff --git a/tests/unit/utils/test_network.py b/tests/unit/utils/test_network.py index af5cbbab2b59..67463cdfe820 100644 --- a/tests/unit/utils/test_network.py +++ b/tests/unit/utils/test_network.py @@ -6,15 +6,12 @@ import textwrap # Import Salt Testing libs -from tests.support.unit import skipIf from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, mock_open, create_autospec, patch, - NO_MOCK, - NO_MOCK_REASON, ) # Import salt libs @@ -134,7 +131,6 @@ False: ('::1', '::1/129', 'FOO', 9, 'aj01::feac/64')} -@skipIf(NO_MOCK, NO_MOCK_REASON) class NetworkTestCase(TestCase): def test_sanitize_host(self): @@ -678,5 +674,5 @@ def test_generate_minion_id_with_long_hostname(self): def test_netlink_tool_remote_on(self): with patch('subprocess.check_output', return_value=NETLINK_SS): - remotes = network._netlink_tool_remote_on('4505', 'remote') + remotes = network._netlink_tool_remote_on('4505', 'remote_port') self.assertEqual(remotes, set(['127.0.0.1', '::ffff:1.2.3.4'])) diff --git a/tests/unit/utils/test_parsers.py b/tests/unit/utils/test_parsers.py index 36b14a00720a..a60a75c8e7b7 100644 --- a/tests/unit/utils/test_parsers.py +++ b/tests/unit/utils/test_parsers.py @@ -6,15 +6,14 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals import os - +import shutil +import tempfile # Import Salt Testing Libs from tests.support.unit import skipIf, TestCase -from tests.support.helpers import destructiveTest, skip_if_not_root +from tests.support.runtests import RUNTIME_VARS from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import Salt Libs @@ -24,11 +23,6 @@ import salt.utils.parsers import salt.utils.platform -try: - import pytest -except ImportError: - pytest = None - class ErrorMock(object): # pylint: disable=too-few-public-methods ''' @@ -109,9 +103,7 @@ def __init__(self, d): self.__dict__ = d -@destructiveTest -@skip_if_not_root -class LogSettingsParserTests(TestCase): +class ParserBase(object): ''' Unit Tests for Log Level Mixin with Salt parsers ''' @@ -126,10 +118,25 @@ class LogSettingsParserTests(TestCase): logfile_config_setting_name = 'log_file' logfile_loglevel_config_setting_name = 'log_level_logfile' # pylint: disable=invalid-name + @classmethod + def setUpClass(cls): + cls.root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + + @classmethod + def tearDownClass(cls): + shutil.rmtree(cls.root_dir, ignore_errors=True) + def setup_log(self): ''' Mock logger functions ''' + testing_config = self.default_config.copy() + testing_config['root_dir'] = self.root_dir + for name in ('pki_dir', 'cachedir'): + testing_config[name] = name + testing_config[self.logfile_config_setting_name] = getattr(self, self.logfile_config_setting_name, self.log_file) + self.testing_config = testing_config + self.addCleanup(setattr, self, 'testing_config', None) self.log_setup = LogSetupMock() patcher = patch.multiple( log, @@ -151,14 +158,14 @@ def test_get_log_level_cli(self): Tests that log level match command-line specified value ''' # Set defaults - default_log_level = self.default_config[self.loglevel_config_setting_name] + default_log_level = self.testing_config[self.loglevel_config_setting_name] # Set log level in CLI log_level = 'critical' args = ['--log-level', log_level] + self.args parser = self.parser() - with patch(self.config_func, MagicMock(return_value=self.default_config)): + with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): parser.setup_logfile_logger() @@ -183,7 +190,7 @@ def test_get_log_level_config(self): # Set log level in config log_level = 'info' - opts = self.default_config.copy() + opts = self.testing_config.copy() opts.update({self.loglevel_config_setting_name: log_level}) parser = self.parser() @@ -209,12 +216,12 @@ def test_get_log_level_default(self): Tests that log level match the default value ''' # Set defaults - log_level = default_log_level = self.default_config[self.loglevel_config_setting_name] + log_level = default_log_level = self.testing_config[self.loglevel_config_setting_name] args = self.args parser = self.parser() - with patch(self.config_func, MagicMock(return_value=self.default_config)): + with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): parser.setup_logfile_logger() @@ -242,14 +249,14 @@ def test_get_log_file_cli(self): Tests that log file match command-line specified value ''' # Set defaults - log_level = self.default_config[self.loglevel_config_setting_name] + log_level = self.testing_config[self.loglevel_config_setting_name] # Set log file in CLI log_file = '{0}_cli.log'.format(self.log_file) args = ['--log-file', log_file] + self.args parser = self.parser() - with patch(self.config_func, MagicMock(return_value=self.default_config)): + with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): parser.setup_logfile_logger() @@ -276,13 +283,13 @@ def test_get_log_file_config(self): Tests that log file match the configured value ''' # Set defaults - log_level = self.default_config[self.loglevel_config_setting_name] + log_level = self.testing_config[self.loglevel_config_setting_name] args = self.args # Set log file in config log_file = '{0}_config.log'.format(self.log_file) - opts = self.default_config.copy() + opts = self.testing_config.copy() opts.update({self.logfile_config_setting_name: log_file}) parser = self.parser() @@ -313,13 +320,14 @@ def test_get_log_file_default(self): Tests that log file match the default value ''' # Set defaults - log_level = self.default_config[self.loglevel_config_setting_name] - log_file = default_log_file = self.default_config[self.logfile_config_setting_name] + log_level = self.testing_config[self.loglevel_config_setting_name] + log_file = self.testing_config[self.logfile_config_setting_name] + default_log_file = self.default_config[self.logfile_config_setting_name] args = self.args parser = self.parser() - with patch(self.config_func, MagicMock(return_value=self.default_config)): + with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): parser.setup_logfile_logger() @@ -351,14 +359,14 @@ def test_get_log_file_level_cli(self): Tests that file log level match command-line specified value ''' # Set defaults - default_log_level = self.default_config[self.loglevel_config_setting_name] + default_log_level = self.testing_config[self.loglevel_config_setting_name] # Set log file level in CLI log_level_logfile = 'error' args = ['--log-file-level', log_level_logfile] + self.args parser = self.parser() - with patch(self.config_func, MagicMock(return_value=self.default_config)): + with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): parser.setup_logfile_logger() @@ -386,13 +394,13 @@ def test_get_log_file_level_config(self): Tests that log file level match the configured value ''' # Set defaults - log_level = self.default_config[self.loglevel_config_setting_name] + log_level = self.testing_config[self.loglevel_config_setting_name] args = self.args # Set log file level in config log_level_logfile = 'info' - opts = self.default_config.copy() + opts = self.testing_config.copy() opts.update({self.logfile_loglevel_config_setting_name: log_level_logfile}) parser = self.parser() @@ -424,7 +432,7 @@ def test_get_log_file_level_default(self): Tests that log file level match the default value ''' # Set defaults - default_log_level = self.default_config[self.loglevel_config_setting_name] + default_log_level = self.testing_config[self.loglevel_config_setting_name] log_level = default_log_level log_level_logfile = default_log_level @@ -432,7 +440,7 @@ def test_get_log_file_level_default(self): args = self.args parser = self.parser() - with patch(self.config_func, MagicMock(return_value=self.default_config)): + with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): parser.setup_logfile_logger() @@ -467,7 +475,7 @@ def test_get_console_log_level_with_file_log_level(self): # pylint: disable=inv args = ['--log-file-level', log_level_logfile] + self.args - opts = self.default_config.copy() + opts = self.testing_config.copy() opts.update({self.loglevel_config_setting_name: log_level}) parser = self.parser() @@ -502,7 +510,7 @@ def test_log_created(self): args = self.args log_file = self.log_file log_file_name = self.logfile_config_setting_name - opts = self.default_config.copy() + opts = self.testing_config.copy() opts.update({'log_file': log_file}) if log_file_name != 'log_file': opts.update({log_file_name: getattr(self, log_file_name)}) @@ -519,10 +527,32 @@ def test_log_created(self): else: self.assertEqual(os.path.getsize(getattr(self, log_file_name)), 0) + def test_callbacks_uniqueness(self): + ''' + Test that the callbacks are only added once, no matter + how many instances of the parser we create + ''' + mixin_container_names = ('_mixin_setup_funcs', + '_mixin_process_funcs', + '_mixin_after_parsed_funcs', + '_mixin_before_exit_funcs') + parser = self.parser() + nums_1 = {} + for cb_container in mixin_container_names: + obj = getattr(parser, cb_container) + nums_1[cb_container] = len(obj) + + # The next time we instantiate the parser, the counts should be equal + parser = self.parser() + nums_2 = {} + for cb_container in mixin_container_names: + obj = getattr(parser, cb_container) + nums_2[cb_container] = len(obj) + self.assertDictEqual(nums_1, nums_2) + -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(salt.utils.platform.is_windows(), 'Windows uses a logging listener') -class MasterOptionParserTestCase(LogSettingsParserTests): +class MasterOptionParserTestCase(ParserBase, TestCase): ''' Tests parsing Salt Master options ''' @@ -531,7 +561,7 @@ def setUp(self): Setting up ''' # Set defaults - self.default_config = salt.config.DEFAULT_MASTER_OPTS + self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() self.addCleanup(delattr, self, 'default_config') # Log file @@ -546,10 +576,13 @@ def setUp(self): self.parser = salt.utils.parsers.MasterOptionParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(salt.utils.platform.is_windows(), 'Windows uses a logging listener') -class MinionOptionParserTestCase(LogSettingsParserTests): +class MinionOptionParserTestCase(ParserBase, TestCase): ''' Tests parsing Salt Minion options ''' @@ -558,7 +591,7 @@ def setUp(self): Setting up ''' # Set defaults - self.default_config = salt.config.DEFAULT_MINION_OPTS + self.default_config = salt.config.DEFAULT_MINION_OPTS.copy() self.addCleanup(delattr, self, 'default_config') # Log file @@ -573,9 +606,12 @@ def setUp(self): self.parser = salt.utils.parsers.MinionOptionParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + -@skipIf(NO_MOCK, NO_MOCK_REASON) -class ProxyMinionOptionParserTestCase(LogSettingsParserTests): +class ProxyMinionOptionParserTestCase(ParserBase, TestCase): ''' Tests parsing Salt Proxy Minion options ''' @@ -600,10 +636,13 @@ def setUp(self): self.parser = salt.utils.parsers.ProxyMinionOptionParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(salt.utils.platform.is_windows(), 'Windows uses a logging listener') -class SyndicOptionParserTestCase(LogSettingsParserTests): +class SyndicOptionParserTestCase(ParserBase, TestCase): ''' Tests parsing Salt Syndic options ''' @@ -615,7 +654,7 @@ def setUp(self): self.logfile_config_setting_name = 'syndic_log_file' # Set defaults - self.default_config = salt.config.DEFAULT_MASTER_OPTS + self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() self.addCleanup(delattr, self, 'default_config') # Log file @@ -631,9 +670,14 @@ def setUp(self): self.parser = salt.utils.parsers.SyndicOptionParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + if os.path.exists(self.syndic_log_file): + os.unlink(self.syndic_log_file) + -@skipIf(NO_MOCK, NO_MOCK_REASON) -class SaltCMDOptionParserTestCase(LogSettingsParserTests): +class SaltCMDOptionParserTestCase(ParserBase, TestCase): ''' Tests parsing Salt CLI options ''' @@ -645,7 +689,7 @@ def setUp(self): self.args = ['foo', 'bar.baz'] # Set defaults - self.default_config = salt.config.DEFAULT_MASTER_OPTS + self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() self.addCleanup(delattr, self, 'default_config') # Log file @@ -660,9 +704,12 @@ def setUp(self): self.parser = salt.utils.parsers.SaltCMDOptionParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + -@skipIf(NO_MOCK, NO_MOCK_REASON) -class SaltCPOptionParserTestCase(LogSettingsParserTests): +class SaltCPOptionParserTestCase(ParserBase, TestCase): ''' Tests parsing salt-cp options ''' @@ -674,7 +721,7 @@ def setUp(self): self.args = ['foo', 'bar', 'baz'] # Set defaults - self.default_config = salt.config.DEFAULT_MASTER_OPTS + self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() self.addCleanup(delattr, self, 'default_config') # Log file @@ -689,9 +736,12 @@ def setUp(self): self.parser = salt.utils.parsers.SaltCPOptionParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + -@skipIf(NO_MOCK, NO_MOCK_REASON) -class SaltKeyOptionParserTestCase(LogSettingsParserTests): +class SaltKeyOptionParserTestCase(ParserBase, TestCase): ''' Tests parsing salt-key options ''' @@ -705,7 +755,7 @@ def setUp(self): self.logfile_config_setting_name = 'key_logfile' # Set defaults - self.default_config = salt.config.DEFAULT_MASTER_OPTS + self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() self.addCleanup(delattr, self, 'default_config') # Log file @@ -785,7 +835,7 @@ def test_get_log_level_default(self): Tests that log level default value is ignored ''' # Set defaults - default_log_level = self.default_config[self.loglevel_config_setting_name] + default_log_level = self.testing_config[self.loglevel_config_setting_name] log_level = None args = self.args @@ -806,9 +856,14 @@ def test_get_log_level_default(self): # Check log file logger log level self.assertEqual(self.log_setup.log_level_logfile, default_log_level) + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + if os.path.exists(self.key_logfile): + os.unlink(self.key_logfile) + -@skipIf(NO_MOCK, NO_MOCK_REASON) -class SaltCallOptionParserTestCase(LogSettingsParserTests): +class SaltCallOptionParserTestCase(ParserBase, TestCase): ''' Tests parsing Salt Minion options ''' @@ -820,7 +875,7 @@ def setUp(self): self.args = ['foo.bar'] # Set defaults - self.default_config = salt.config.DEFAULT_MINION_OPTS + self.default_config = salt.config.DEFAULT_MINION_OPTS.copy() self.addCleanup(delattr, self, 'default_config') # Log file @@ -835,9 +890,12 @@ def setUp(self): self.parser = salt.utils.parsers.SaltCallOptionParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + -@skipIf(NO_MOCK, NO_MOCK_REASON) -class SaltRunOptionParserTestCase(LogSettingsParserTests): +class SaltRunOptionParserTestCase(ParserBase, TestCase): ''' Tests parsing Salt Master options ''' @@ -849,7 +907,7 @@ def setUp(self): self.args = ['foo.bar'] # Set defaults - self.default_config = salt.config.DEFAULT_MASTER_OPTS + self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() self.addCleanup(delattr, self, 'default_config') # Log file @@ -864,9 +922,12 @@ def setUp(self): self.parser = salt.utils.parsers.SaltRunOptionParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + -@skipIf(NO_MOCK, NO_MOCK_REASON) -class SaltSSHOptionParserTestCase(LogSettingsParserTests): +class SaltSSHOptionParserTestCase(ParserBase, TestCase): ''' Tests parsing Salt Master options ''' @@ -881,7 +942,7 @@ def setUp(self): self.logfile_config_setting_name = 'ssh_log_file' # Set defaults - self.default_config = salt.config.DEFAULT_MASTER_OPTS + self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() self.addCleanup(delattr, self, 'default_config') # Log file @@ -897,9 +958,14 @@ def setUp(self): self.parser = salt.utils.parsers.SaltSSHOptionParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + if os.path.exists(self.ssh_log_file): + os.unlink(self.ssh_log_file) + -@skipIf(NO_MOCK, NO_MOCK_REASON) -class SaltCloudParserTestCase(LogSettingsParserTests): +class SaltCloudParserTestCase(ParserBase, TestCase): ''' Tests parsing Salt Cloud options ''' @@ -930,9 +996,12 @@ def setUp(self): self.parser = salt.utils.parsers.SaltCloudParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + -@skipIf(NO_MOCK, NO_MOCK_REASON) -class SPMParserTestCase(LogSettingsParserTests): +class SPMParserTestCase(ParserBase, TestCase): ''' Tests parsing Salt Cloud options ''' @@ -964,9 +1033,14 @@ def setUp(self): self.parser = salt.utils.parsers.SPMParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + if os.path.exists(self.spm_logfile): + os.unlink(self.spm_logfile) -@skipIf(NO_MOCK, NO_MOCK_REASON) -class SaltAPIParserTestCase(LogSettingsParserTests): + +class SaltAPIParserTestCase(ParserBase, TestCase): ''' Tests parsing Salt Cloud options ''' @@ -998,9 +1072,13 @@ def setUp(self): self.parser = salt.utils.parsers.SaltAPIParser self.addCleanup(delattr, self, 'parser') + def tearDown(self): + if os.path.exists(self.log_file): + os.unlink(self.log_file) + if os.path.exists(self.api_logfile): + os.unlink(self.api_logfile) + -@skipIf(not pytest, False) -@skipIf(NO_MOCK, NO_MOCK_REASON) class DaemonMixInTestCase(TestCase): ''' Tests the PIDfile deletion in the DaemonMixIn. @@ -1074,7 +1152,3 @@ def test_pid_deleted_oserror_as_non_root(self): assert salt.utils.parsers.os.unlink.call_count == 1 salt.utils.parsers.logger.info.assert_not_called() salt.utils.parsers.logger.debug.assert_not_called() - - -# Hide the class from unittest framework when it searches for TestCase classes in the module -del LogSettingsParserTests diff --git a/tests/unit/utils/test_path.py b/tests/unit/utils/test_path.py index a693492cc870..5134fbd1996b 100644 --- a/tests/unit/utils/test_path.py +++ b/tests/unit/utils/test_path.py @@ -14,7 +14,7 @@ # Import Salt Testing libs from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON +from tests.support.mock import patch # Import Salt libs import salt.utils.compat @@ -137,7 +137,6 @@ def __unpatch_path(self): salt.utils.compat.reload(module) -@skipIf(NO_MOCK, NO_MOCK_REASON) class PathTestCase(TestCase): def test_which_bin(self): ret = salt.utils.path.which_bin('str') @@ -160,14 +159,12 @@ def test_sanitize_win_path(self): self.assertEqual(salt.utils.path.sanitize_win_path('\\windows\\system'), '\\windows\\system') self.assertEqual(salt.utils.path.sanitize_win_path('\\bo:g|us\\p?at*h>'), '\\bo_g_us\\p_at_h_') - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_check_or_die(self): self.assertRaises(CommandNotFoundError, salt.utils.path.check_or_die, None) with patch('salt.utils.path.which', return_value=False): self.assertRaises(CommandNotFoundError, salt.utils.path.check_or_die, 'FAKE COMMAND') - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_join(self): with patch('salt.utils.platform.is_windows', return_value=False) as is_windows_mock: self.assertFalse(is_windows_mock.return_value) @@ -176,7 +173,6 @@ def test_join(self): self.assertEqual(ret, expected_path) -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestWhich(TestCase): ''' Tests salt.utils.path.which function to ensure that it returns True as @@ -186,27 +182,28 @@ class TestWhich(TestCase): # The mock patch below will make sure that ALL calls to the which function # returns None def test_missing_binary_in_linux(self): - with patch('salt.utils.path.which', lambda exe: None): - self.assertTrue( - salt.utils.path.which('this-binary-does-not-exist') is None - ) + # salt.utils.path.which uses platform.is_windows to determine the platform, so we're using linux here + with patch('salt.utils.platform.is_windows', lambda: False): + with patch('salt.utils.path.which', lambda exe: None): + self.assertTrue( + salt.utils.path.which('this-binary-does-not-exist') is None + ) # The mock patch below will make sure that ALL calls to the which function # return whatever is sent to it def test_existing_binary_in_linux(self): - with patch('salt.utils.path.which', lambda exe: exe): - self.assertTrue(salt.utils.path.which('this-binary-exists-under-linux')) + # salt.utils.path.which uses platform.is_windows to determine the platform, so we're using linux here + with patch('salt.utils.platform.is_windows', lambda: False): + with patch('salt.utils.path.which', lambda exe: exe): + self.assertTrue(salt.utils.path.which('this-binary-exists-under-linux')) def test_existing_binary_in_windows(self): - with patch('os.access') as osaccess: + with patch('os.path.isfile') as isfile: # We define the side_effect attribute on the mocked object in order to - # specify which calls return which values. First call to os.access + # specify which calls return which values. First call to os.path.isfile # returns X, the second Y, the third Z, etc... - osaccess.side_effect = [ - # The first os.access should return False(the abspath one) - False, - # The second, iterating through $PATH, should also return False, - # still checking for Linux + isfile.side_effect = [ + # The first os.path.isfile should return False due to checking the explicit path (first is_executable) False, # We will now also return False once so we get a .EXE back from # the function, see PATHEXT below. @@ -214,21 +211,27 @@ def test_existing_binary_in_windows(self): # Lastly return True, this is the windows check. True ] - # Let's patch os.environ to provide a custom PATH variable - with patch.dict(os.environ, {'PATH': os.sep + 'bin', - 'PATHEXT': '.COM;.EXE;.BAT;.CMD'}): - # Let's also patch is_windows to return True - with patch('salt.utils.platform.is_windows', lambda: True): - with patch('os.path.isfile', lambda x: True): - self.assertEqual( - salt.utils.path.which('this-binary-exists-under-windows'), - os.path.join(os.sep + 'bin', 'this-binary-exists-under-windows.EXE') - ) + + # Patch os.access so that it always returns True + with patch('os.access', lambda path, mode: True): + # Disable os.path.islink + with patch('os.path.islink', lambda path: False): + # we're using ';' as os.pathsep in this test + with patch('os.pathsep', ';'): + # Let's patch os.environ to provide a custom PATH variable + with patch.dict(os.environ, {'PATH': os.sep + 'bin', + 'PATHEXT': '.COM;.EXE;.BAT;.CMD'}): + # Let's also patch is_windows to return True + with patch('salt.utils.platform.is_windows', lambda: True): + self.assertEqual( + salt.utils.path.which('this-binary-exists-under-windows'), + os.path.join(os.sep + 'bin', 'this-binary-exists-under-windows.EXE') + ) def test_missing_binary_in_windows(self): with patch('os.access') as osaccess: osaccess.side_effect = [ - # The first os.access should return False(the abspath one) + # The first os.access should return False due to checking the explicit path (first is_executable) False, # The second, iterating through $PATH, should also return False, # still checking for Linux @@ -236,27 +239,26 @@ def test_missing_binary_in_windows(self): # be called 5 times False, False, False, False, False ] - # Let's patch os.environ to provide a custom PATH variable - with patch.dict(os.environ, {'PATH': os.sep + 'bin'}): - # Let's also patch is_widows to return True - with patch('salt.utils.platform.is_windows', lambda: True): - self.assertEqual( - # Since we're passing the .exe suffix, the last True above - # will not matter. The result will be None - salt.utils.path.which('this-binary-is-missing-in-windows.exe'), - None - ) + # we're using ';' as os.pathsep in this test + with patch('os.pathsep', ';'): + # Let's patch os.environ to provide a custom PATH variable + with patch.dict(os.environ, {'PATH': os.sep + 'bin'}): + # Let's also patch is_widows to return True + with patch('salt.utils.platform.is_windows', lambda: True): + self.assertEqual( + # Since we're passing the .exe suffix, the last True above + # will not matter. The result will be None + salt.utils.path.which('this-binary-is-missing-in-windows.exe'), + None + ) def test_existing_binary_in_windows_pathext(self): - with patch('os.access') as osaccess: + with patch('os.path.isfile') as isfile: # We define the side_effect attribute on the mocked object in order to - # specify which calls return which values. First call to os.access + # specify which calls return which values. First call to os.path.isfile # returns X, the second Y, the third Z, etc... - osaccess.side_effect = [ - # The first os.access should return False(the abspath one) - False, - # The second, iterating through $PATH, should also return False, - # still checking for Linux + isfile.side_effect = [ + # The first os.path.isfile should return False due to checking the explicit path (first is_executable) False, # We will now also return False 3 times so we get a .CMD back from # the function, see PATHEXT below. @@ -264,14 +266,24 @@ def test_existing_binary_in_windows_pathext(self): False, False, False, True ] - # Let's patch os.environ to provide a custom PATH variable - with patch.dict(os.environ, {'PATH': os.sep + 'bin', - 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;' - '.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY'}): - # Let's also patch is_windows to return True - with patch('salt.utils.platform.is_windows', lambda: True): - with patch('os.path.isfile', lambda x: True): - self.assertEqual( - salt.utils.path.which('this-binary-exists-under-windows'), - os.path.join(os.sep + 'bin', 'this-binary-exists-under-windows.CMD') - ) + + # Patch os.access so that it always returns True + with patch('os.access', lambda path, mode: True): + + # Disable os.path.islink + with patch('os.path.islink', lambda path: False): + + # we're using ';' as os.pathsep in this test + with patch('os.pathsep', ';'): + + # Let's patch os.environ to provide a custom PATH variable + with patch.dict(os.environ, {'PATH': os.sep + 'bin', + 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;' + '.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY'}): + + # Let's also patch is_windows to return True + with patch('salt.utils.platform.is_windows', lambda: True): + self.assertEqual( + salt.utils.path.which('this-binary-exists-under-windows'), + os.path.join(os.sep + 'bin', 'this-binary-exists-under-windows.CMD') + ) diff --git a/tests/unit/utils/test_pbm.py b/tests/unit/utils/test_pbm.py index f154e2875578..a291307fd867 100644 --- a/tests/unit/utils/test_pbm.py +++ b/tests/unit/utils/test_pbm.py @@ -11,7 +11,7 @@ # Import Salt testing libraries from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, \ +from tests.support.mock import patch, MagicMock, \ PropertyMock # Import Salt libraries @@ -31,7 +31,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetProfileManagerTestCase(TestCase): '''Tests for salt.utils.pbm.get_profile_manager''' @@ -106,7 +105,6 @@ def test_profile_manager_raises_runtime_fault(self): self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetPlacementSolverTestCase(TestCase): '''Tests for salt.utils.pbm.get_placement_solver''' @@ -181,7 +179,6 @@ def test_placement_solver_raises_runtime_fault(self): self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetCapabilityDefinitionsTestCase(TestCase): '''Tests for salt.utils.pbm.get_capability_definitions''' @@ -252,7 +249,6 @@ def test_return_cap_definitions(self): 'fake_cap_meta3']) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetPoliciesByIdTestCase(TestCase): '''Tests for salt.utils.pbm.get_policies_by_id''' @@ -302,7 +298,6 @@ def test_return_policies(self): self.assertEqual(ret, self.mock_policies) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetStoragePoliciesTestCase(TestCase): '''Tests for salt.utils.pbm.get_storage_policies''' @@ -392,7 +387,6 @@ def test_return_filtered_policies(self): self.assertEqual(ret, [self.mock_policies[1], self.mock_policies[3]]) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class CreateStoragePolicyTestCase(TestCase): '''Tests for salt.utils.pbm.create_storage_policy''' @@ -440,7 +434,6 @@ def test_create_policy_raises_runtime_fault(self): self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class UpdateStoragePolicyTestCase(TestCase): '''Tests for salt.utils.pbm.update_storage_policy''' @@ -489,7 +482,6 @@ def test_create_policy_raises_runtime_fault(self): self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetDefaultStoragePolicyOfDatastoreTestCase(TestCase): '''Tests for salt.utils.pbm.get_default_storage_policy_of_datastore''' @@ -592,7 +584,6 @@ def test_return_policy_ref(self): self.assertEqual(ret, self.mock_policy_refs[0]) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class AssignDefaultStoragePolicyToDatastoreTestCase(TestCase): '''Tests for salt.utils.pbm.assign_default_storage_policy_to_datastore''' diff --git a/tests/unit/utils/test_pkg.py b/tests/unit/utils/test_pkg.py index c293852058db..e8b19bef1411 100644 --- a/tests/unit/utils/test_pkg.py +++ b/tests/unit/utils/test_pkg.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -# Import Python libs -from __future__ import absolute_import -# Import Salt Libs -import salt.utils.pkg -# Import Salt Testing Libs +from __future__ import absolute_import, unicode_literals, print_function + from tests.support.unit import TestCase +from tests.support.mock import MagicMock, patch +import salt.utils.pkg +from salt.utils.pkg import rpm class PkgUtilsTestCase(TestCase): @@ -45,3 +45,60 @@ def test_split_comparison(self): oper, verstr = salt.utils.pkg.split_comparison(test_parameter[0]) self.assertEqual(test_parameter[1], oper) self.assertEqual(test_parameter[2], verstr) + + +class PkgRPMTestCase(TestCase): + ''' + Test case for pkg.rpm utils + ''' + + @patch('salt.utils.path.which', MagicMock(return_value=True)) + def test_get_osarch_by_rpm(self): + ''' + Get os_arch if RPM package is installed. + :return: + ''' + subprocess_mock = MagicMock() + subprocess_mock.Popen = MagicMock() + subprocess_mock.Popen().communicate = MagicMock(return_value=['Z80']) + with patch('salt.utils.pkg.rpm.subprocess', subprocess_mock): + assert rpm.get_osarch() == 'Z80' + assert subprocess_mock.Popen.call_count == 2 # One within the mock + assert subprocess_mock.Popen.call_args[1]['close_fds'] + assert subprocess_mock.Popen.call_args[1]['shell'] + assert len(subprocess_mock.Popen.call_args_list) == 2 + assert subprocess_mock.Popen.call_args[0][0] == 'rpm --eval "%{_host_cpu}"' + + @patch('salt.utils.path.which', MagicMock(return_value=False)) + @patch('salt.utils.pkg.rpm.subprocess', MagicMock(return_value=False)) + @patch('salt.utils.pkg.rpm.platform.uname', MagicMock( + return_value=('Sinclair BASIC', 'motophone', '1982 Sinclair Research Ltd', '1.0', 'ZX81', 'Z80'))) + def test_get_osarch_by_platform(self): + ''' + Get os_arch if RPM package is not installed (inird image, for example). + :return: + ''' + assert rpm.get_osarch() == 'Z80' + + @patch('salt.utils.path.which', MagicMock(return_value=False)) + @patch('salt.utils.pkg.rpm.subprocess', MagicMock(return_value=False)) + @patch('salt.utils.pkg.rpm.platform.uname', MagicMock( + return_value=('Sinclair BASIC', 'motophone', '1982 Sinclair Research Ltd', '1.0', 'ZX81', ''))) + def test_get_osarch_by_platform_no_cpu_arch(self): + ''' + Get os_arch if RPM package is not installed (inird image, for example) but cpu arch cannot be determined. + :return: + ''' + assert rpm.get_osarch() == 'ZX81' + + @patch('salt.utils.path.which', MagicMock(return_value=False)) + @patch('salt.utils.pkg.rpm.subprocess', MagicMock(return_value=False)) + @patch('salt.utils.pkg.rpm.platform.uname', MagicMock( + return_value=('Sinclair BASIC', 'motophone', '1982 Sinclair Research Ltd', '1.0', '', ''))) + def test_get_osarch_by_platform_no_cpu_arch_no_machine(self): + ''' + Get os_arch if RPM package is not installed (inird image, for example) + where both cpu arch and machine cannot be determined. + :return: + ''' + assert rpm.get_osarch() == 'unknown' diff --git a/tests/unit/utils/test_process.py b/tests/unit/utils/test_process.py index 4e8f7964538d..99345a53fb92 100644 --- a/tests/unit/utils/test_process.py +++ b/tests/unit/utils/test_process.py @@ -10,18 +10,19 @@ import signal import multiprocessing import functools +import datetime +import warnings # Import Salt Testing libs from tests.support.unit import TestCase, skipIf from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import salt libs import salt.utils.platform import salt.utils.process +from salt.utils.versions import warn_until_date # Import 3rd-party libs from salt.ext import six @@ -40,7 +41,9 @@ def wrapper(self): def _die(): salt.utils.process.appendproctitle('test_{0}'.format(name)) - setattr(self, 'die_' + name, _die) + attrname = 'die_' + name + setattr(self, attrname, _die) + self.addCleanup(delattr, self, attrname) return wrapper @@ -58,7 +61,9 @@ def _incr(counter, num): salt.utils.process.appendproctitle('test_{0}'.format(name)) for _ in range(0, num): counter.value += 1 - setattr(self, 'incr_' + name, _incr) + attrname = 'incr_' + name + setattr(self, attrname, _incr) + self.addCleanup(delattr, self, attrname) return wrapper @@ -76,7 +81,9 @@ def _spin(): salt.utils.process.appendproctitle('test_{0}'.format(name)) while True: time.sleep(1) - setattr(self, 'spin_' + name, _spin) + attrname = 'spin_' + name + setattr(self, attrname, _spin) + self.addCleanup(delattr, self, attrname) return wrapper @@ -217,7 +224,6 @@ def incr_counter(counter): class TestProcess(TestCase): - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_daemonize_if(self): # pylint: disable=assignment-from-none with patch('sys.argv', ['salt-call']): @@ -238,7 +244,47 @@ def test_daemonize_if(self): # pylint: enable=assignment-from-none -class TestSignalHandlingMultiprocessingProcess(TestCase): +class TestProcessCallbacks(TestCase): + + @staticmethod + def process_target(evt): + evt.set() + + def test_callbacks(self): + 'Validate Process call after fork and finalize methods' + teardown_to_mock = 'salt.log.setup.shutdown_multiprocessing_logging' + log_to_mock = 'salt.utils.process.Process._setup_process_logging' + with patch(teardown_to_mock) as ma, patch(log_to_mock) as mb: + evt = multiprocessing.Event() + proc = salt.utils.process.Process(target=self.process_target, args=(evt,)) + proc.run() + assert evt.is_set() + mb.assert_called() + ma.assert_called() + + def test_callbacks_called_when_run_overriden(self): + 'Validate Process sub classes call after fork and finalize methods when run is overridden' + + class MyProcess(salt.utils.process.Process): + + def __init__(self): + super(MyProcess, self).__init__() + self.evt = multiprocessing.Event() + + def run(self): + self.evt.set() + + teardown_to_mock = 'salt.log.setup.shutdown_multiprocessing_logging' + log_to_mock = 'salt.utils.process.Process._setup_process_logging' + with patch(teardown_to_mock) as ma, patch(log_to_mock) as mb: + proc = MyProcess() + proc.run() + assert proc.evt.is_set() + ma.assert_called() + mb.assert_called() + + +class TestSignalHandlingProcess(TestCase): @classmethod def Process(cls, pid): @@ -252,20 +298,18 @@ def target(cls): def children(cls, *args, **kwargs): raise psutil.NoSuchProcess(1) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_process_does_not_exist(self): try: with patch('psutil.Process', self.Process): - proc = salt.utils.process.SignalHandlingMultiprocessingProcess(target=self.target) + proc = salt.utils.process.SignalHandlingProcess(target=self.target) proc.start() except psutil.NoSuchProcess: assert False, "psutil.NoSuchProcess raised" - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_process_children_do_not_exist(self): try: with patch('psutil.Process.children', self.children): - proc = salt.utils.process.SignalHandlingMultiprocessingProcess(target=self.target) + proc = salt.utils.process.SignalHandlingProcess(target=self.target) proc.start() except psutil.NoSuchProcess: assert False, "psutil.NoSuchProcess raised" @@ -300,7 +344,7 @@ def kill_target_sub_proc(): @skipIf(sys.platform.startswith('win'), 'No os.fork on Windows') def test_signal_processing_regression_test(self): evt = multiprocessing.Event() - sh_proc = salt.utils.process.SignalHandlingMultiprocessingProcess( + sh_proc = salt.utils.process.SignalHandlingProcess( target=self.run_forever_target, args=(self.run_forever_sub_target, evt) ) @@ -320,33 +364,6 @@ def test_signal_processing_regression_test(self): def no_op_target(): pass - @skipIf(NO_MOCK, NO_MOCK_REASON) - def test_signal_processing_test_after_fork_called(self): - 'Validate MultiprocessingProcess and sub classes call after fork methods' - evt = multiprocessing.Event() - sig_to_mock = 'salt.utils.process.SignalHandlingMultiprocessingProcess._setup_signals' - log_to_mock = 'salt.utils.process.MultiprocessingProcess._setup_process_logging' - with patch(sig_to_mock) as ma, patch(log_to_mock) as mb: - self.sh_proc = salt.utils.process.SignalHandlingMultiprocessingProcess(target=self.no_op_target) - self.sh_proc.run() - ma.assert_called() - mb.assert_called() - - @skipIf(NO_MOCK, NO_MOCK_REASON) - def test_signal_processing_test_final_methods_called(self): - 'Validate MultiprocessingProcess and sub classes call finalize methods' - evt = multiprocessing.Event() - teardown_to_mock = 'salt.log.setup.shutdown_multiprocessing_logging' - log_to_mock = 'salt.utils.process.MultiprocessingProcess._setup_process_logging' - sig_to_mock = 'salt.utils.process.SignalHandlingMultiprocessingProcess._setup_signals' - # Mock _setup_signals so we do not register one for this process. - with patch(sig_to_mock): - with patch(teardown_to_mock) as ma, patch(log_to_mock) as mb: - self.sh_proc = salt.utils.process.SignalHandlingMultiprocessingProcess(target=self.no_op_target) - self.sh_proc.run() - ma.assert_called() - mb.assert_called() - @staticmethod def pid_setting_target(sub_target, val, evt): val.value = os.getpid() @@ -356,13 +373,13 @@ def pid_setting_target(sub_target, val, evt): @skipIf(sys.platform.startswith('win'), 'Required signals not supported on windows') def test_signal_processing_handle_signals_called(self): - 'Validate SignalHandlingMultiprocessingProcess handles signals' + 'Validate SignalHandlingProcess handles signals' # Gloobal event to stop all processes we're creating evt = multiprocessing.Event() # Create a process to test signal handler val = multiprocessing.Value('i', 0) - proc = salt.utils.process.SignalHandlingMultiprocessingProcess( + proc = salt.utils.process.SignalHandlingProcess( target=self.pid_setting_target, args=(self.run_forever_sub_target, val, evt), ) @@ -403,6 +420,56 @@ def test_signal_processing_handle_signals_called(self): proc.join(30) +class TestSignalHandlingProcessCallbacks(TestCase): + + @staticmethod + def process_target(evt): + evt.set() + + def test_callbacks(self): + 'Validate SignalHandlingProcess call after fork and finalize methods' + + teardown_to_mock = 'salt.log.setup.shutdown_multiprocessing_logging' + log_to_mock = 'salt.utils.process.Process._setup_process_logging' + sig_to_mock = 'salt.utils.process.SignalHandlingProcess._setup_signals' + # Mock _setup_signals so we do not register one for this process. + evt = multiprocessing.Event() + with patch(sig_to_mock): + with patch(teardown_to_mock) as ma, patch(log_to_mock) as mb: + sh_proc = salt.utils.process.SignalHandlingProcess( + target=self.process_target, + args=(evt,) + ) + sh_proc.run() + assert evt.is_set() + ma.assert_called() + mb.assert_called() + + def test_callbacks_called_when_run_overriden(self): + 'Validate SignalHandlingProcess sub classes call after fork and finalize methods when run is overridden' + + class MyProcess(salt.utils.process.SignalHandlingProcess): + + def __init__(self): + super(MyProcess, self).__init__() + self.evt = multiprocessing.Event() + + def run(self): + self.evt.set() + + teardown_to_mock = 'salt.log.setup.shutdown_multiprocessing_logging' + log_to_mock = 'salt.utils.process.Process._setup_process_logging' + sig_to_mock = 'salt.utils.process.SignalHandlingProcess._setup_signals' + # Mock _setup_signals so we do not register one for this process. + with patch(sig_to_mock): + with patch(teardown_to_mock) as ma, patch(log_to_mock) as mb: + sh_proc = MyProcess() + sh_proc.run() + assert sh_proc.evt.is_set() + ma.assert_called() + mb.assert_called() + + class TestDup2(TestCase): def test_dup2_no_fileno(self): @@ -475,3 +542,121 @@ def test_process_list_cleanup(self): assert not proc.is_alive() plist.cleanup() assert proc not in plist.processes + + +class TestDeprecatedClassNames(TestCase): + + @staticmethod + def process_target(): + pass + + @staticmethod + def patched_warn_until_date(current_date): + def _patched_warn_until_date(date, + message, + category=DeprecationWarning, + stacklevel=None, + _current_date=current_date, + _dont_call_warnings=False): + # Because we add another function in between, the stacklevel + # set in salt.utils.process, 3, needs to now be 4 + stacklevel = 4 + return warn_until_date(date, + message, + category=category, + stacklevel=stacklevel, + _current_date=_current_date, + _dont_call_warnings=_dont_call_warnings) + return _patched_warn_until_date + + def test_multiprocessing_process_warning(self): + # We *always* want *all* warnings thrown on this module + warnings.filterwarnings('always', '', DeprecationWarning, __name__) + + fake_utcnow = datetime.date(2021, 1, 1) + + proc = None + + try: + with patch('salt.utils.versions.warn_until_date', self.patched_warn_until_date(fake_utcnow)): + # Test warning + with warnings.catch_warnings(record=True) as recorded_warnings: + proc = salt.utils.process.MultiprocessingProcess(target=self.process_target) + self.assertEqual( + 'Please stop using \'salt.utils.process.MultiprocessingProcess\' ' + 'and instead use \'salt.utils.process.Process\'. ' + '\'salt.utils.process.MultiprocessingProcess\' will go away ' + 'after 2022-01-01.', + six.text_type(recorded_warnings[0].message) + ) + finally: + if proc is not None: + del proc + + def test_multiprocessing_process_runtime_error(self): + fake_utcnow = datetime.date(2022, 1, 1) + + proc = None + + try: + with patch('salt.utils.versions.warn_until_date', self.patched_warn_until_date(fake_utcnow)): + with self.assertRaisesRegex( + RuntimeError, + r"Please stop using 'salt.utils.process.MultiprocessingProcess' " + r"and instead use 'salt.utils.process.Process'. " + r"'salt.utils.process.MultiprocessingProcess' will go away " + r'after 2022-01-01. ' + r'This warning\(now exception\) triggered on ' + r"filename '(.*)test_process.py', line number ([\d]+), is " + r'supposed to be shown until ([\d-]+). Today is ([\d-]+). ' + r'Please remove the warning.'): + proc = salt.utils.process.MultiprocessingProcess(target=self.process_target) + finally: + if proc is not None: + del proc + + def test_signal_handling_multiprocessing_process_warning(self): + # We *always* want *all* warnings thrown on this module + warnings.filterwarnings('always', '', DeprecationWarning, __name__) + + fake_utcnow = datetime.date(2021, 1, 1) + + proc = None + + try: + with patch('salt.utils.versions.warn_until_date', self.patched_warn_until_date(fake_utcnow)): + # Test warning + with warnings.catch_warnings(record=True) as recorded_warnings: + proc = salt.utils.process.SignalHandlingMultiprocessingProcess(target=self.process_target) + self.assertEqual( + 'Please stop using \'salt.utils.process.SignalHandlingMultiprocessingProcess\' ' + 'and instead use \'salt.utils.process.SignalHandlingProcess\'. ' + '\'salt.utils.process.SignalHandlingMultiprocessingProcess\' will go away ' + 'after 2022-01-01.', + six.text_type(recorded_warnings[0].message) + ) + finally: + if proc is not None: + del proc + + def test_signal_handling_multiprocessing_process_runtime_error(self): + fake_utcnow = datetime.date(2022, 1, 1) + + proc = None + + try: + with patch('salt.utils.versions.warn_until_date', self.patched_warn_until_date(fake_utcnow)): + with self.assertRaisesRegex( + RuntimeError, + r"Please stop using 'salt.utils.process.SignalHandlingMultiprocessingProcess' " + r"and instead use 'salt.utils.process.SignalHandlingProcess'. " + r"'salt.utils.process.SignalHandlingMultiprocessingProcess' will go away " + r'after 2022-01-01. ' + r'This warning\(now exception\) triggered on ' + r"filename '(.*)test_process.py', line number ([\d]+), is " + r'supposed to be shown until ([\d-]+). Today is ([\d-]+). ' + r'Please remove the warning.'): + proc = salt.utils.process.SignalHandlingMultiprocessingProcess(target=self.process_target) + finally: + if proc is not None: + del proc diff --git a/tests/unit/utils/test_pydsl.py b/tests/unit/utils/test_pydsl.py index c3a5330b1b14..5a232db30e73 100644 --- a/tests/unit/utils/test_pydsl.py +++ b/tests/unit/utils/test_pydsl.py @@ -10,9 +10,9 @@ import copy # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.helpers import with_tempdir from tests.support.unit import TestCase -from tests.support.paths import TMP # Import Salt libs import salt.loader @@ -34,7 +34,7 @@ class CommonTestCaseBoilerplate(TestCase): def setUp(self): - self.root_dir = tempfile.mkdtemp(dir=TMP) + self.root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.root_dir, ignore_errors=True) self.state_tree_dir = os.path.join(self.root_dir, 'state_tree') self.cache_dir = os.path.join(self.root_dir, 'cachedir') diff --git a/tests/unit/utils/test_pyobjects.py b/tests/unit/utils/test_pyobjects.py index 8908d63f061d..70daf6e7f51e 100644 --- a/tests/unit/utils/test_pyobjects.py +++ b/tests/unit/utils/test_pyobjects.py @@ -12,9 +12,9 @@ # Import Salt Testing libs from tests.support.unit import TestCase +from tests.support.runtests import RUNTIME_VARS # Import Salt libs -import tests.integration as integration import salt.config import salt.state import salt.utils.files @@ -270,7 +270,7 @@ class so that our setUp & tearDown get invoked first, and super can def setUp(self, *args, **kwargs): super(RendererMixin, self).setUp(*args, **kwargs) - self.root_dir = tempfile.mkdtemp('pyobjects_test_root', dir=integration.TMP) + self.root_dir = tempfile.mkdtemp('pyobjects_test_root', dir=RUNTIME_VARS.TMP) self.state_tree_dir = os.path.join(self.root_dir, 'state_tree') self.cache_dir = os.path.join(self.root_dir, 'cachedir') if not os.path.isdir(self.root_dir): diff --git a/tests/unit/utils/test_reactor.py b/tests/unit/utils/test_reactor.py index a33d16942e02..dc7b5c791f4d 100644 --- a/tests/unit/utils/test_reactor.py +++ b/tests/unit/utils/test_reactor.py @@ -13,11 +13,9 @@ import salt.utils.reactor as reactor import salt.utils.yaml -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mixins import AdaptedConfigurationTestCaseMixin from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, patch, MagicMock, Mock, @@ -380,7 +378,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestReactor(TestCase, AdaptedConfigurationTestCaseMixin): ''' Tests for constructing the low chunks to be executed via the Reactor @@ -462,7 +459,6 @@ def test_reactions(self): self.assertEqual(reactions, LOW_CHUNKS[tag]) -@skipIf(NO_MOCK, NO_MOCK_REASON) class TestReactWrap(TestCase, AdaptedConfigurationTestCaseMixin): ''' Tests that we are formulating the wrapper calls properly diff --git a/tests/unit/utils/test_sanitizers.py b/tests/unit/utils/test_sanitizers.py index 51f2219c31ba..90f4b1de95fb 100644 --- a/tests/unit/utils/test_sanitizers.py +++ b/tests/unit/utils/test_sanitizers.py @@ -8,11 +8,9 @@ from salt.utils.sanitizers import clean, mask_args_value # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase -@skipIf(NO_MOCK, NO_MOCK_REASON) class SanitizersTestCase(TestCase): ''' TestCase for sanitizers diff --git a/tests/unit/utils/test_schedule.py b/tests/unit/utils/test_schedule.py index 8e91de417907..f5daf23c2b5f 100644 --- a/tests/unit/utils/test_schedule.py +++ b/tests/unit/utils/test_schedule.py @@ -12,8 +12,8 @@ # Import Salt Testing Libs from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch, NO_MOCK, NO_MOCK_REASON -import tests.integration as integration +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS # Import Salt Libs import salt.config @@ -29,8 +29,8 @@ log = logging.getLogger(__name__) + # pylint: disable=too-many-public-methods,invalid-name -@skipIf(NO_MOCK, NO_MOCK_REASON) class ScheduleTestCase(TestCase): ''' Unit tests for salt.utils.schedule module @@ -38,7 +38,7 @@ class ScheduleTestCase(TestCase): @classmethod def setUpClass(cls): - root_dir = os.path.join(integration.TMP, 'schedule-unit-tests') + root_dir = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') default_config = salt.config.minion_config(None) default_config['conf_dir'] = default_config['root_dir'] = root_dir default_config['sock_dir'] = os.path.join(root_dir, 'test-socks') diff --git a/tests/unit/utils/test_schema.py b/tests/unit/utils/test_schema.py index a07fcbab6bce..ed1515f9fec6 100644 --- a/tests/unit/utils/test_schema.py +++ b/tests/unit/utils/test_schema.py @@ -1734,7 +1734,9 @@ class TestConf(schema.Schema): with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': {'sides': '4', 'color': 'blue'}}, TestConf.serialize()) if JSONSCHEMA_VERSION >= _LooseVersion('3.0.0'): - self.assertIn('\'4\' is not of type \'boolean\'', excinfo.exception.message) + self.assertIn("'4'", excinfo.exception.message) + self.assertIn("is not of type", excinfo.exception.message) + self.assertIn("'boolean'", excinfo.exception.message) else: self.assertIn('is not valid under any of the given schemas', excinfo.exception.message) @@ -1840,7 +1842,9 @@ class TestConf(schema.Schema): with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': ['maybe']}, TestConf.serialize()) if JSONSCHEMA_VERSION >= _LooseVersion('3.0.0'): - self.assertIn('\'maybe\' is not one of [\'yes\']', excinfo.exception.message) + self.assertIn("'maybe'", excinfo.exception.message) + self.assertIn("is not one of", excinfo.exception.message) + self.assertIn("'yes'", excinfo.exception.message) else: self.assertIn('is not valid under any of the given schemas', excinfo.exception.message) @@ -1895,7 +1899,9 @@ class TestConf(schema.Schema): with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate({'item': ['maybe']}, TestConf.serialize()) if JSONSCHEMA_VERSION >= _LooseVersion('3.0.0'): - self.assertIn('\'maybe\' is not one of [\'yes\']', excinfo.exception.message) + self.assertIn("'maybe'", excinfo.exception.message) + self.assertIn("is not one of", excinfo.exception.message) + self.assertIn("'yes'", excinfo.exception.message) else: self.assertIn('is not valid under any of the given schemas', excinfo.exception.message) diff --git a/tests/unit/utils/test_sdb.py b/tests/unit/utils/test_sdb.py index 3278989cc0e5..5d2242d2ee17 100644 --- a/tests/unit/utils/test_sdb.py +++ b/tests/unit/utils/test_sdb.py @@ -10,39 +10,35 @@ # Import Salt Testing Libs from tests.support.runtests import RUNTIME_VARS from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON -) +from tests.support.unit import TestCase # Import Salt Libs import salt.utils.sdb as sdb -TEMP_DATABASE_FILE = os.path.join(RUNTIME_VARS.TMP, 'test_sdb.sqlite') -SDB_OPTS = { +class SdbTestCase(TestCase, LoaderModuleMockMixin): + ''' + Test cases for salt.modules.sdb + ''' + + @classmethod + def setUpClass(cls): + cls.TEMP_DATABASE_FILE = os.path.join(RUNTIME_VARS.TMP, 'test_sdb.sqlite') + cls.sdb_opts = { 'extension_modules': '', 'optimization_order': [0, 1, 2], 'test_sdb_data': { 'driver': 'sqlite3', - 'database': TEMP_DATABASE_FILE, + 'database': cls.TEMP_DATABASE_FILE, 'table': 'sdb', 'create_table': True } - } - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -class SdbTestCase(TestCase, LoaderModuleMockMixin): - ''' - Test cases for salt.modules.sdb - ''' + } @classmethod def tearDownClass(cls): try: - os.unlink(TEMP_DATABASE_FILE) + os.unlink(cls.TEMP_DATABASE_FILE) except OSError: pass @@ -53,13 +49,13 @@ def setup_loader_modules(self): def test_sqlite_get_not_found(self): what = sdb.sdb_get( - 'sdb://test_sdb_data/thisKeyDoesNotExist', SDB_OPTS) + 'sdb://test_sdb_data/thisKeyDoesNotExist', self.sdb_opts) self.assertEqual(what, None) # test with SQLite database write and read def test_sqlite_get_found(self): expected = {b'name': b'testone', b'number': 46} - sdb.sdb_set('sdb://test_sdb_data/test1', expected, SDB_OPTS) - resp = sdb.sdb_get('sdb://test_sdb_data/test1', SDB_OPTS) + sdb.sdb_set('sdb://test_sdb_data/test1', expected, self.sdb_opts) + resp = sdb.sdb_get('sdb://test_sdb_data/test1', self.sdb_opts) self.assertEqual(resp, expected) diff --git a/tests/unit/utils/test_ssdp.py b/tests/unit/utils/test_ssdp.py index 88ea1764167d..d73caa2fa8f6 100644 --- a/tests/unit/utils/test_ssdp.py +++ b/tests/unit/utils/test_ssdp.py @@ -7,8 +7,6 @@ from tests.support.unit import TestCase, skipIf from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) @@ -35,12 +33,42 @@ def get_socket_mock(self, expected_ip, expected_hostname): sock_mock = MagicMock() sock_mock.socket = MagicMock(return_value=sck) - sock_mock.gethostbyname = MagicMock(return_value=expected_hostname) + sock_mock.gethostname = MagicMock(return_value=expected_hostname) + sock_mock.gethostbyname = MagicMock(return_value=expected_ip) return sock_mock + def get_ssdp_factory(self, expected_ip=None, expected_hostname=None, **config): + if expected_ip is None: + expected_ip = '127.0.0.1' + if expected_hostname is None: + expected_hostname = 'localhost' + sock_mock = self.get_socket_mock(expected_ip, expected_hostname) + with patch('salt.utils.ssdp.socket', sock_mock): + factory = ssdp.SSDPFactory(**config) + return factory + + def get_ssdp_discovery_client(self, expected_ip=None, expected_hostname=None, **config): + if expected_ip is None: + expected_ip = '127.0.0.1' + if expected_hostname is None: + expected_hostname = 'localhost' + sock_mock = self.get_socket_mock(expected_ip, expected_hostname) + with patch('salt.utils.ssdp.socket', sock_mock): + factory = ssdp.SSDPDiscoveryClient(**config) + return factory + + def get_ssdp_discovery_server(self, expected_ip=None, expected_hostname=None, **config): + if expected_ip is None: + expected_ip = '127.0.0.1' + if expected_hostname is None: + expected_hostname = 'localhost' + sock_mock = self.get_socket_mock(expected_ip, expected_hostname) + with patch('salt.utils.ssdp.socket', sock_mock): + factory = ssdp.SSDPDiscoveryServer(**config) + return factory + -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(pytest is None, 'PyTest is missing') class SSDPBaseTestCase(TestCase, Mocks): ''' @@ -115,16 +143,14 @@ def test_base_self_ip(self): sock_mock.socket().getsockname.side_effect = SSDPBaseTestCase.exception_generic with patch('salt.utils.ssdp.socket', sock_mock): - assert base.get_self_ip() == expected_host + assert base.get_self_ip() == expected_ip -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(pytest is None, 'PyTest is missing') -class SSDPFactoryTestCase(TestCase): +class SSDPFactoryTestCase(TestCase, Mocks): ''' Test socket protocol ''' - @patch('salt.utils.ssdp.socket.gethostbyname', MagicMock(return_value='10.10.10.10')) def test_attr_check(self): ''' Tests attributes are set to the base class @@ -135,12 +161,13 @@ def test_attr_check(self): ssdp.SSDPBase.SIGNATURE: '-signature-', ssdp.SSDPBase.ANSWER: {'this-is': 'the-answer'} } - factory = ssdp.SSDPFactory(**config) + expected_ip = '10.10.10.10' + factory = self.get_ssdp_factory(expected_ip=expected_ip, **config) for attr in [ssdp.SSDPBase.SIGNATURE, ssdp.SSDPBase.ANSWER]: assert hasattr(factory, attr) assert getattr(factory, attr) == config[attr] assert not factory.disable_hidden - assert factory.my_ip == '10.10.10.10' + assert factory.my_ip == expected_ip def test_transport_sendto_success(self): ''' @@ -150,7 +177,7 @@ def test_transport_sendto_success(self): ''' transport = MagicMock() log = MagicMock() - factory = ssdp.SSDPFactory() + factory = self.get_ssdp_factory() with patch.object(factory, 'transport', transport), patch.object(factory, 'log', log): data = {'some': 'data'} addr = '10.10.10.10' @@ -161,26 +188,26 @@ def test_transport_sendto_success(self): assert factory.log.debug.called assert factory.log.debug.mock_calls[0][1][0] == 'Sent successfully' - @patch('salt.utils.ssdp.time.sleep', MagicMock()) def test_transport_sendto_retry(self): ''' Test transport send_to. :return: ''' - transport = MagicMock() - transport.sendto = MagicMock(side_effect=SSDPBaseTestCase.exception_attr_error) - log = MagicMock() - factory = ssdp.SSDPFactory() - with patch.object(factory, 'transport', transport), patch.object(factory, 'log', log): - data = {'some': 'data'} - addr = '10.10.10.10' - factory._sendto(data=data, addr=addr) - assert factory.transport.sendto.called - assert ssdp.time.sleep.called - assert ssdp.time.sleep.call_args[0][0] > 0 and ssdp.time.sleep.call_args[0][0] < 0.5 - assert factory.log.debug.called - assert 'Permission error' in factory.log.debug.mock_calls[0][1][0] + with patch('salt.utils.ssdp.time.sleep', MagicMock()): + transport = MagicMock() + transport.sendto = MagicMock(side_effect=SSDPBaseTestCase.exception_attr_error) + log = MagicMock() + factory = self.get_ssdp_factory() + with patch.object(factory, 'transport', transport), patch.object(factory, 'log', log): + data = {'some': 'data'} + addr = '10.10.10.10' + factory._sendto(data=data, addr=addr) + assert factory.transport.sendto.called + assert ssdp.time.sleep.called + assert ssdp.time.sleep.call_args[0][0] > 0 and ssdp.time.sleep.call_args[0][0] < 0.5 + assert factory.log.debug.called + assert 'Permission error' in factory.log.debug.mock_calls[0][1][0] def test_datagram_signature_bad(self): ''' @@ -188,7 +215,7 @@ def test_datagram_signature_bad(self): :return: ''' - factory = ssdp.SSDPFactory() + factory = self.get_ssdp_factory() data = 'nonsense' addr = '10.10.10.10', 'foo.suse.de' @@ -205,7 +232,7 @@ def test_datagram_signature_wrong_timestamp_quiet(self): :return: ''' - factory = ssdp.SSDPFactory() + factory = self.get_ssdp_factory() data = '{}nonsense'.format(ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE]) addr = '10.10.10.10', 'foo.suse.de' with patch.object(factory, 'log', MagicMock()), patch.object(factory, '_sendto', MagicMock()): @@ -220,7 +247,7 @@ def test_datagram_signature_wrong_timestamp_reply(self): :return: ''' - factory = ssdp.SSDPFactory() + factory = self.get_ssdp_factory() factory.disable_hidden = True signature = ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE] data = '{}nonsense'.format(signature) @@ -237,7 +264,7 @@ def test_datagram_signature_outdated_timestamp_quiet(self): Test if datagram processing reacts on outdated message (more than 20 seconds). Quiet mode. :return: ''' - factory = ssdp.SSDPFactory() + factory = self.get_ssdp_factory() signature = ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE] data = '{}{}'.format(signature, '1516623820') addr = '10.10.10.10', 'foo.suse.de' @@ -261,7 +288,7 @@ def test_datagram_signature_outdated_timestamp_reply(self): Test if datagram processing reacts on outdated message (more than 20 seconds). Reply mode. :return: ''' - factory = ssdp.SSDPFactory() + factory = self.get_ssdp_factory() factory.disable_hidden = True signature = ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE] data = '{}{}'.format(signature, '1516623820') @@ -287,7 +314,7 @@ def test_datagram_signature_correct_timestamp_reply(self): Test if datagram processing sends out correct reply within 20 seconds. :return: ''' - factory = ssdp.SSDPFactory() + factory = self.get_ssdp_factory() factory.disable_hidden = True signature = ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE] data = '{}{}'.format(signature, '1516623820') @@ -309,9 +336,8 @@ def test_datagram_signature_correct_timestamp_reply(self): assert 'Received "%s" from %s:%s' in factory.log.debug.call_args[0][0] -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(pytest is None, 'PyTest is missing') -class SSDPServerTestCase(TestCase): +class SSDPServerTestCase(TestCase, Mocks): ''' Server-related test cases ''' @@ -328,51 +354,50 @@ def test_config_detached(self): assert srv._config['answer']['master'] == new_ip assert config['answer']['master'] == old_ip - @patch('salt.utils.ssdp.SSDPFactory', MagicMock()) def test_run(self): ''' Test server runner. :return: ''' - config = {'answer': {'master': '10.10.10.10'}, - ssdp.SSDPBase.LISTEN_IP: '10.10.10.10', - ssdp.SSDPBase.PORT: 12345} - srv = ssdp.SSDPDiscoveryServer(**config) - srv.create_datagram_endpoint = MagicMock() - srv.log = MagicMock() - - trnsp = MagicMock() - proto = MagicMock() - loop = MagicMock() - loop.run_until_complete = MagicMock(return_value=(trnsp, proto)) - - io = MagicMock() - io.ported = False - io.get_event_loop = MagicMock(return_value=loop) - - with patch('salt.utils.ssdp.asyncio', io): - srv.run() - cde_args = io.get_event_loop().create_datagram_endpoint.call_args[1] - cfg_ip_addr, cfg_port = cde_args['local_addr'] - - assert io.get_event_loop.called - assert io.get_event_loop().run_until_complete.called - assert io.get_event_loop().create_datagram_endpoint.called - assert io.get_event_loop().run_forever.called - assert trnsp.close.called - assert loop.close.called - assert srv.log.info.called - assert srv.log.info.call_args[0][0] == 'Stopping service discovery listener.' - assert 'allow_broadcast' in cde_args - assert cde_args['allow_broadcast'] - assert 'local_addr' in cde_args - assert not cfg_ip_addr == ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.LISTEN_IP] and cfg_ip_addr == '10.10.10.10' - assert not cfg_port == ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.PORT] and cfg_port == 12345 - - -@skipIf(NO_MOCK, NO_MOCK_REASON) + with patch('salt.utils.ssdp.SSDPFactory', MagicMock()): + config = {'answer': {'master': '10.10.10.10'}, + ssdp.SSDPBase.LISTEN_IP: '10.10.10.10', + ssdp.SSDPBase.PORT: 12345} + srv = self.get_ssdp_discovery_server(**config) + srv.create_datagram_endpoint = MagicMock() + srv.log = MagicMock() + + trnsp = MagicMock() + proto = MagicMock() + loop = MagicMock() + loop.run_until_complete = MagicMock(return_value=(trnsp, proto)) + + io = MagicMock() + io.ported = False + io.get_event_loop = MagicMock(return_value=loop) + + with patch('salt.utils.ssdp.asyncio', io): + srv.run() + cde_args = io.get_event_loop().create_datagram_endpoint.call_args[1] + cfg_ip_addr, cfg_port = cde_args['local_addr'] + + assert io.get_event_loop.called + assert io.get_event_loop().run_until_complete.called + assert io.get_event_loop().create_datagram_endpoint.called + assert io.get_event_loop().run_forever.called + assert trnsp.close.called + assert loop.close.called + assert srv.log.info.called + assert srv.log.info.call_args[0][0] == 'Stopping service discovery listener.' + assert 'allow_broadcast' in cde_args + assert cde_args['allow_broadcast'] + assert 'local_addr' in cde_args + assert not cfg_ip_addr == ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.LISTEN_IP] and cfg_ip_addr == '10.10.10.10' + assert not cfg_port == ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.PORT] and cfg_port == 12345 + + @skipIf(pytest is None, 'PyTest is missing') -class SSDPClientTestCase(TestCase): +class SSDPClientTestCase(TestCase, Mocks): ''' Client-related test cases ''' @@ -398,7 +423,7 @@ def test_config_passed(self): ''' config = {ssdp.SSDPBase.SIGNATURE: 'SUSE Enterprise Server', ssdp.SSDPBase.TIMEOUT: 5, ssdp.SSDPBase.PORT: 12345} - clnt = ssdp.SSDPDiscoveryClient(**config) + clnt = self.get_ssdp_discovery_client(**config) assert clnt._config[ssdp.SSDPBase.SIGNATURE] == config[ssdp.SSDPBase.SIGNATURE] assert clnt._config[ssdp.SSDPBase.PORT] == config[ssdp.SSDPBase.PORT] assert clnt._config[ssdp.SSDPBase.TIMEOUT] == config[ssdp.SSDPBase.TIMEOUT] @@ -409,7 +434,7 @@ def test_config_detached(self): :return: ''' config = {ssdp.SSDPBase.SIGNATURE: 'SUSE Enterprise Server', } - clnt = ssdp.SSDPDiscoveryClient(**config) + clnt = self.get_ssdp_discovery_client(**config) clnt._config['foo'] = 'bar' assert 'foo' in clnt._config assert 'foo' not in config @@ -476,7 +501,7 @@ def test_discover_no_masters(self): :return: ''' - clnt = ssdp.SSDPDiscoveryClient() + clnt = self.get_ssdp_discovery_client() clnt._query = MagicMock() clnt._collect_masters_map = MagicMock() clnt.log = MagicMock() diff --git a/tests/unit/utils/test_systemd.py b/tests/unit/utils/test_systemd.py index bddfee5e5469..9361e76cf774 100644 --- a/tests/unit/utils/test_systemd.py +++ b/tests/unit/utils/test_systemd.py @@ -6,8 +6,8 @@ import os # Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import Mock, patch, NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase +from tests.support.mock import Mock, patch # Import Salt libs import salt.utils.systemd as _systemd @@ -24,7 +24,6 @@ def _not_booted_effect(path): return os.stat(path) -@skipIf(NO_MOCK, NO_MOCK_REASON) class SystemdTestCase(TestCase): ''' Tests the functions in salt.utils.systemd diff --git a/tests/unit/utils/test_templates.py b/tests/unit/utils/test_templates.py new file mode 100644 index 000000000000..3d5855dd21e8 --- /dev/null +++ b/tests/unit/utils/test_templates.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +''' +Unit tests for salt.utils.templates.py +''' + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals +import sys +import logging + +# Import Salt libs +import salt.utils.templates + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf + +log = logging.getLogger(__name__) + + +### Here we go! +class RenderTestCase(TestCase): + def setUp(self): + # Default context for salt.utils.templates.render_*_tmpl to work + self.context = { + 'opts': { + 'cachedir': '/D', + '__cli': 'salt', + }, + 'saltenv': None, + } + + ### Tests for Jinja (whitespace-friendly) + def test_render_jinja_sanity(self): + tmpl = '''OK''' + res = salt.utils.templates.render_jinja_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + def test_render_jinja_evaluate(self): + tmpl = '''{{ "OK" }}''' + res = salt.utils.templates.render_jinja_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + def test_render_jinja_evaluate_multi(self): + tmpl = '''{% if 1 -%}OK{%- endif %}''' + res = salt.utils.templates.render_jinja_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + def test_render_jinja_variable(self): + tmpl = '''{{ var }}''' + + ctx = dict(self.context) + ctx['var'] = 'OK' + res = salt.utils.templates.render_jinja_tmpl(tmpl, ctx) + self.assertEqual(res, 'OK') + + ### Tests for mako template + def test_render_mako_sanity(self): + tmpl = '''OK''' + res = salt.utils.templates.render_mako_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + def test_render_mako_evaluate(self): + tmpl = '''${ "OK" }''' + res = salt.utils.templates.render_mako_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + def test_render_mako_evaluate_multi(self): + tmpl = ''' + % if 1: + OK + % endif + ''' + res = salt.utils.templates.render_mako_tmpl(tmpl, dict(self.context)) + stripped = res.strip() + self.assertEqual(stripped, 'OK') + + def test_render_mako_variable(self): + tmpl = '''${ var }''' + + ctx = dict(self.context) + ctx['var'] = 'OK' + res = salt.utils.templates.render_mako_tmpl(tmpl, ctx) + self.assertEqual(res, 'OK') + + ### Tests for wempy template + @skipIf(sys.version_info > (3,), 'The wempy module is currently unsupported under Python3') + def test_render_wempy_sanity(self): + tmpl = '''OK''' + res = salt.utils.templates.render_wempy_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + @skipIf(sys.version_info > (3,), 'The wempy module is currently unsupported under Python3') + def test_render_wempy_evaluate(self): + tmpl = '''{{="OK"}}''' + res = salt.utils.templates.render_wempy_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + @skipIf(sys.version_info > (3,), 'The wempy module is currently unsupported under Python3') + def test_render_wempy_evaluate_multi(self): + tmpl = '''{{if 1:}}OK{{pass}}''' + res = salt.utils.templates.render_wempy_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + @skipIf(sys.version_info > (3,), 'The wempy module is currently unsupported under Python3') + def test_render_wempy_variable(self): + tmpl = '''{{=var}}''' + + ctx = dict(self.context) + ctx['var'] = 'OK' + res = salt.utils.templates.render_wempy_tmpl(tmpl, ctx) + self.assertEqual(res, 'OK') + + ### Tests for genshi template (xml-based) + def test_render_genshi_sanity(self): + tmpl = '''OK''' + res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + def test_render_genshi_evaluate(self): + tmpl = '''${ "OK" }''' + res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + def test_render_genshi_evaluate_condition(self): + tmpl = '''OK''' + res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + def test_render_genshi_variable(self): + tmpl = '''$var''' + + ctx = dict(self.context) + ctx['var'] = 'OK' + res = salt.utils.templates.render_genshi_tmpl(tmpl, ctx) + self.assertEqual(res, 'OK') + + def test_render_genshi_variable_replace(self): + tmpl = '''not ok''' + + ctx = dict(self.context) + ctx['var'] = 'OK' + res = salt.utils.templates.render_genshi_tmpl(tmpl, ctx) + self.assertEqual(res, 'OK') + + ### Tests for cheetah template (line-oriented and xml-friendly) + def test_render_cheetah_sanity(self): + tmpl = '''OK''' + res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + def test_render_cheetah_evaluate(self): + tmpl = '''<%="OK"%>''' + res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context)) + self.assertEqual(res, 'OK') + + def test_render_cheetah_evaluate_xml(self): + tmpl = ''' + <% if 1: %> + OK + <% pass %> + ''' + res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context)) + stripped = res.strip() + self.assertEqual(stripped, 'OK') + + def test_render_cheetah_evaluate_text(self): + tmpl = ''' + #if 1 + OK + #end if + ''' + + res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context)) + stripped = res.strip() + self.assertEqual(stripped, 'OK') + + def test_render_cheetah_variable(self): + tmpl = '''$var''' + + ctx = dict(self.context) + ctx['var'] = 'OK' + res = salt.utils.templates.render_cheetah_tmpl(tmpl, ctx) + self.assertEqual(res.strip(), 'OK') diff --git a/tests/unit/utils/test_thin.py b/tests/unit/utils/test_thin.py index 62cae8da3df4..8e3eadec95bb 100644 --- a/tests/unit/utils/test_thin.py +++ b/tests/unit/utils/test_thin.py @@ -7,16 +7,14 @@ import os import sys from tests.support.unit import TestCase, skipIf -from tests.support.helpers import TestsLoggingHandler +from tests.support.helpers import TstSuiteLoggingHandler from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, MagicMock, patch) import salt.exceptions +import salt.utils.json from salt.utils import thin -from salt.utils import json import salt.utils.stringutils import salt.utils.platform from salt.utils.stringutils import to_bytes as bts @@ -29,7 +27,6 @@ pytest = None -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(pytest is None, 'PyTest is missing') class SSHThinTestCase(TestCase): ''' @@ -214,7 +211,7 @@ def test_gte(self): :return: ''' - assert json.loads(thin.gte()).get('foo') == 'bar' + assert salt.utils.json.loads(thin.gte()).get('foo') == 'bar' def test_add_dep_path(self): ''' @@ -246,12 +243,12 @@ def test_get_salt_call_script(self): out = thin._get_salt_call('foo', 'bar', py26=[2, 6], py27=[2, 7], py34=[3, 4]) for line in salt.utils.stringutils.to_str(out).split(os.linesep): if line.startswith('namespaces = {'): - data = json.loads(line.replace('namespaces = ', '').strip()) + data = salt.utils.json.loads(line.replace('namespaces = ', '').strip()) assert data.get('py26') == [2, 6] assert data.get('py27') == [2, 7] assert data.get('py34') == [3, 4] if line.startswith('syspaths = '): - data = json.loads(line.replace('syspaths = ', '')) + data = salt.utils.json.loads(line.replace('syspaths = ', '')) assert data == ['foo', 'bar'] def test_get_ext_namespaces_empty(self): @@ -456,7 +453,7 @@ def test_gen_thin_python_exist_or_not(self): Test thin.gen_thin function if the opposite python binary does not exist ''' - with TestsLoggingHandler() as handler: + with TstSuiteLoggingHandler() as handler: thin.gen_thin('') salt.utils.thin.subprocess.Popen.assert_not_called() diff --git a/tests/unit/utils/test_url.py b/tests/unit/utils/test_url.py index f39a00c5da60..e12adeac9369 100644 --- a/tests/unit/utils/test_url.py +++ b/tests/unit/utils/test_url.py @@ -8,16 +8,13 @@ import salt.utils.url # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) -@skipIf(NO_MOCK, NO_MOCK_REASON) class UrlTestCase(TestCase): ''' TestCase for salt.utils.url module diff --git a/tests/unit/utils/test_verify.py b/tests/unit/utils/test_verify.py index 23e1bbfdbe72..a90c4192b4bf 100644 --- a/tests/unit/utils/test_verify.py +++ b/tests/unit/utils/test_verify.py @@ -20,17 +20,15 @@ import resource # Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import skipIf, TestCase -from tests.support.paths import TMP from tests.support.helpers import ( requires_network, - TestsLoggingHandler + TstSuiteLoggingHandler ) from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON ) # Import salt libs @@ -113,7 +111,7 @@ def write(self, data): @skipIf(salt.utils.platform.is_windows(), 'No verify_env Windows') def test_verify_env(self): - root_dir = tempfile.mkdtemp(dir=TMP) + root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) var_dir = os.path.join(root_dir, 'var', 'log', 'salt') key_dir = os.path.join(root_dir, 'key_dir') verify_env([var_dir], getpass.getuser(), root_dir=root_dir) @@ -142,7 +140,7 @@ def test_verify_socket(self): pass def test_max_open_files(self): - with TestsLoggingHandler() as handler: + with TstSuiteLoggingHandler() as handler: logmsg_dbg = ( 'DEBUG:This salt-master instance has accepted {0} minion keys.' ) @@ -261,7 +259,6 @@ def test_max_open_files(self): resource.setrlimit(resource.RLIMIT_NOFILE, (mof_s, mof_h)) shutil.rmtree(tempdir) - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_verify_log(self): ''' Test that verify_log works as expected diff --git a/tests/unit/utils/test_versions.py b/tests/unit/utils/test_versions.py index 4790b921281a..79d6110b50b2 100644 --- a/tests/unit/utils/test_versions.py +++ b/tests/unit/utils/test_versions.py @@ -12,12 +12,13 @@ from __future__ import absolute_import, print_function, unicode_literals import os import sys +import datetime import warnings # Import Salt Testing libs -import tests.integration as integration from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON +from tests.support.mock import patch +from tests.support.paths import CODE_DIR # Import Salt libs import salt.modules.cmdmod @@ -105,12 +106,10 @@ def test_spelling_version_name(self): check the spelling of the version name for the release names in the salt.utils.versions.warn_until call ''' - salt_dir = integration.CODE_DIR - query = 'salt.utils.versions.warn_until' + query = 'salt.utils.versions.warn_until(' names = salt.version.SaltStackVersion.NAMES - salt_dir += '/salt/' - cmd = 'grep -lr {0} -A 1 '.format(query) + salt_dir + cmd = 'grep -lr {} -A 1 {}'.format(query, os.path.join(CODE_DIR, 'salt')) grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd).split(os.linesep) @@ -136,7 +135,6 @@ def test_spelling_version_name(self): class VersionFuncsTestCase(TestCase): - @skipIf(NO_MOCK, NO_MOCK_REASON) def test_compare(self): ret = salt.utils.versions.compare('1.0', '==', '1.0') self.assertTrue(ret) @@ -291,3 +289,87 @@ def raise_warning(**kwargs): r'0.17.0 is released. Current version is now 0.17.0. ' r'Please remove the warning.'): raise_warning(bar='baz', qux='quux', _version_info_=(0, 17)) # some kwargs + + def test_warn_until_date_warning_raised(self): + # We *always* want *all* warnings thrown on this module + warnings.filterwarnings('always', '', DeprecationWarning, __name__) + + _current_date = datetime.date(2000, 1, 1) + + # Test warning with datetime.date instance + with warnings.catch_warnings(record=True) as recorded_warnings: + salt.utils.versions.warn_until_date( + datetime.date(2000, 1, 2), + 'Deprecation Message!', + _current_date=_current_date + ) + self.assertEqual( + 'Deprecation Message!', six.text_type(recorded_warnings[0].message) + ) + + # Test warning with datetime.datetime instance + with warnings.catch_warnings(record=True) as recorded_warnings: + salt.utils.versions.warn_until_date( + datetime.datetime(2000, 1, 2), + 'Deprecation Message!', + _current_date=_current_date + ) + self.assertEqual( + 'Deprecation Message!', six.text_type(recorded_warnings[0].message) + ) + + # Test warning with date as a string + with warnings.catch_warnings(record=True) as recorded_warnings: + salt.utils.versions.warn_until_date( + '20000102', + 'Deprecation Message!', + _current_date=_current_date + ) + self.assertEqual( + 'Deprecation Message!', six.text_type(recorded_warnings[0].message) + ) + + # the deprecation warning is not issued because we passed + # _dont_call_warning + with warnings.catch_warnings(record=True) as recorded_warnings: + salt.utils.versions.warn_until_date( + '20000102', + 'Deprecation Message!', + _dont_call_warnings=True, + _current_date=_current_date + ) + self.assertEqual(0, len(recorded_warnings)) + + # Let's test for RuntimeError raise + with self.assertRaisesRegex( + RuntimeError, + r'Deprecation Message! This warning\(now exception\) triggered on ' + r'filename \'(.*)test_versions.py\', line number ([\d]+), is ' + r'supposed to be shown until ([\d-]+). Today is ([\d-]+). ' + r'Please remove the warning.'): + salt.utils.versions.warn_until_date('20000101', 'Deprecation Message!') + + # Even though we're calling warn_until_date, we pass _dont_call_warnings + # because we're only after the RuntimeError + with self.assertRaisesRegex( + RuntimeError, + r'Deprecation Message! This warning\(now exception\) triggered on ' + r'filename \'(.*)test_versions.py\', line number ([\d]+), is ' + r'supposed to be shown until ([\d-]+). Today is ([\d-]+). ' + r'Please remove the warning.'): + salt.utils.versions.warn_until_date( + '20000101', + 'Deprecation Message!', + _dont_call_warnings=True, + _current_date=_current_date + ) + + def test_warn_until_date_bad_strptime_format(self): + # We *always* want *all* warnings thrown on this module + warnings.filterwarnings('always', '', DeprecationWarning, __name__) + + # Let's test for RuntimeError raise + with self.assertRaisesRegex( + ValueError, + 'time data \'0022\' does not match format \'%Y%m%d\''): + salt.utils.versions.warn_until_date('0022', 'Deprecation Message!') diff --git a/tests/unit/utils/test_vmware.py b/tests/unit/utils/test_vmware.py index 7ba69c37115e..d0a225d49704 100644 --- a/tests/unit/utils/test_vmware.py +++ b/tests/unit/utils/test_vmware.py @@ -13,11 +13,10 @@ import sys # Import Salt testing libraries +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase, skipIf from tests.support.mixins import LoaderModuleMockMixin from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON, patch, MagicMock, PropertyMock, @@ -64,7 +63,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetClusterTestCase(TestCase): ''' @@ -253,7 +251,6 @@ def test_create_cluster_raise_runtime_fault(self): self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class UpdateClusterTestCase(TestCase): ''' @@ -329,7 +326,6 @@ def test_wait_for_task_call(self): self.mock_task, 'fake_cluster', 'ClusterUpdateTask') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class WaitForTaskTestCase(TestCase): ''' @@ -572,7 +568,6 @@ def test_info_error_invalid_argument_with_fault_message(self): 'InvalidArgumentFault msg (LocalFault msg)') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetMorsWithPropertiesTestCase(TestCase): ''' @@ -782,7 +777,6 @@ def test_one_elem_multiple_properties(self): 'object': inner_obj_mock}) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetPropertiesOfManagedObjectTestCase(TestCase): ''' @@ -854,7 +848,6 @@ def test_no_items_named_object(self): 'retrieved', excinfo.exception.strerror) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetManagedObjectName(TestCase): ''' @@ -891,7 +884,6 @@ def test_return_managed_object_name(self): self.assertEqual(ret, 'fake_name') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetContentTestCase(TestCase): ''' @@ -1173,7 +1165,6 @@ def test_local_properties_set(self): obj=container_ref_mock, skip=False, selectSet=None) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetRootFolderTestCase(TestCase): ''' @@ -1217,7 +1208,6 @@ def test_return(self): self.assertEqual(ret, self.mock_root_folder) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetServiceInfoTestCase(TestCase): ''' @@ -1267,7 +1257,6 @@ def test_about_raises_runtime_fault(self): self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') class GssapiTokenTest(TestCase): @@ -1345,7 +1334,6 @@ def test_context_extablished(self): excinfo.exception.strerror) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class PrivateGetServiceInstanceTestCase(TestCase): ''' @@ -1795,7 +1783,6 @@ def test_third_attempt_unsuccessful_connection_vim_fault(self): self.assertIn('VimFault', excinfo.Exception.message) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetServiceInstanceTestCase(TestCase): ''' @@ -1970,7 +1957,6 @@ def test_current_time_raise_runtime_fault(self): self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class DisconnectTestCase(TestCase): ''' @@ -2018,7 +2004,6 @@ def test_disconnect_raise_runtime_fault(self): self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class IsConnectionToAVCenterTestCase(TestCase): ''' @@ -2078,7 +2063,6 @@ def test_connected_to_invalid_entity(self): excinfo.exception.strerror) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetNewServiceInstanceStub(TestCase, LoaderModuleMockMixin): ''' @@ -2170,7 +2154,6 @@ def test_new_stub_returned(self): self.assertEqual(ret, self.mock_new_stub) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetServiceInstanceFromManagedObjectTestCase(TestCase): ''' @@ -2226,7 +2209,6 @@ def test_si_return_and_stub_assignment(self): self.assertEqual(ret._stub, self.mock_stub) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetDatacentersTestCase(TestCase): ''' @@ -2294,7 +2276,6 @@ def test_get_all_datastores(self): self.assertEqual(res, [self.mock_dc1, self.mock_dc2]) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetDatacenterTestCase(TestCase): ''' @@ -2335,7 +2316,6 @@ def test_get_datacenter_return(self): self.assertEqual(res, self.mock_dc) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class CreateDatacenterTestCase(TestCase): ''' @@ -2414,7 +2394,6 @@ class FakeTaskClass(object): pass -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetDvssTestCase(TestCase): def setUp(self): @@ -2489,7 +2468,6 @@ def test_filtered_all_dvss(self): self.mock_items[2]['object']]) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetNetworkFolderTestCase(TestCase): def setUp(self): @@ -2556,7 +2534,6 @@ def test_get_network_folder(self): self.assertEqual(ret, self.mock_entries[0]['object']) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class CreateDvsTestCase(TestCase): def setUp(self): @@ -2655,10 +2632,11 @@ def test_wait_for_tasks(self): dvs_create_spec=self.mock_dvs_create_spec) self.mock_wait_for_task.assert_called_once_with( self.mock_task, 'fake_dvs', - '') + ''.format( + 'tests.' if RUNTIME_VARS.PYTEST_SESSION else '') + ) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class UpdateDvsTestCase(TestCase): def setUp(self): @@ -2724,10 +2702,11 @@ def test_wait_for_tasks(self): salt.utils.vmware.update_dvs(self.mock_dvs_ref, self.mock_dvs_spec) self.mock_wait_for_task.assert_called_once_with( self.mock_task, 'fake_dvs', - '') + ''.format( + 'tests.' if RUNTIME_VARS.PYTEST_SESSION else '') + ) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class SetDvsNetworkResourceManagementEnabledTestCase(TestCase): def setUp(self): @@ -2793,7 +2772,6 @@ def test_enable_network_resource_management_raises_runtime_fault(self): self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetDvportgroupsTestCase(TestCase): def setUp(self): @@ -2887,7 +2865,6 @@ def test_filtered_pgs(self): self.mock_items[2]['object']]) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetUplinkDvportgroupTestCase(TestCase): def setUp(self): @@ -2956,7 +2933,6 @@ def test_get_uplink_pg(self): self.assertEqual(ret, self.mock_items[1]['object']) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class CreateDvportgroupTestCase(TestCase): def setUp(self): @@ -3023,10 +2999,11 @@ def test_wait_for_tasks(self): salt.utils.vmware.create_dvportgroup(self.mock_dvs_ref, self.mock_pg_spec) self.mock_wait_for_task.assert_called_once_with( self.mock_task, 'fake_dvs', - '') + ''.format( + 'tests.' if RUNTIME_VARS.PYTEST_SESSION else '') + ) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class UpdateDvportgroupTestCase(TestCase): def setUp(self): @@ -3096,10 +3073,11 @@ def test_wait_for_tasks(self): salt.utils.vmware.update_dvportgroup(self.mock_pg_ref, self.mock_pg_spec) self.mock_wait_for_task.assert_called_once_with( self.mock_task, 'fake_pg', - '') + ''.format( + 'tests.' if RUNTIME_VARS.PYTEST_SESSION else '') + ) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class RemoveDvportgroupTestCase(TestCase): def setUp(self): @@ -3163,10 +3141,11 @@ def test_wait_for_tasks(self): salt.utils.vmware.remove_dvportgroup(self.mock_pg_ref) self.mock_wait_for_task.assert_called_once_with( self.mock_task, 'fake_pg', - '') + ''.format( + 'tests.' if RUNTIME_VARS.PYTEST_SESSION else '') + ) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetHostsTestCase(TestCase): ''' @@ -3301,7 +3280,6 @@ def test_one_host_returned(self): self.assertEqual(res, [self.mock_host1]) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetLicenseManagerTestCase(TestCase): ''' @@ -3352,7 +3330,6 @@ def test_valid_assignment_manager(self): self.assertEqual(ret, self.mock_lic_mgr) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetLicenseAssignmentManagerTestCase(TestCase): ''' @@ -3411,7 +3388,6 @@ def test_valid_assignment_manager(self): self.assertEqual(ret, self.mock_lic_assign_mgr) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetLicensesTestCase(TestCase): ''' @@ -3486,7 +3462,6 @@ def test_valid_licenses(self): self.assertEqual(ret, self.mock_licenses) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class AddLicenseTestCase(TestCase): ''' @@ -3587,7 +3562,6 @@ def test_valid_license_added(self): self.assertEqual(ret, self.mock_license) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetAssignedLicensesTestCase(TestCase): ''' @@ -3644,7 +3618,7 @@ def test_license_assignment_manager_passed_in(self): def test_entity_name(self): mock_trace = MagicMock() - with patch('salt.log.setup.SaltLoggingClass.trace', mock_trace): + with patch('salt._logging.impl.SaltLoggingClass.trace', mock_trace): salt.utils.vmware.get_assigned_licenses(self.mock_si, self.mock_entity_ref, 'fake_entity_name') @@ -3766,7 +3740,6 @@ def test_valid_assignments(self): self.assertEqual(ret, self.mock_assignments) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class AssignLicenseTestCase(TestCase): ''' @@ -3820,7 +3793,7 @@ def test_license_assignment_manager_passed_in(self): def test_entity_name(self): mock_trace = MagicMock() - with patch('salt.log.setup.SaltLoggingClass.trace', mock_trace): + with patch('salt._logging.impl.SaltLoggingClass.trace', mock_trace): salt.utils.vmware.assign_license(self.mock_si, self.mock_lic_key, 'fake_license_name', @@ -3945,7 +3918,6 @@ def test_valid_assignments(self): self.assertEqual(ret, self.mock_license) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetStorageSystemTestCase(TestCase): ''' @@ -4030,7 +4002,6 @@ def test_valid_mors_result(self): self.assertEqual(res, self.mock_obj) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class GetDatastoresTestCase(TestCase): ''' @@ -4255,7 +4226,6 @@ def test_get_datastores_filtered_by_both_name_and_backing_disk(self): self.mock_entries[2]['object']]) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') class RenameDatastoreTestCase(TestCase): ''' @@ -4319,7 +4289,6 @@ def test_rename_datastore(self): 'fake_new_name') -@skipIf(NO_MOCK, NO_MOCK_REASON) class ConvertToKbTestCase(TestCase): ''' Tests for converting units @@ -4341,7 +4310,6 @@ def test_conversion_bad_input_argument_fault(self): self.assertRaises(ArgumentValueError, salt.utils.vmware.convert_to_kb, 'test', 10) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @patch('salt.utils.vmware.get_managed_object_name', MagicMock()) @patch('salt.utils.vmware.wait_for_task', MagicMock()) @@ -4413,7 +4381,6 @@ def test_create_vm_wait_for_task(self): self.mock_task, self.vm_name, 'CreateVM Task', 10, 'info') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @patch('salt.utils.vmware.get_managed_object_name', MagicMock()) @patch('salt.utils.vmware.wait_for_task', MagicMock()) @@ -4485,7 +4452,6 @@ def test_register_vm_wait_for_task(self): self.mock_task, self.vm_name, 'RegisterVM Task') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @patch('salt.utils.vmware.get_managed_object_name', MagicMock()) @patch('salt.utils.vmware.wait_for_task', MagicMock()) @@ -4530,7 +4496,6 @@ def test_update_vm_wait_for_task(self): self.mock_task, 'my_vm', 'ReconfigureVM Task') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @patch('salt.utils.vmware.get_managed_object_name', MagicMock()) @patch('salt.utils.vmware.wait_for_task', MagicMock()) @@ -4574,7 +4539,6 @@ def test_destroy_vm_wait_for_task(self): self.mock_task, 'my_vm', 'Destroy Task') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @patch('salt.utils.vmware.get_managed_object_name', MagicMock()) class UnregisterVirtualMachineTestCase(TestCase): diff --git a/tests/unit/utils/test_vsan.py b/tests/unit/utils/test_vsan.py index 3ce6f63c4022..403a2861e9c2 100644 --- a/tests/unit/utils/test_vsan.py +++ b/tests/unit/utils/test_vsan.py @@ -12,7 +12,7 @@ # Import Salt testing libraries from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock, \ +from tests.support.mock import patch, MagicMock, \ PropertyMock # Import Salt libraries @@ -32,7 +32,6 @@ log = logging.getLogger(__name__) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') class VsanSupportedTestCase(TestCase): @@ -80,7 +79,6 @@ def test_api_version_raises_runtime_fault(self): self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') class GetVsanClusterConfigSystemTestCase(TestCase, LoaderModuleMockMixin): @@ -138,7 +136,6 @@ def test_return(self): self.assertEqual(ret, self.mock_ret) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_PYVSAN, 'The \'pyvsan\' bindings are missing') class GetVsanDiskManagementSystemTestCase(TestCase, LoaderModuleMockMixin): @@ -196,7 +193,6 @@ def test_return(self): self.assertEqual(ret, self.mock_ret) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') class GetHostVsanSystemTestCase(TestCase): @@ -279,7 +275,6 @@ def test_valid_mors_result(self): self.assertEqual(res, self.mock_vsan_system) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') class CreateDiskgroupTestCase(TestCase): @@ -408,7 +403,6 @@ def test_result(self): self.assertTrue(res) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') class AddCapacityToDiskGroupTestCase(TestCase): @@ -547,7 +541,6 @@ def test_result(self): self.assertTrue(res) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') class RemoveCapacityFromDiskGroup(TestCase): @@ -664,7 +657,6 @@ def test_result(self): self.assertTrue(res) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') class RemoveDiskgroup(TestCase): @@ -783,7 +775,6 @@ def test_result(self): self.assertTrue(res) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') class GetClusterVsanInfoTestCase(TestCase, LoaderModuleMockMixin): @@ -864,7 +855,6 @@ def test_VsanClusterGetConfig_raises_runtime_fault(self): self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') class ReconfigureClusterVsanTestCase(TestCase): @@ -970,7 +960,6 @@ def test__wait_for_tasks_call(self): self.mock_si) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') @skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') class _WaitForTasks(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/utils/test_win_dacl.py b/tests/unit/utils/test_win_dacl.py index 9caf2163957d..49698217f997 100644 --- a/tests/unit/utils/test_win_dacl.py +++ b/tests/unit/utils/test_win_dacl.py @@ -8,7 +8,6 @@ # Import Salt Testing Libs from tests.support.helpers import destructiveTest, generate_random_name, patch from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import NO_MOCK, NO_MOCK_REASON from tests.support.unit import TestCase, skipIf # Import Salt Libs @@ -26,7 +25,6 @@ FAKE_KEY = 'SOFTWARE\\{0}'.format(generate_random_name('SaltTesting-')) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_WIN32, 'Requires pywin32') @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') class WinDaclTestCase(TestCase): @@ -83,7 +81,6 @@ def test_get_name(self): self.assertEqual(win_dacl.get_name(sid_obj), 'Administrators') -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_WIN32, 'Requires pywin32') @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') class WinDaclRegTestCase(TestCase, LoaderModuleMockMixin): @@ -383,7 +380,6 @@ def test_set_perms(self): self.assertDictEqual(result, expected) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not HAS_WIN32, 'Requires pywin32') @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') class WinDaclFileTestCase(TestCase, LoaderModuleMockMixin): diff --git a/tests/unit/utils/test_win_functions.py b/tests/unit/utils/test_win_functions.py index 370994c479f0..2cad56966e26 100644 --- a/tests/unit/utils/test_win_functions.py +++ b/tests/unit/utils/test_win_functions.py @@ -5,17 +5,24 @@ # Import Salt Testing Libs from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON -) +from tests.support.mock import patch, MagicMock # Import Salt Libs import salt.utils.platform import salt.utils.win_functions as win_functions +# Import 3rd Party Libs +try: + import win32net + HAS_WIN32 = True + + class WinError(win32net.error): + winerror = 0 + +except ImportError: + HAS_WIN32 = False + -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinFunctionsTestCase(TestCase): ''' Test cases for salt.utils.win_functions @@ -53,9 +60,58 @@ def test_escape_argument_path_with_space(self): self.assertEqual(encoded, '^"C:\\Some Path\\With Spaces^"') - @skipIf(not salt.utils.platform.is_windows(), 'WinDLL only available on Windows') + @skipIf(not salt.utils.platform.is_windows(), + 'WinDLL only available on Windows') def test_broadcast_setting_change(self): ''' Test to rehash the Environment variables ''' self.assertTrue(win_functions.broadcast_setting_change()) + + @skipIf(not salt.utils.platform.is_windows(), + 'WinDLL only available on Windows') + def test_get_user_groups(self): + groups = ['Administrators', 'Users'] + with patch('win32net.NetUserGetLocalGroups', return_value=groups): + ret = win_functions.get_user_groups('Administrator') + self.assertListEqual(groups, ret) + + @skipIf(not salt.utils.platform.is_windows(), + 'WinDLL only available on Windows') + def test_get_user_groups_sid(self): + groups = ['Administrators', 'Users'] + expected = ['S-1-5-32-544', 'S-1-5-32-545'] + with patch('win32net.NetUserGetLocalGroups', return_value=groups): + ret = win_functions.get_user_groups('Administrator', sid=True) + self.assertListEqual(expected, ret) + + @skipIf(not salt.utils.platform.is_windows(), + 'WinDLL only available on Windows') + def test_get_user_groups_system(self): + groups = ['SYSTEM'] + with patch('win32net.NetUserGetLocalGroups', return_value=groups): + ret = win_functions.get_user_groups('SYSTEM') + self.assertListEqual(groups, ret) + + @skipIf(not salt.utils.platform.is_windows(), + 'WinDLL only available on Windows') + @skipIf(not HAS_WIN32, 'Requires pywin32 libraries') + def test_get_user_groups_missing_permission(self): + groups = ['Administrators', 'Users'] + win_error = WinError() + win_error.winerror = 5 + effect = [win_error, groups] + with patch('win32net.NetUserGetLocalGroups', side_effect=effect): + ret = win_functions.get_user_groups('Administrator') + self.assertListEqual(groups, ret) + + @skipIf(not salt.utils.platform.is_windows(), + 'WinDLL only available on Windows') + @skipIf(not HAS_WIN32, 'Requires pywin32 libraries') + def test_get_user_groups_error(self): + win_error = WinError() + win_error.winerror = 1927 + mock_error = MagicMock(side_effect=win_error) + with patch('win32net.NetUserGetLocalGroups', side_effect=mock_error): + with self.assertRaises(WinError): + win_functions.get_user_groups('Administrator') diff --git a/tests/unit/utils/test_win_lgpo_auditpol.py b/tests/unit/utils/test_win_lgpo_auditpol.py index b7b288a64ca6..6c627fa6d34d 100644 --- a/tests/unit/utils/test_win_lgpo_auditpol.py +++ b/tests/unit/utils/test_win_lgpo_auditpol.py @@ -6,7 +6,7 @@ # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock +from tests.support.mock import patch, MagicMock from tests.support.unit import TestCase, skipIf # Import Salt Libs @@ -17,7 +17,6 @@ settings = ['No Auditing', 'Success', 'Failure', 'Success and Failure'] -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') class WinLgpoAuditpolTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): diff --git a/tests/unit/utils/test_win_lgpo_netsh.py b/tests/unit/utils/test_win_lgpo_netsh.py index f268598d142b..fc0b1e8e44f4 100644 --- a/tests/unit/utils/test_win_lgpo_netsh.py +++ b/tests/unit/utils/test_win_lgpo_netsh.py @@ -5,7 +5,6 @@ # Import Salt Testing Libs from tests.support.helpers import destructiveTest -from tests.support.mock import NO_MOCK, NO_MOCK_REASON from tests.support.unit import TestCase, skipIf # Import Salt Libs @@ -14,7 +13,6 @@ from salt.exceptions import CommandExecutionError -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') class WinLgpoNetshTestCase(TestCase): def test_get_settings_firewallpolicy_local(self): diff --git a/tests/unit/utils/test_win_osinfo.py b/tests/unit/utils/test_win_osinfo.py index 7f7f5ca944a3..82c459b4b4ce 100644 --- a/tests/unit/utils/test_win_osinfo.py +++ b/tests/unit/utils/test_win_osinfo.py @@ -11,10 +11,6 @@ # Import Salt Testing Libs from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - NO_MOCK, - NO_MOCK_REASON -) # Import Salt Libs import salt.utils.win_osinfo as win_osinfo @@ -22,7 +18,6 @@ @skipIf(not salt.utils.platform.is_windows(), 'Requires Windows') -@skipIf(NO_MOCK, NO_MOCK_REASON) class WinOsInfo(TestCase): ''' Test cases for salt/utils/win_osinfo.py diff --git a/tests/unit/utils/test_win_pdh.py b/tests/unit/utils/test_win_pdh.py index b64d7c67f53b..955edca84b82 100644 --- a/tests/unit/utils/test_win_pdh.py +++ b/tests/unit/utils/test_win_pdh.py @@ -4,7 +4,6 @@ from __future__ import absolute_import, unicode_literals, print_function # Import Salt Testing Libs -from tests.support.mock import NO_MOCK, NO_MOCK_REASON from tests.support.unit import TestCase, skipIf # Import Salt Libs @@ -12,7 +11,6 @@ import salt.utils.win_pdh as win_pdh -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') class WinPdhTestCase(TestCase): def test_list_objects(self): diff --git a/tests/unit/utils/test_win_reg.py b/tests/unit/utils/test_win_reg.py index 2d3961c54d23..7af97b5f37e7 100644 --- a/tests/unit/utils/test_win_reg.py +++ b/tests/unit/utils/test_win_reg.py @@ -5,7 +5,7 @@ # Import Salt Testing Libs from tests.support.helpers import destructiveTest, generate_random_name -from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch +from tests.support.mock import patch from tests.support.unit import TestCase, skipIf # Import Salt Libs @@ -18,7 +18,6 @@ FAKE_KEY = 'SOFTWARE\\{0}'.format(generate_random_name('SaltTesting-')) -@skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') class WinFunctionsTestCase(TestCase): ''' diff --git a/tests/unit/utils/test_yamldumper.py b/tests/unit/utils/test_yamldumper.py index 7056d90fec20..7168f48eccc3 100644 --- a/tests/unit/utils/test_yamldumper.py +++ b/tests/unit/utils/test_yamldumper.py @@ -11,11 +11,9 @@ import salt.utils.yamldumper # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase -@skipIf(NO_MOCK, NO_MOCK_REASON) class YamlDumperTestCase(TestCase): ''' TestCase for salt.utils.yamldumper module diff --git a/tests/unit/utils/test_yamlloader.py b/tests/unit/utils/test_yamlloader.py index 4222b30260eb..65d3850d5845 100644 --- a/tests/unit/utils/test_yamlloader.py +++ b/tests/unit/utils/test_yamlloader.py @@ -15,14 +15,13 @@ from salt.ext import six # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON, mock_open +from tests.support.unit import TestCase +from tests.support.mock import patch, mock_open # Import 3rd-party libs from salt.ext import six -@skipIf(NO_MOCK, NO_MOCK_REASON) class YamlLoaderTestCase(TestCase): ''' TestCase for salt.utils.yamlloader module diff --git a/tests/unit/utils/test_zeromq.py b/tests/unit/utils/test_zeromq.py index 14239bd3f6c7..28745b850949 100644 --- a/tests/unit/utils/test_zeromq.py +++ b/tests/unit/utils/test_zeromq.py @@ -12,8 +12,6 @@ from tests.support.unit import TestCase, skipIf from tests.support.mock import ( patch, - NO_MOCK, - NO_MOCK_REASON ) # Import salt libs @@ -33,7 +31,6 @@ def test_ip_bracket(self): ip_addr_obj = ipaddress.ip_address(test_ipv4) self.assertEqual(test_ipv4, salt.utils.zeromq.ip_bracket(ip_addr_obj)) - @skipIf(NO_MOCK, NO_MOCK_REASON) @skipIf(not hasattr(zmq, 'IPC_PATH_MAX_LEN'), "ZMQ does not have max length support.") def test_check_ipc_length(self): ''' diff --git a/tests/unit/utils/test_zfs.py b/tests/unit/utils/test_zfs.py index 29b3fe893f49..d94b06815b7d 100644 --- a/tests/unit/utils/test_zfs.py +++ b/tests/unit/utils/test_zfs.py @@ -14,883 +14,36 @@ from __future__ import absolute_import, unicode_literals, print_function # Import Salt Testing libs -from tests.support.unit import skipIf, TestCase +from tests.support.zfs import ZFSMockData +from tests.support.unit import TestCase from tests.support.mock import ( MagicMock, patch, - NO_MOCK, - NO_MOCK_REASON, ) # Import Salt Execution module to test import salt.utils.zfs as zfs # Import Salt Utils -import salt.loader from salt.utils.odict import OrderedDict -# property_map mocks -pmap_exec_zpool = { - 'retcode': 2, - 'stdout': '', - 'stderr': "\n".join([ - 'missing property argument', - 'usage:', - ' get [-Hp] [-o "all" | field[,...]] <"all" | property[,...]> ...', - '', - 'the following properties are supported:', - '', - ' PROPERTY EDIT VALUES', - '', - ' allocated NO ', - ' capacity NO ', - ' dedupratio NO <1.00x or higher if deduped>', - ' expandsize NO ', - ' fragmentation NO ', - ' free NO ', - ' freeing NO ', - ' guid NO ', - ' health NO ', - ' leaked NO ', - ' size NO ', - ' altroot YES ', - ' autoexpand YES on | off', - ' autoreplace YES on | off', - ' bootfs YES ', - ' bootsize YES ', - ' cachefile YES | none', - ' comment YES ', - ' dedupditto YES ', - ' delegation YES on | off', - ' failmode YES wait | continue | panic', - ' listsnapshots YES on | off', - ' readonly YES on | off', - ' version YES ', - ' feature@... YES disabled | enabled | active', - '', - 'The feature@ properties must be appended with a feature name.', - 'See zpool-features(5). ', - ]), -} -pmap_zpool = { - 'comment': { - 'edit': True, - 'type': 'str', - 'values': '' - }, - 'freeing': { - 'edit': False, - 'type': 'size', - 'values': '' - }, - 'listsnapshots': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'leaked': { - 'edit': False, - 'type': 'size', - 'values': '' - }, - 'version': { - 'edit': True, - 'type': 'numeric', - 'values': '' - }, - 'write': { - 'edit': False, - 'type': 'size', - 'values': '' - }, - 'replace': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'delegation': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'dedupditto': { - 'edit': True, - 'type': 'str', - 'values': '' - }, - 'autoexpand': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'alloc': { - 'edit': False, - 'type': 'size', - 'values': '' - }, - 'allocated': { - 'edit': False, - 'type': 'size', - 'values': '' - }, - 'guid': { - 'edit': False, - 'type': 'numeric', - 'values': '' - }, - 'size': { - 'edit': False, - 'type': 'size', - 'values': '' - }, - 'cap': { - 'edit': False, - 'type': 'numeric', - 'values': '' - }, - 'capacity': { - 'edit': False, - 'type': 'size', - 'values': '' - }, - "capacity-alloc": { - "edit": False, - "type": "size", - "values": "" - }, - "capacity-free": { - "edit": False, - "type": "size", - "values": "" - }, - 'cachefile': { - 'edit': True, - 'type': 'str', - 'values': ' | none' - }, - "cksum": { - "edit": False, - "type": "numeric", - "values": "" - }, - 'bootfs': { - 'edit': True, - 'type': 'str', - 'values': '' - }, - 'autoreplace': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - "bandwith-read": { - "edit": False, - "type": "size", - "values": "" - }, - "bandwith-write": { - "edit": False, - "type": "size", - "values": "" - }, - "operations-read": { - "edit": False, - "type": "size", - "values": "" - }, - "operations-write": { - "edit": False, - "type": "size", - "values": "" - }, - "read": { - "edit": False, - "type": "size", - "values": "" - }, - 'readonly': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'dedupratio': { - 'edit': False, - 'type': 'str', - 'values': '<1.00x or higher if deduped>' - }, - 'health': { - 'edit': False, - 'type': 'str', - 'values': '' - }, - 'feature@': { - 'edit': True, - 'type': 'str', - 'values': 'disabled | enabled | active' - }, - 'expandsize': { - 'edit': False, - 'type': 'size', - 'values': '' - }, - 'listsnaps': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'bootsize': { - 'edit': True, - 'type': 'size', - 'values': '' - }, - 'free': { - 'edit': False, - 'type': 'size', - 'values': '' - }, - 'failmode': { - 'edit': True, - 'type': 'str', - 'values': 'wait | continue | panic' - }, - 'altroot': { - 'edit': True, - 'type': 'str', - 'values': '' - }, - 'expand': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'frag': { - 'edit': False, - 'type': 'str', - 'values': '' - }, - 'fragmentation': { - 'edit': False, - 'type': 'str', - 'values': '' - } -} -pmap_exec_zfs = { - 'retcode': 2, - 'stdout': '', - 'stderr': "\n".join([ - 'missing property argument', - 'usage:', - ' get [-crHp] [-d max] [-o "all" | field[,...]]', - ' [-t type[,...]] [-s source[,...]]', - ' <"all" | property[,...]> [filesystem|volume|snapshot|bookmark] ...', - '', - 'The following properties are supported:', - '', - ' PROPERTY EDIT INHERIT VALUES', - '', - ' available NO NO ', - ' clones NO NO [,...]', - ' compressratio NO NO <1.00x or higher if compressed>', - ' creation NO NO ', - ' defer_destroy NO NO yes | no', - ' filesystem_count NO NO ', - ' logicalreferenced NO NO ', - ' logicalused NO NO ', - ' mounted NO NO yes | no', - ' origin NO NO ', - ' receive_resume_token NO NO ', - ' refcompressratio NO NO <1.00x or higher if compressed>', - ' referenced NO NO ', - ' snapshot_count NO NO ', - ' type NO NO filesystem | volume | snapshot | bookmark', - ' used NO NO ', - ' usedbychildren NO NO ', - ' usedbydataset NO NO ', - ' usedbyrefreservation NO NO ', - ' usedbysnapshots NO NO ', - ' userrefs NO NO ', - ' written NO NO ', - ' aclinherit YES YES discard | noallow | restricted | passthrough | passthrough-x', - ' aclmode YES YES discard | groupmask | passthrough | restricted', - ' atime YES YES on | off', - ' canmount YES NO on | off | noauto', - ' casesensitivity NO YES sensitive | insensitive | mixed', - ' checksum YES YES on | off | fletcher2 | fletcher4 | sha256 | sha512 | skein | edonr', - ' compression YES YES on | off | lzjb | gzip | gzip-[1-9] | zle | lz4', - ' copies YES YES 1 | 2 | 3', - ' dedup YES YES on | off | verify | sha256[,verify], sha512[,verify], skein[,verify], edonr,verify', - ' devices YES YES on | off', - ' exec YES YES on | off', - ' filesystem_limit YES NO | none', - ' logbias YES YES latency | throughput', - ' mlslabel YES YES ', - ' mountpoint YES YES | legacy | none', - ' nbmand YES YES on | off', - ' normalization NO YES none | formC | formD | formKC | formKD', - ' primarycache YES YES all | none | metadata', - ' quota YES NO | none', - ' readonly YES YES on | off', - ' recordsize YES YES 512 to 1M, power of 2', - ' redundant_metadata YES YES all | most', - ' refquota YES NO | none', - ' refreservation YES NO | none', - ' reservation YES NO | none', - ' secondarycache YES YES all | none | metadata', - ' setuid YES YES on | off', - ' sharenfs YES YES on | off | share(1M) options', - ' sharesmb YES YES on | off | sharemgr(1M) options', - ' snapdir YES YES hidden | visible', - ' snapshot_limit YES NO | none', - ' sync YES YES standard | always | disabled', - ' utf8only NO YES on | off', - ' version YES NO 1 | 2 | 3 | 4 | 5 | current', - ' volblocksize NO YES 512 to 128k, power of 2', - ' volsize YES NO ', - ' vscan YES YES on | off', - ' xattr YES YES on | off', - ' zoned YES YES on | off', - ' userused@... NO NO ', - ' groupused@... NO NO ', - ' userquota@... YES NO | none', - ' groupquota@... YES NO | none', - ' written@ NO NO ', - '', - 'Sizes are specified in bytes with standard units such as K, M, G, etc.', - '', - 'User-defined properties can be specified by using a name containing a colon (:).', - '', - 'The {user|group}{used|quota}@ properties must be appended with', - 'a user or group specifier of one of these forms:', - ' POSIX name (eg: "matt")', - ' POSIX id (eg: "126829")', - ' SMB name@domain (eg: "matt@sun")', - ' SMB SID (eg: "S-1-234-567-89")', - ]), -} -pmap_zfs = { - "origin": { - "edit": False, - "inherit": False, - "values": "", - "type": "str" - }, - "setuid": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "referenced": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "vscan": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "logicalused": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "userrefs": { - "edit": False, - "inherit": False, - "values": "", - "type": "numeric" - }, - "primarycache": { - "edit": True, - "inherit": True, - "values": "all | none | metadata", - "type": "str" - }, - "logbias": { - "edit": True, - "inherit": True, - "values": "latency | throughput", - "type": "str" - }, - "creation": { - "edit": False, - "inherit": False, - "values": "", - "type": "str" - }, - "sync": { - "edit": True, - "inherit": True, - "values": "standard | always | disabled", - "type": "str" - }, - "dedup": { - "edit": True, - "inherit": True, - "values": "on | off | verify | sha256[,verify], sha512[,verify], skein[,verify], edonr,verify", - "type": "bool" - }, - "sharenfs": { - "edit": True, - "inherit": True, - "values": "on | off | share(1m) options", - "type": "bool" - }, - "receive_resume_token": { - "edit": False, - "inherit": False, - "values": "", - "type": "str" - }, - "usedbyrefreservation": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "sharesmb": { - "edit": True, - "inherit": True, - "values": "on | off | sharemgr(1m) options", - "type": "bool" - }, - "rdonly": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "reservation": { - "edit": True, - "inherit": False, - "values": " | none", - "type": "size" - }, - "reserv": { - "edit": True, - "inherit": False, - "values": " | none", - "type": "size" - }, - "mountpoint": { - "edit": True, - "inherit": True, - "values": " | legacy | none", - "type": "str" - }, - "casesensitivity": { - "edit": False, - "inherit": True, - "values": "sensitive | insensitive | mixed", - "type": "str" - }, - "utf8only": { - "edit": False, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "usedbysnapshots": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "readonly": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "written@": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "avail": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "recsize": { - "edit": True, - "inherit": True, - "values": "512 to 1m, power of 2", - "type": "str" - }, - "atime": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "compression": { - "edit": True, - "inherit": True, - "values": "on | off | lzjb | gzip | gzip-[1-9] | zle | lz4", - "type": "bool" - }, - "snapdir": { - "edit": True, - "inherit": True, - "values": "hidden | visible", - "type": "str" - }, - "aclmode": { - "edit": True, - "inherit": True, - "values": "discard | groupmask | passthrough | restricted", - "type": "str" - }, - "zoned": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "copies": { - "edit": True, - "inherit": True, - "values": "1 | 2 | 3", - "type": "numeric" - }, - "snapshot_limit": { - "edit": True, - "inherit": False, - "values": " | none", - "type": "numeric" - }, - "aclinherit": { - "edit": True, - "inherit": True, - "values": "discard | noallow | restricted | passthrough | passthrough-x", - "type": "str" - }, - "compressratio": { - "edit": False, - "inherit": False, - "values": "<1.00x or higher if compressed>", - "type": "str" - }, - "xattr": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "written": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "version": { - "edit": True, - "inherit": False, - "values": "1 | 2 | 3 | 4 | 5 | current", - "type": "numeric" - }, - "recordsize": { - "edit": True, - "inherit": True, - "values": "512 to 1m, power of 2", - "type": "str" - }, - "refquota": { - "edit": True, - "inherit": False, - "values": " | none", - "type": "size" - }, - "filesystem_limit": { - "edit": True, - "inherit": False, - "values": " | none", - "type": "numeric" - }, - "lrefer.": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "type": { - "edit": False, - "inherit": False, - "values": "filesystem | volume | snapshot | bookmark", - "type": "str" - }, - "secondarycache": { - "edit": True, - "inherit": True, - "values": "all | none | metadata", - "type": "str" - }, - "refer": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "available": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "used": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "exec": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "compress": { - "edit": True, - "inherit": True, - "values": "on | off | lzjb | gzip | gzip-[1-9] | zle | lz4", - "type": "bool" - }, - "volblock": { - "edit": False, - "inherit": True, - "values": "512 to 128k, power of 2", - "type": "str" - }, - "refcompressratio": { - "edit": False, - "inherit": False, - "values": "<1.00x or higher if compressed>", - "type": "str" - }, - "quota": { - "edit": True, - "inherit": False, - "values": " | none", - "type": "size" - }, - "groupquota@": { - "edit": True, - "inherit": False, - "values": " | none", - "type": "size" - }, - "userquota@": { - "edit": True, - "inherit": False, - "values": " | none", - "type": "size" - }, - "snapshot_count": { - "edit": False, - "inherit": False, - "values": "", - "type": "numeric" - }, - "volsize": { - "edit": True, - "inherit": False, - "values": "", - "type": "size" - }, - "clones": { - "edit": False, - "inherit": False, - "values": "[,...]", - "type": "str" - }, - "canmount": { - "edit": True, - "inherit": False, - "values": "on | off | noauto", - "type": "bool" - }, - "mounted": { - "edit": False, - "inherit": False, - "values": "yes | no", - "type": "bool_alt" - }, - "groupused@": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "normalization": { - "edit": False, - "inherit": True, - "values": "none | formc | formd | formkc | formkd", - "type": "str" - }, - "usedbychildren": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "usedbydataset": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "mlslabel": { - "edit": True, - "inherit": True, - "values": "", - "type": "str" - }, - "refreserv": { - "edit": True, - "inherit": False, - "values": " | none", - "type": "size" - }, - "defer_destroy": { - "edit": False, - "inherit": False, - "values": "yes | no", - "type": "bool_alt" - }, - "volblocksize": { - "edit": False, - "inherit": True, - "values": "512 to 128k, power of 2", - "type": "str" - }, - "lused.": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "redundant_metadata": { - "edit": True, - "inherit": True, - "values": "all | most", - "type": "str" - }, - "filesystem_count": { - "edit": False, - "inherit": False, - "values": "", - "type": "numeric" - }, - "devices": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "refreservation": { - "edit": True, - "inherit": False, - "values": " | none", - "type": "size" - }, - "userused@": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "logicalreferenced": { - "edit": False, - "inherit": False, - "values": "", - "type": "size" - }, - "checksum": { - "edit": True, - "inherit": True, - "values": "on | off | fletcher2 | fletcher4 | sha256 | sha512 | skein | edonr", - "type": "bool" - }, - "nbmand": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - } -} - - -def _from_auto(name, value, source='auto'): - ''' - some more complex patching for zfs.from_auto - ''' - with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)), \ - patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - return salt.utils.zfs.from_auto(name, value, source) - - -def _from_auto_dict(values, source='auto'): - ''' - some more complex patching for zfs.from_auto_dict - ''' - with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)), \ - patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - return salt.utils.zfs.from_auto_dict(values, source) - - -def _to_auto(name, value, source='auto', convert_to_human=True): - ''' - some more complex patching for zfs.to_auto - ''' - with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)), \ - patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - return salt.utils.zfs.to_auto(name, value, source, convert_to_human) - - -def _to_auto_dict(values, source='auto', convert_to_human=True): - ''' - some more complex patching for zfs.to_auto_dict - ''' - with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)), \ - patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - return salt.utils.zfs.to_auto_dict(values, source, convert_to_human) - - -utils_patch = { - 'zfs.is_supported': MagicMock(return_value=True), - 'zfs.has_feature_flags': MagicMock(return_value=True), - 'zfs.property_data_zpool': MagicMock(return_value=pmap_zpool), - 'zfs.property_data_zfs': MagicMock(return_value=pmap_zfs), - # NOTE: we make zpool_command and zfs_command a NOOP - # these are extensively tested in tests.unit.utils.test_zfs - 'zfs.zpool_command': MagicMock(return_value='/bin/false'), - 'zfs.zfs_command': MagicMock(return_value='/bin/false'), - # NOTE: from_auto_dict is a special snowflake - # internally it calls multiple calls from - # salt.utils.zfs but we cannot patch those using - # the common methode, __utils__ is not available - # so they are direct calls, we do some voodoo here. - 'zfs.from_auto_dict': _from_auto_dict, - 'zfs.from_auto': _from_auto, - 'zfs.to_auto_dict': _to_auto_dict, - 'zfs.to_auto': _to_auto, -} - # Skip this test case if we don't have access to mock! -@skipIf(NO_MOCK, NO_MOCK_REASON) class ZfsUtilsTestCase(TestCase): ''' This class contains a set of functions that test salt.utils.zfs utils ''' - ## NOTE: test parameter parsing + def setUp(self): + # property_map mocks + mock_data = ZFSMockData() + self.pmap_zfs = mock_data.pmap_zfs + self.pmap_zpool = mock_data.pmap_zpool + self.pmap_exec_zfs = mock_data.pmap_exec_zfs + self.pmap_exec_zpool = mock_data.pmap_exec_zpool + for name in ('pmap_zfs', 'pmap_zpool', 'pmap_exec_zfs', 'pmap_exec_zpool'): + self.addCleanup(delattr, self, name) + + # NOTE: test parameter parsing def test_is_supported(self): ''' Test zfs.is_supported method @@ -908,8 +61,8 @@ def test_property_data_zpool(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, '_exec', MagicMock(return_value=pmap_exec_zpool)): - self.assertEqual(zfs.property_data_zpool(), pmap_zpool) + with patch.object(zfs, '_exec', MagicMock(return_value=self.pmap_exec_zpool)): + self.assertEqual(zfs.property_data_zpool(), self.pmap_zpool) def test_property_data_zfs(self): ''' @@ -917,10 +70,10 @@ def test_property_data_zfs(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, '_exec', MagicMock(return_value=pmap_exec_zfs)): - self.assertEqual(zfs.property_data_zfs(), pmap_zfs) + with patch.object(zfs, '_exec', MagicMock(return_value=self.pmap_exec_zfs)): + self.assertEqual(zfs.property_data_zfs(), self.pmap_zfs) - ## NOTE: testing from_bool results + # NOTE: testing from_bool results def test_from_bool_on(self): ''' Test from_bool with 'on' @@ -977,7 +130,7 @@ def test_from_bool_alt_passthrough(self): self.assertEqual(zfs.from_bool_alt('passthrough'), 'passthrough') self.assertEqual(zfs.from_bool_alt(zfs.from_bool_alt('passthrough')), 'passthrough') - ## NOTE: testing to_bool results + # NOTE: testing to_bool results def test_to_bool_true(self): ''' Test to_bool with True @@ -1034,7 +187,7 @@ def test_to_bool_alt_passthrough(self): self.assertEqual(zfs.to_bool_alt('passthrough'), 'passthrough') self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt('passthrough')), 'passthrough') - ## NOTE: testing from_numeric results + # NOTE: testing from_numeric results def test_from_numeric_str(self): ''' Test from_numeric with '42' @@ -1063,7 +216,7 @@ def test_from_numeric_passthrough(self): self.assertEqual(zfs.from_numeric('passthrough'), 'passthrough') self.assertEqual(zfs.from_numeric(zfs.from_numeric('passthrough')), 'passthrough') - ## NOTE: testing to_numeric results + # NOTE: testing to_numeric results def test_to_numeric_str(self): ''' Test to_numeric with '42' @@ -1092,7 +245,7 @@ def test_to_numeric_passthrough(self): self.assertEqual(zfs.to_numeric('passthrough'), 'passthrough') self.assertEqual(zfs.to_numeric(zfs.to_numeric('passthrough')), 'passthrough') - ## NOTE: testing from_size results + # NOTE: testing from_size results def test_from_size_absolute(self): ''' Test from_size with '5G' @@ -1121,7 +274,7 @@ def test_from_size_passthrough(self): self.assertEqual(zfs.from_size('passthrough'), 'passthrough') self.assertEqual(zfs.from_size(zfs.from_size('passthrough')), 'passthrough') - ## NOTE: testing to_size results + # NOTE: testing to_size results def test_to_size_str_absolute(self): ''' Test to_size with '5368709120' @@ -1164,7 +317,7 @@ def test_to_size_passthrough(self): self.assertEqual(zfs.to_size('passthrough'), 'passthrough') self.assertEqual(zfs.to_size(zfs.to_size('passthrough')), 'passthrough') - ## NOTE: testing from_str results + # NOTE: testing from_str results def test_from_str_space(self): ''' Test from_str with "\"my pool/my dataset\" @@ -1200,12 +353,12 @@ def test_from_str_passthrough(self): self.assertEqual(zfs.from_str('passthrough'), 'passthrough') self.assertEqual(zfs.from_str(zfs.from_str('passthrough')), 'passthrough') - ## NOTE: testing to_str results + # NOTE: testing to_str results def test_to_str_space(self): ''' Test to_str with 'my pool/my dataset' ''' - ## NOTE: for fun we use both the '"str"' and "\"str\"" way of getting the literal string: "str" + # NOTE: for fun we use both the '"str"' and "\"str\"" way of getting the literal string: "str" self.assertEqual(zfs.to_str('my pool/my dataset'), '"my pool/my dataset"') self.assertEqual(zfs.to_str(zfs.to_str('my pool/my dataset')), "\"my pool/my dataset\"") @@ -1230,7 +383,7 @@ def test_to_str_passthrough(self): self.assertEqual(zfs.to_str('passthrough'), 'passthrough') self.assertEqual(zfs.to_str(zfs.to_str('passthrough')), 'passthrough') - ## NOTE: testing is_snapshot + # NOTE: testing is_snapshot def test_is_snapshot_snapshot(self): ''' Test is_snapshot with a valid snapshot name @@ -1249,7 +402,7 @@ def test_is_snapshot_filesystem(self): ''' self.assertFalse(zfs.is_snapshot('zpool_name/dataset')) - ## NOTE: testing is_bookmark + # NOTE: testing is_bookmark def test_is_bookmark_snapshot(self): ''' Test is_bookmark with a valid snapshot name @@ -1268,7 +421,7 @@ def test_is_bookmark_filesystem(self): ''' self.assertFalse(zfs.is_bookmark('zpool_name/dataset')) - ## NOTE: testing is_dataset + # NOTE: testing is_dataset def test_is_dataset_snapshot(self): ''' Test is_dataset with a valid snapshot name @@ -1287,15 +440,15 @@ def test_is_dataset_filesystem(self): ''' self.assertTrue(zfs.is_dataset('zpool_name/dataset')) - ## NOTE: testing zfs_command + # NOTE: testing zfs_command def test_zfs_command_simple(self): ''' Test if zfs_command builds the correct string ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): self.assertEqual( zfs.zfs_command('list'), "/sbin/zfs list" @@ -1307,8 +460,8 @@ def test_zfs_command_none_target(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): self.assertEqual( zfs.zfs_command('list', target=[None, 'mypool', None]), "/sbin/zfs list mypool" @@ -1320,8 +473,8 @@ def test_zfs_command_flag(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_flags = [ '-r', # recursive ] @@ -1336,8 +489,8 @@ def test_zfs_command_opt(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_opts = { '-t': 'snap', # only list snapshots } @@ -1352,8 +505,8 @@ def test_zfs_command_flag_opt(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_flags = [ '-r', # recursive ] @@ -1371,8 +524,8 @@ def test_zfs_command_target(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_flags = [ '-r', # recursive ] @@ -1390,8 +543,8 @@ def test_zfs_command_target_with_space(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_flags = [ '-r', # recursive ] @@ -1409,8 +562,8 @@ def test_zfs_command_property(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): self.assertEqual( zfs.zfs_command('get', property_name='quota', target='mypool'), "/sbin/zfs get quota mypool" @@ -1422,8 +575,8 @@ def test_zfs_command_property_value(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_flags = [ '-r', # recursive ] @@ -1438,8 +591,8 @@ def test_zfs_command_multi_property_value(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): property_name = ['quota', 'readonly'] property_value = ['5G', 'no'] self.assertEqual( @@ -1453,8 +606,8 @@ def test_zfs_command_fs_props(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_flags = [ '-p', # create parent ] @@ -1473,8 +626,8 @@ def test_zfs_command_fs_props_with_space(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_props = { 'quota': '4.2M', 'compression': 'lz4', @@ -1484,15 +637,15 @@ def test_zfs_command_fs_props_with_space(self): '/sbin/zfs create -o compression=lz4 -o quota=4404019 "my pool/jorge\'s dataset"' ) - ## NOTE: testing zpool_command + # NOTE: testing zpool_command def test_zpool_command_simple(self): ''' Test if zfs_command builds the correct string ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): self.assertEqual( zfs.zpool_command('list'), "/sbin/zpool list" @@ -1504,8 +657,8 @@ def test_zpool_command_opt(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_opts = { '-o': 'name,size', # show only name and size } @@ -1520,8 +673,8 @@ def test_zpool_command_opt_list(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_opts = { '-d': ['/tmp', '/zvol'], } @@ -1536,8 +689,8 @@ def test_zpool_command_flag_opt(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_opts = { '-o': 'name,size', # show only name and size } @@ -1552,8 +705,8 @@ def test_zpool_command_target(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_opts = { '-o': 'name,size', # show only name and size } @@ -1568,8 +721,8 @@ def test_zpool_command_target_with_space(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): fs_props = { 'quota': '100G', } @@ -1587,8 +740,8 @@ def test_zpool_command_property(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): self.assertEqual( zfs.zpool_command('get', property_name='comment', target='mypool'), "/sbin/zpool get comment mypool" @@ -1600,8 +753,8 @@ def test_zpool_command_property_value(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): my_flags = [ '-v', # verbose ] @@ -1616,8 +769,8 @@ def test_parse_command_result_success(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): res = {} res['retcode'] = 0 res['stderr'] = '' @@ -1633,8 +786,8 @@ def test_parse_command_result_success_nolabel(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): res = {} res['retcode'] = 0 res['stderr'] = '' @@ -1650,8 +803,8 @@ def test_parse_command_result_fail(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): res = {} res['retcode'] = 1 res['stderr'] = '' @@ -1667,8 +820,8 @@ def test_parse_command_result_nolabel(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): res = {} res['retcode'] = 1 res['stderr'] = '' @@ -1684,8 +837,8 @@ def test_parse_command_result_fail_message(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): res = {} res['retcode'] = 1 res['stderr'] = "\n".join([ @@ -1705,8 +858,8 @@ def test_parse_command_result_fail_message_nolabel(self): ''' with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=pmap_zpool)): + with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): res = {} res['retcode'] = 1 res['stderr'] = "\n".join([ @@ -1719,5 +872,3 @@ def test_parse_command_result_fail_message_nolabel(self): zfs.parse_command_result(res), OrderedDict([('error', 'ice is not hot')]), ) - -# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/tests/unit/utils/validate/test_net.py b/tests/unit/utils/validate/test_net.py index 1114a31a02ef..debbcab76f49 100644 --- a/tests/unit/utils/validate/test_net.py +++ b/tests/unit/utils/validate/test_net.py @@ -7,11 +7,9 @@ from salt.utils.validate import net # Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import NO_MOCK, NO_MOCK_REASON +from tests.support.unit import TestCase -@skipIf(NO_MOCK, NO_MOCK_REASON) class ValidateNetTestCase(TestCase): ''' TestCase for salt.utils.validate.net module diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 651c2c76a4d1..000000000000 --- a/tox.ini +++ /dev/null @@ -1,35 +0,0 @@ -[tox] -envlist = - py{27,34,35,36}, - py{27,34,35,36}-coverage, - py{27,34,35,36}-pytest, - py{27,34,35,36}-runtests, - py{27,34,35,36}-pytest-coverage, - py{27,34,35,36}-runtests-coverage, - pylint-salt, - pylint-tests -skip_missing_interpreters = True -skipsdist = True - -[testenv] -passenv = LANG HOME -sitepackages = True -commands = - python -c 'import sys; sys.stderr.write("\n\nPlease use nox instead.\n\n"); sys.exit(1)' - -[testenv:pylint-salt] -basepython = python2.7 -deps = -r{toxinidir}/requirements/dev.txt -commands = - pylint --version - pylint --rcfile=.testing.pylintrc --disable=I,W1307,C0411,C0413,W8410,str-format-in-logging {posargs:setup.py salt/} -sitepackages = False - - -[testenv:pylint-tests] -basepython = python2.7 -deps = -r{toxinidir}/requirements/dev.txt -commands = - pylint --version - pylint --rcfile=.testing.pylintrc --disable=I,W0232,E1002,W1307,C0411,C0413,W8410,str-format-in-logging {posargs:tests/} -sitepackages = False