Merge branch 'master' into issue_49420

This commit is contained in:
Daniel Wozniak 2019-12-14 02:14:12 -07:00 committed by GitHub
commit d3be3c0b79
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
690 changed files with 2012 additions and 3555 deletions

View file

@ -63,6 +63,34 @@ 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
@ -86,6 +114,16 @@ 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
@ -146,6 +184,34 @@ 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
@ -206,6 +272,34 @@ 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
@ -266,6 +360,35 @@ repos:
- --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:

View file

@ -13,7 +13,10 @@ Versions are `MAJOR.PATCH`.
- [#54943](https://github.com/saltstack/salt/pull/54943) - RAET transport method has been removed per the deprecation schedule - [@s0undt3ch](https://github.com/s0undt3ch)
### Deprecated
- [#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
@ -26,6 +29,7 @@ Versions are `MAJOR.PATCH`.
- [#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)
---

View file

@ -5388,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``

View file

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

View file

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

View file

@ -125,3 +125,10 @@ 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.

View file

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

View file

@ -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
<salt.modules.win_pkg.refresh_db>` fetches the software package definitions.
:mod:`pkg.refresh_db <salt.modules.win_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 <salt.modules.win_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 <salt.modules.win_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 <salt.modules.win_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
<salt.runners.winrepo.genrepo>`) 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

View file

@ -151,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),

2
requirements/crypto.txt Normal file
View file

@ -0,0 +1,2 @@
pycryptodome; sys.platform != 'win32'
pycryptodomex; sys.platform == 'win32'

View file

@ -5,6 +5,5 @@
# pip-compile -o requirements/static/py2.7/darwin-crypto.txt -v requirements/static/crypto.in
#
m2crypto==0.35.2
pycryptodome==3.9.0
pycryptodomex==3.9.0
typing==3.7.4.1 # via m2crypto

View file

@ -49,7 +49,6 @@ google-auth==1.6.3 # via kubernetes
idna==2.8
importlib-metadata==0.23 # via pluggy, pytest
ipaddress==1.0.22
jaraco.classes==2.0 # via cherrypy
jaraco.functools==2.0 # via tempora
jinja2==2.10.1
jmespath==0.9.4 # via boto3, botocore
@ -84,9 +83,7 @@ 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
pycryptodome==3.8.1 ; sys_platform != "win32"
pynacl==1.3.0 # via paramiko
pyopenssl==19.0.0
pyparsing==2.4.5 # via packaging

View file

@ -5,6 +5,5 @@
# pip-compile -o requirements/static/py2.7/linux-crypto.txt -v requirements/static/crypto.in
#
m2crypto==0.35.2
pycryptodome==3.9.0
pycryptodomex==3.9.0
typing==3.7.4.1 # via m2crypto

View file

@ -79,9 +79,7 @@ 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

View file

@ -5,6 +5,5 @@
# pip-compile -o requirements/static/py2.7/windows-crypto.txt -v requirements/static/crypto.in
#
m2crypto==0.35.2
pycryptodome==3.9.0
pycryptodomex==3.9.0
typing==3.7.4.1 # via m2crypto

View file

@ -83,7 +83,6 @@ pycurl==7.43.0.2
pygit2==0.28.2
pymysql==0.9.3
pyopenssl==19.0.0
pypiwin32==223 # via cherrypy
pyparsing==2.4.5 # via packaging
pytest-helpers-namespace==2019.1.8
pytest-salt-runtests-bridge==2019.7.10

View file

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

View file

@ -70,9 +70,7 @@ 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

View file

@ -5,6 +5,5 @@
# pip-compile -o requirements/static/py3.5/darwin-crypto.txt -v requirements/static/crypto.in
#
m2crypto==0.35.2
pycryptodome==3.9.0
pycryptodomex==3.9.0
typing==3.7.4.1 # via m2crypto

View file

@ -42,7 +42,6 @@ google-auth==1.6.3 # via kubernetes
idna==2.8
importlib-metadata==0.23 # via pluggy, pytest
ipaddress==1.0.22
jaraco.classes==2.0 # via cherrypy
jaraco.functools==2.0 # via tempora
jinja2==2.10.1
jmespath==0.9.4 # via boto3, botocore
@ -76,9 +75,7 @@ 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
pycryptodome==3.8.1 ; sys_platform != "win32"
pynacl==1.3.0 # via paramiko
pyopenssl==19.0.0
pyparsing==2.4.5 # via packaging

View file

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

View file

@ -70,9 +70,7 @@ 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

View file

@ -5,6 +5,5 @@
# pip-compile -o requirements/static/py3.5/windows-crypto.txt -v requirements/static/crypto.in
#
m2crypto==0.35.2
pycryptodome==3.9.0
pycryptodomex==3.9.0
typing==3.7.4.1 # via m2crypto

View file

@ -75,7 +75,6 @@ pycurl==7.43.0.2
pygit2==0.28.2
pymysql==0.9.3
pyopenssl==19.0.0
pypiwin32==223 # via cherrypy
pyparsing==2.4.5 # via packaging
pytest-helpers-namespace==2019.1.8
pytest-salt-runtests-bridge==2019.7.10

View file

@ -5,6 +5,5 @@
# pip-compile -o requirements/static/py3.6/darwin-crypto.txt -v requirements/static/crypto.in
#
m2crypto==0.35.2
pycryptodome==3.9.0
pycryptodomex==3.9.0
typing==3.7.4.1 # via m2crypto

View file

@ -42,7 +42,6 @@ google-auth==1.6.3 # via kubernetes
idna==2.8
importlib-metadata==0.23 # via pluggy, pytest
ipaddress==1.0.22
jaraco.classes==2.0 # via cherrypy
jaraco.functools==2.0 # via tempora
jinja2==2.10.1
jmespath==0.9.4 # via boto3, botocore
@ -76,9 +75,7 @@ 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
pycryptodome==3.8.1 ; sys_platform != "win32"
pynacl==1.3.0 # via paramiko
pyopenssl==19.0.0
pyparsing==2.4.5 # via packaging

View file

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

View file

@ -70,9 +70,7 @@ 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

View file

@ -5,6 +5,5 @@
# pip-compile -o requirements/static/py3.6/windows-crypto.txt -v requirements/static/crypto.in
#
m2crypto==0.35.2
pycryptodome==3.9.0
pycryptodomex==3.9.0
typing==3.7.4.1 # via m2crypto

View file

@ -75,7 +75,6 @@ pycurl==7.43.0.2
pygit2==0.28.2
pymysql==0.9.3
pyopenssl==19.0.0
pypiwin32==223 # via cherrypy
pyparsing==2.4.5 # via packaging
pytest-helpers-namespace==2019.1.8
pytest-salt-runtests-bridge==2019.7.10

View file

@ -5,6 +5,5 @@
# pip-compile -o requirements/static/py3.7/darwin-crypto.txt -v requirements/static/crypto.in
#
m2crypto==0.35.2
pycryptodome==3.9.0
pycryptodomex==3.9.0
typing==3.7.4.1 # via m2crypto

View file

@ -42,7 +42,6 @@ google-auth==1.6.3 # via kubernetes
idna==2.8
importlib-metadata==0.23 # via pluggy, pytest
ipaddress==1.0.22
jaraco.classes==2.0 # via cherrypy
jaraco.functools==2.0 # via tempora
jinja2==2.10.1
jmespath==0.9.4 # via boto3, botocore
@ -76,9 +75,7 @@ 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
pycryptodome==3.8.1 ; sys_platform != "win32"
pynacl==1.3.0 # via paramiko
pyopenssl==19.0.0
pyparsing==2.4.5 # via packaging

View file

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

View file

@ -70,9 +70,7 @@ 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

View file

@ -5,6 +5,5 @@
# pip-compile -o requirements/static/py3.7/windows-crypto.txt -v requirements/static/crypto.in
#
m2crypto==0.35.2
pycryptodome==3.9.0
pycryptodomex==3.9.0
typing==3.7.4.1 # via m2crypto

View file

@ -71,7 +71,6 @@ pyasn1==0.4.5
pycparser==2.19
pycryptodome==3.8.1 # via python-jose
pycryptodomex==3.8.1 ; sys_platform == "win32"
pypiwin32==223 # via cherrypy
pycurl==7.43.0.2
pygit2==0.28.2
pymysql==0.9.3

View file

@ -21,7 +21,6 @@ psutil
pyopenssl
python-gnupg
pyvmomi
pywin32==223
rfc3987
salttesting==2017.6.1
sed

View file

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

View file

@ -484,199 +484,6 @@ class LoadAuth(object):
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:
<stuff returned from django>
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%: <all users in the AD 'webadmins' group>
- 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

View file

@ -629,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.
@ -2772,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

View file

@ -537,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
@ -553,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,
@ -563,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
@ -623,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 {}

View file

@ -423,7 +423,7 @@ class MinionBase(object):
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
@ -442,22 +442,26 @@ class MinionBase(object):
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, proxy=self.proxy)
self.executors = salt.loader.executors(self.opts,
functions=self.functions,
proxy=self.proxy,
context=context)
@staticmethod
def process_schedule(minion, loop_interval):
@ -829,7 +833,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)
@ -843,7 +847,7 @@ class SMinion(MinionBase):
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):
@ -1638,8 +1642,8 @@ class Minion(MinionBase):
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]
@ -3393,7 +3397,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
@ -3428,13 +3432,23 @@ class SProxyMinion(SMinion):
# Then load the proxy module
self.proxy = salt.loader.proxy(self.opts)
self.utils = salt.loader.utils(self.opts, 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)
self.returners = salt.loader.returners(self.opts, self.functions, proxy=self.proxy)
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']
@ -3449,7 +3463,7 @@ class SProxyMinion(SMinion):
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

View file

@ -196,9 +196,14 @@ def add(name, beacon_data, **kwargs):
if name in beacons and beacons[name] == beacon_data:
ret['result'] = True
ret['comment'] = 'Added beacon: {0}.'.format(name)
else:
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
@ -295,9 +300,14 @@ def modify(name, beacon_data, **kwargs):
if name in beacons and beacons[name] == beacon_data:
ret['result'] = True
ret['comment'] = 'Modified beacon: {0}.'.format(name)
else:
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
@ -342,9 +352,14 @@ def delete(name, **kwargs):
ret['result'] = True
ret['comment'] = 'Deleted beacon: {0}.'.format(name)
return ret
else:
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.'
@ -422,9 +437,14 @@ def enable(**kwargs):
if 'enabled' in beacons and beacons['enabled']:
ret['result'] = True
ret['comment'] = 'Enabled beacons on minion.'
else:
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
@ -464,9 +484,14 @@ def disable(**kwargs):
if 'enabled' in beacons and not beacons['enabled']:
ret['result'] = True
ret['comment'] = 'Disabled beacons on minion.'
else:
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
@ -532,12 +557,14 @@ def enable_beacon(name, **kwargs):
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
if event_ret is not None:
ret['comment'] = event_ret['comment']
else:
ret['comment'] = 'Beacon enabled event never received'
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
@ -593,12 +620,14 @@ def disable_beacon(name, **kwargs):
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
if event_ret is not None:
ret['comment'] = event_ret['comment']
else:
ret['comment'] = 'Beacon disabled event never received'
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

View file

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

View file

@ -11,7 +11,6 @@ import sys
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

View file

@ -481,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)
@ -711,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:
@ -995,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)
@ -1036,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:

View file

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

View file

@ -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
@ -2154,6 +2159,10 @@ def _get_name_map(saltenv='base'):
return u_name_map
def get_package_info(name, saltenv='base'):
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
@ -2188,7 +2197,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

View file

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

View file

@ -957,7 +957,7 @@ class State(object):
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 @@ class State(object):
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):
'''
@ -4210,7 +4210,7 @@ class MasterState(State):
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):

View file

@ -403,6 +403,7 @@ def wait(name,
unless=None,
creates=None,
cwd=None,
root=None,
runas=None,
shell=None,
env=(),
@ -437,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
@ -677,6 +682,7 @@ def run(name,
unless=None,
creates=None,
cwd=None,
root=None,
runas=None,
shell=None,
env=None,
@ -710,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
@ -887,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'],
@ -917,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

View file

@ -1514,6 +1514,75 @@ def installed(
see the :ref:`Reloading Modules <reloading-modules>` documentation for more
information.
.. seealso:: unless and onlyif
You can use the :ref:`unless <unless-requisite>` or
:ref:`onlyif <onlyif-requisite>` 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 <unless-requisite>`
'''
if isinstance(pkgs, list) and len(pkgs) == 0:
return {'name': name,

View file

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

View file

@ -779,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.

View file

@ -122,7 +122,7 @@ def present(host, groups, interfaces, **kwargs):
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]))

View file

@ -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):
'''

View file

@ -777,30 +777,6 @@ class CkMinions(object):
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,

View file

@ -8,16 +8,11 @@ from __future__ import absolute_import, unicode_literals, print_function
# 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):

View file

@ -8,17 +8,11 @@ from __future__ import absolute_import, unicode_literals, print_function
# 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):

View file

@ -239,25 +239,36 @@ def pytest_configure(config):
# ----- 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_closest_marker('destructive_test')
if destructive_tests_marker is not None:
if destructive_tests_marker is not None or _has_unittest_attr(item, '__destructive_test__'):
if item.config.getoption('--run-destructive') is False:
pytest.skip('Destructive tests are disabled')
os.environ['DESTRUCTIVE_TESTS'] = six.text_type(item.config.getoption('--run-destructive'))
expensive_tests_marker = item.get_closest_marker('expensive_test')
if expensive_tests_marker is not None:
if expensive_tests_marker is not None or _has_unittest_attr(item, '__expensive_test__'):
if item.config.getoption('--run-expensive') is False:
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_closest_marker('skip_if_not_root')
if skip_if_not_root_marker is not None:
if skip_if_not_root_marker is not None or _has_unittest_attr(item, '__skip_if_not_root__'):
if os.getuid() != 0:
pytest.skip('You must be logged in as root to run this test')

View file

@ -36,7 +36,7 @@ from tests.support.unit import TestCase
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
@ -276,6 +276,7 @@ class TestDaemon(object):
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(
@ -313,6 +314,7 @@ class TestDaemon(object):
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(
@ -350,6 +352,7 @@ class TestDaemon(object):
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(
@ -388,6 +391,7 @@ class TestDaemon(object):
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(
@ -425,6 +429,7 @@ class TestDaemon(object):
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(
@ -716,6 +721,10 @@ class TestDaemon(object):
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,
@ -788,6 +797,10 @@ class TestDaemon(object):
syndic_master_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER
syndic_master_opts['root_dir'] = os.path.join(TMP, 'rootdir-syndic-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

View file

@ -7,13 +7,14 @@ Integration tests for DigitalOcean APIv2
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 @@ class DigitalOceanTest(CloudTest):
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)])

View file

@ -15,7 +15,6 @@ import salt.utils.yaml
# Import Salt Testing Libs
from tests.support.runtests import RUNTIME_VARS
from tests.support.helpers import expensiveTest
from tests.support.unit import skipIf
from tests.support import win_installer
@ -59,7 +58,6 @@ class EC2Test(CloudTest):
self._installer = self.__fetch_installer()
return self._installer
@expensiveTest
def setUp(self):
'''
Sets up the test requirements

View file

@ -26,6 +26,7 @@ TIMEOUT = 500
log = logging.getLogger(__name__)
@expensiveTest
class CloudTest(ShellCase):
PROVIDER = ''
REQUIRED_PROVIDER_CONFIG_ITEMS = tuple()
@ -178,7 +179,6 @@ class CloudTest(ShellCase):
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

View file

@ -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 @@ class PyTestEngine(object):
@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.warning('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 @@ class PyTestEngine(object):
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,17 +97,20 @@ class PyTestEngine(object):
@gen.coroutine
def fire_master_started_event(self):
log.info('Firing salt-master started event...')
log.info('Firing salt-%s started event...', self.opts['__role'])
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': {}}
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
timeout = 30
while True:
if self.stop_sending_events_file and not os.path.exists(self.stop_sending_events_file):
break
timeout -= 1
try:
event_bus.fire_event(load, master_start_event_tag, timeout=500)
event_bus.fire_event(load, start_event_tag, timeout=500)
if timeout <= 0:
break
yield gen.sleep(1)

View file

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

View file

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

View file

@ -6,7 +6,7 @@ from __future__ import absolute_import, print_function, unicode_literals
# 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 @@ class LoggingJIDsTest(ModuleCase):
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):

View file

@ -17,7 +17,7 @@ import subprocess
# 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

View file

@ -69,7 +69,7 @@ class ConfigTest(ModuleCase):
self.run_function(
'config.option',
['master_port']),
64506)
self.get_config('minion')['master_port'])
# pillar conf opt
self.assertEqual(
self.run_function(

View file

@ -12,7 +12,8 @@ from tests.support.helpers import (
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
@ -37,6 +38,7 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin):
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:

View file

@ -81,7 +81,6 @@ from tests.support.helpers import (
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 @@ class GitPythonMixin(object):
@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 @@ class TestGitPythonAuthenticatedHTTP(TestGitPythonHTTP, GitPythonMixin):
@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 @@ class TestPygit2SSH(GitPillarSSHTestBase):
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 @@ class TestPygit2HTTP(GitPillarHTTPTestBase):
)
@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))

View file

@ -12,19 +12,17 @@ from __future__ import absolute_import
# 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

View file

@ -14,7 +14,7 @@ import logging
# 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 @@ log = logging.getLogger(__name__)
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'

View file

@ -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: '
'<function-name> <provider>',
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 @@ class SaltCloudCliTest(ShellCase,
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 @@ class SaltCloudCliTest(ShellCase,
)
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)

View file

@ -32,6 +32,8 @@ log = logging.getLogger(__name__)
class CopyTest(ShellCase, ShellCaseCommonTestsMixin):
_call_binary_ = 'salt-cp'
def test_cp_testfile(self):
'''
test salt-cp

View file

@ -252,15 +252,15 @@ class KeyTest(ShellCase, ShellCaseCommonTestsMixin):
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)

View file

@ -21,6 +21,8 @@ from tests.integration.utils import testprogram
@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.

View file

@ -24,6 +24,8 @@ class MatchTest(ShellCase, ShellCaseCommonTestsMixin):
Test salt matchers
'''
_call_binary_ = 'salt'
def test_list(self):
'''
test salt -L matcher

View file

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

View file

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

View file

@ -33,6 +33,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):
'''

View file

@ -24,7 +24,7 @@ from tests.support.helpers import (
)
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': {

View file

@ -7,6 +7,7 @@ tests for host state
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
@ -17,21 +18,29 @@ from tests.support.mixins import SaltReturnAssertsMixin
import salt.utils.files
import salt.utils.stringutils
log = logging.getLogger(__name__)
class HostTest(ModuleCase, SaltReturnAssertsMixin):
'''
Validate the host state
'''
def setUp(self):
self.hfpath = os.path.join(RUNTIME_VARS.TMP, 'hosts')
shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, 'hosts'), self.hfpath)
self.addCleanup(self._delete_hosts_file)
super(HostTest, self).setUp()
@classmethod
def setUpClass(cls):
cls.hosts_file = os.path.join(RUNTIME_VARS.TMP, 'hosts')
def _delete_hosts_file(self):
if os.path.exists(self.hfpath):
os.remove(self.hfpath)
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(RUNTIME_VARS.FILES, 'hosts'), self.hosts_file)
self.addCleanup(self.__clear_hosts)
super(HostTest, self).setUp()
def test_present(self):
'''
@ -41,6 +50,6 @@ class HostTest(ModuleCase, SaltReturnAssertsMixin):
ip = '10.10.10.10'
ret = self.run_state('host.present', name=name, ip=ip)
self.assertSaltTrueReturn(ret)
with salt.utils.files.fopen(self.hfpath) 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)

View file

@ -5,6 +5,7 @@ Integration tests for the zookeeper states
# 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 @@ try:
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):

View file

@ -30,10 +30,9 @@ from salt.ext import six
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.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__)
@ -440,27 +439,8 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)):
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 @@ class TestSaltProgramMeta(TestProgramMeta):
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 @@ class TestSaltProgram(six.with_metaclass(TestSaltProgramMeta, TestProgram)):
# 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 @@ class TestSaltProgram(six.with_metaclass(TestSaltProgramMeta, TestProgram)):
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(RUNTIME_VARS.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', [])

View file

@ -18,9 +18,7 @@ import os
import re
import sys
import time
import stat
import errno
import signal
import textwrap
import logging
import tempfile
@ -29,16 +27,14 @@ from datetime import datetime, timedelta
# 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
@ -48,74 +44,15 @@ STATE_FUNCTION_RUNNING_RE = re.compile(
r'''The function (?:"|')(?P<state_func>.*)(?:"|') is running as PID '''
r'(?P<pid>[\d]+) and was started at (?P<date>.*) with jid (?P<jid>[\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
@ -378,33 +315,7 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin):
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(),
@ -852,15 +763,20 @@ class ModuleCase(TestCase, SaltClientTestCaseMixin):
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
@ -872,16 +788,6 @@ class ModuleCase(TestCase, SaltClientTestCaseMixin):
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
@ -954,15 +860,20 @@ class MultimasterModuleCase(ModuleCase, SaltMultimasterClientTestCaseMixin):
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
@ -997,8 +908,12 @@ class SyndicCase(TestCase, SaltClientTestCaseMixin):
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)
)

