mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch 'master' into issue_49420
This commit is contained in:
commit
d3be3c0b79
690 changed files with 2012 additions and 3555 deletions
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
||||
---
|
||||
|
||||
|
|
|
@ -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``
|
||||
|
||||
|
|
|
@ -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__
|
||||
-----------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
10
noxfile.py
10
noxfile.py
|
@ -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
2
requirements/crypto.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
pycryptodome; sys.platform != 'win32'
|
||||
pycryptodomex; sys.platform == 'win32'
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
9
requirements/static/py3.4/linux-crypto.txt
Normal file
9
requirements/static/py3.4/linux-crypto.txt
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
9
requirements/static/py3.5/linux-crypto.txt
Normal file
9
requirements/static/py3.5/linux-crypto.txt
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
9
requirements/static/py3.6/linux-crypto.txt
Normal file
9
requirements/static/py3.6/linux-crypto.txt
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
9
requirements/static/py3.7/linux-crypto.txt
Normal file
9
requirements/static/py3.7/linux-crypto.txt
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -21,7 +21,6 @@ psutil
|
|||
pyopenssl
|
||||
python-gnupg
|
||||
pyvmomi
|
||||
pywin32==223
|
||||
rfc3987
|
||||
salttesting==2017.6.1
|
||||
sed
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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]))
|
||||
|
||||
|
|
|
@ -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):
|
||||
'''
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)])
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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-----
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -32,6 +32,8 @@ log = logging.getLogger(__name__)
|
|||
|
||||
class CopyTest(ShellCase, ShellCaseCommonTestsMixin):
|
||||
|
||||
_call_binary_ = 'salt-cp'
|
||||
|
||||
def test_cp_testfile(self):
|
||||
'''
|
||||
test salt-cp
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -24,6 +24,8 @@ class MatchTest(ShellCase, ShellCaseCommonTestsMixin):
|
|||
Test salt matchers
|
||||
'''
|
||||
|
||||
_call_binary_ = 'salt'
|
||||
|
||||
def test_list(self):
|
||||
'''
|
||||
test salt -L matcher
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
'''
|
||||
|
|
|
@ -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': {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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', [])
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
|
|
54
tests/support/cli_scripts.py
Normal file
54
tests/support/cli_scripts.py
Normal 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)
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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()
|
|
@ -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):
|
||||
'''
|
||||
|
|
|
@ -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
Loading…
Add table
Reference in a new issue