View file

@ -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=True
)
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)

View file

@ -444,6 +444,8 @@ class SSHDMixin(SaltClientMixin, SaltReturnAssertsMixin):
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 @@ class WebserverMixin(SaltClientMixin, SaltReturnAssertsMixin):
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()

View file

@ -22,7 +22,6 @@ import logging
import os
import random
import shutil
import signal
import socket
import string
import subprocess
@ -36,7 +35,6 @@ import tornado.web
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
from pytestsalt.utils import get_unused_localhost_port
@ -51,9 +49,6 @@ import salt.utils.files
import salt.utils.platform
import salt.utils.stringutils
if salt.utils.platform.is_windows():
import salt.utils.win_functions
log = logging.getLogger(__name__)
HAS_SYMLINKS = None
@ -97,6 +92,11 @@ def destructiveTest(caller):
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)
@ -131,6 +131,11 @@ def expensiveTest(caller):
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)
@ -325,14 +330,14 @@ class RedirectStdStreams(object):
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
@ -408,27 +413,6 @@ class TestsLoggingHandler(object):
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
@ -1089,7 +1073,6 @@ def requires_salt_modules(*names):
.. versionadded:: 0.5.2
'''
def _check_required_salt_modules(*required_salt_modules):
# Late import
from tests.support.sminion import create_sminion
@ -1122,7 +1105,6 @@ def requires_salt_modules(*names):
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)
@ -1142,6 +1124,7 @@ def requires_salt_modules(*names):
return caller(cls)
return wrapper
return decorator
@ -1179,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
@ -1192,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 = '<could not be retrived; dead process: {0}>'.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.
@ -1623,30 +1445,6 @@ class MirrorPostHandler(tornado.web.RequestHandler):
raise NotImplementedError()
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.
'''
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 dedent(text, linesep=os.linesep):
'''
A wrapper around textwrap.dedent that also sets line endings.

View file

@ -14,6 +14,7 @@
from __future__ import absolute_import, print_function
import os
import sys
import copy
import time
import types
import atexit
@ -25,7 +26,7 @@ import subprocess
import multiprocessing
# 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 +71,20 @@ class CheckShellBinaryNameAndVersionMixin(object):
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)
@ -409,9 +423,6 @@ class LoaderModuleMockMixin(six.with_metaclass(_FixLoaderModuleMockMixinMroOrder
@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(
@ -709,23 +720,18 @@ class SaltReturnAssertsMixin(object):
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):
@ -749,11 +755,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.SignalHandlingProcess(
target=_fetch_events, args=(cls.q,)
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
@ -761,10 +770,12 @@ class SaltMinionEventAssertsMixin(object):
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

View file

@ -25,75 +25,26 @@ import sys
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)
# 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')
# 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'
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):

View file

@ -30,7 +30,7 @@ from functools import partial
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
@ -860,18 +860,18 @@ class SaltTestingParser(optparse.OptionParser):
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')

View file

@ -16,10 +16,8 @@ from __future__ import absolute_import
import os
import re
import sys
import stat
import logging
import tempfile
import textwrap
import salt.utils.path
@ -78,53 +76,6 @@ TMP_SCRIPT_DIR = os.path.join(TMP, 'scripts')
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 list_test_mods():
'''
@ -142,62 +93,3 @@ def list_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

View file

@ -15,7 +15,6 @@ from __future__ import absolute_import
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 SaltSyndic as PytestSaltSyndic
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,12 +176,6 @@ 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)
break
else:
terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)

View file

@ -15,6 +15,7 @@ import logging
# Import salt libs
import salt.minion
import salt.utils.path
import salt.utils.stringutils
# Import testing libs
@ -131,7 +132,7 @@ def build_minion_opts(minion_id=None,
if item.startswith(sys.base_prefix):
path_items.remove(item)
os.environ['PATH'] = os.pathsep.join(path_items)
virtualenv_binary = salt.utils.which('virtualenv')
virtualenv_binary = salt.utils.path.which('virtualenv')
if path is not None:
# Restore previous environ PATH
os.environ['PATH'] = path

View file

@ -62,7 +62,7 @@ if sys.version_info < (2, 7):
expectedFailure,
TestSuite as __TestSuite,
skip,
skipIf,
skipIf as _skipIf,
TestResult as _TestResult,
TextTestResult as __TextTestResult
)
@ -106,7 +106,7 @@ else:
expectedFailure,
TestSuite as _TestSuite,
skip,
skipIf,
skipIf as _skipIf,
TestResult,
TextTestResult as _TextTestResult,
SkipTest,
@ -430,6 +430,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',

View file

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

View file

@ -7,7 +7,7 @@ from __future__ import absolute_import
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 @@ class Bind(object):
]
@skipIf(NO_MOCK, NO_MOCK_REASON)
@skipIf(not salt.auth.ldap.HAS_LDAP, 'Install python-ldap for this test')
class LDAPAuthTestCase(TestCase):
'''

View file

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

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