mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2018.3' into mac_brew
This commit is contained in:
commit
e818c752b3
16 changed files with 168 additions and 86 deletions
8
.ci/lint
8
.ci/lint
|
@ -29,13 +29,13 @@ pipeline {
|
|||
parallel {
|
||||
stage('salt linting') {
|
||||
steps {
|
||||
sh 'eval "$(pyenv init -)"; tox -e pylint-salt | tee pylint-report.xml'
|
||||
sh 'eval "$(pyenv init -)"; tox -e pylint-salt $(find salt/ -name "*.py" -exec git diff --name-only "origin/$CHANGE_TARGET" "origin/$BRANCH_NAME" setup.py {} +) | tee pylint-report.xml'
|
||||
archiveArtifacts artifacts: 'pylint-report.xml'
|
||||
}
|
||||
}
|
||||
stage('test linting') {
|
||||
steps {
|
||||
sh 'eval "$(pyenv init -)"; tox -e pylint-tests | tee pylint-report-tests.xml'
|
||||
sh 'eval "$(pyenv init -)"; tox -e pylint-tests $(find tests/ -name "*.py" -exec git diff --name-only "origin/$CHANGE_TARGET" "origin/$BRANCH_NAME" {} +) | tee pylint-report-tests.xml'
|
||||
archiveArtifacts artifacts: 'pylint-report-tests.xml'
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,9 @@ pipeline {
|
|||
parserName: 'PyLint',
|
||||
pattern: 'pylint-report*.xml'
|
||||
]],
|
||||
failedTotalAll: '1',
|
||||
failedTotalAll: '0',
|
||||
useDeltaValues: false,
|
||||
canRunOnFailed: true,
|
||||
usePreviousBuildAsReference: true
|
||||
])
|
||||
cleanWs()
|
||||
|
|
|
@ -21,11 +21,21 @@ SaltStack has its own coding style guide that informs contributors on various co
|
|||
approaches. Please review the :ref:`Salt Coding Style <coding-style>` documentation
|
||||
for information about Salt's particular coding patterns.
|
||||
|
||||
Within the :ref:`Salt Coding Style <coding-style>` documentation, there is a section
|
||||
about running Salt's ``.pylintrc`` file. SaltStack recommends running the ``.pylintrc``
|
||||
file on any files you are changing with your code contribution before submitting a
|
||||
pull request to Salt's repository. Please see the :ref:`Linting<pylint-instructions>`
|
||||
documentation for more information.
|
||||
Within the :ref:`Salt Coding Style <coding-style>` documentation, there is a
|
||||
section about running Salt's ``.testing.pylintrc`` file. SaltStack recommends
|
||||
running the ``.testing.pylintrc`` file on any files you are changing with your
|
||||
code contribution before submitting a pull request to Salt's repository. Please
|
||||
see the :ref:`Linting<pylint-instructions>` documentation for more information.
|
||||
|
||||
.. note::
|
||||
|
||||
There are two pylint files in the ``salt`` directory. One is the
|
||||
``.pylintrc`` file and the other is the ``.testing.pylintrc`` file. The
|
||||
tests that run in Jenkins against GitHub Pull Requests use
|
||||
``.testing.pylintrc``. The ``testing.pylintrc`` file is a little less
|
||||
strict than the ``.pylintrc`` and is used to make it easier for contributors
|
||||
to submit changes. The ``.pylintrc`` file can be used for linting, but the
|
||||
``testing.pylintrc`` is the source of truth when submitting pull requests.
|
||||
|
||||
|
||||
.. _github-pull-request:
|
||||
|
|
|
@ -22,21 +22,31 @@ improve Salt)!!
|
|||
Linting
|
||||
=======
|
||||
|
||||
Most Salt style conventions are codified in Salt's ``.pylintrc`` file. Salt's
|
||||
pylint file has two dependencies: pylint_ and saltpylint_. You can install
|
||||
these dependencies with ``pip``:
|
||||
Most Salt style conventions are codified in Salt's ``.testing.pylintrc`` file.
|
||||
Salt's pylint file has two dependencies: pylint_ and saltpylint_. You can
|
||||
install these dependencies with ``pip``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install pylint
|
||||
pip install saltpylint
|
||||
|
||||
The ``.pylintrc`` file is found in the root of the Salt project and can be passed
|
||||
as an argument to the pylint_ program as follows:
|
||||
The ``.testing.pylintrc`` file is found in the root of the Salt project and can
|
||||
be passed as an argument to the pylint_ program as follows:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pylint --rcfile=/path/to/salt/.pylintrc salt/dir/to/lint
|
||||
pylint --rcfile=/path/to/salt/.testing.pylintrc salt/dir/to/lint
|
||||
|
||||
.. note::
|
||||
|
||||
There are two pylint files in the ``salt`` directory. One is the
|
||||
``.pylintrc`` file and the other is the ``.testing.pylintrc`` file. The
|
||||
tests that run in Jenkins against GitHub Pull Requests use
|
||||
``.testing.pylintrc``. The ``testing.pylintrc`` file is a little less
|
||||
strict than the ``.pylintrc`` and is used to make it easier for contributors
|
||||
to submit changes. The ``.pylintrc`` file can be used for linting, but the
|
||||
``testing.pylintrc`` is the source of truth when submitting pull requests.
|
||||
|
||||
.. _pylint: http://www.pylint.org
|
||||
.. _saltpylint: https://github.com/saltstack/salt-pylint
|
||||
|
|
|
@ -80,13 +80,13 @@ def _ssh_state(chunks, st_kwargs,
|
|||
|
||||
# Read in the JSON data and return the data structure
|
||||
try:
|
||||
return salt.utils.json.loads(stdout, object_hook=salt.utils.data.encode_dict)
|
||||
return salt.utils.data.decode(salt.utils.json.loads(stdout, object_hook=salt.utils.data.encode_dict))
|
||||
except Exception as e:
|
||||
log.error("JSON Render failed for: %s\n%s", stdout, stderr)
|
||||
log.error(str(e))
|
||||
|
||||
# If for some reason the json load fails, return the stdout
|
||||
return stdout
|
||||
return salt.utils.data.decode(stdout)
|
||||
|
||||
|
||||
def _set_retcode(ret, highstate=None):
|
||||
|
|
|
@ -129,8 +129,6 @@ def serve_file(load, fnd):
|
|||
with salt.utils.files.fopen(fpath, 'rb') as fp_:
|
||||
fp_.seek(load['loc'])
|
||||
data = fp_.read(__opts__['file_buffer_size'])
|
||||
if data and six.PY3 and not salt.utils.files.is_binary(fpath):
|
||||
data = data.decode(__salt_system_encoding__)
|
||||
if gzip and data:
|
||||
data = salt.utils.gzip_util.compress(data, gzip)
|
||||
ret['gzip'] = gzip
|
||||
|
|
|
@ -200,7 +200,7 @@ def _walk(path, value, metrics, timestamp, skip):
|
|||
to a float. Defaults to `False`.
|
||||
'''
|
||||
log.trace(
|
||||
'Carbon return walking path: %s, value: %s, metrics: %s, ',
|
||||
'Carbon return walking path: %s, value: %s, metrics: %s, '
|
||||
'timestamp: %s', path, value, metrics, timestamp
|
||||
)
|
||||
if isinstance(value, collections.Mapping):
|
||||
|
|
|
@ -172,7 +172,9 @@ def init(
|
|||
start=True,
|
||||
disk='default',
|
||||
saltenv='base',
|
||||
enable_vnc=False):
|
||||
enable_vnc=False,
|
||||
seed_cmd='seed.apply',
|
||||
enable_qcow=False):
|
||||
'''
|
||||
This routine is used to create a new virtual machine. This routines takes
|
||||
a number of options to determine what the newly created virtual machine
|
||||
|
@ -194,14 +196,14 @@ def init(
|
|||
on the salt fileserver, but http, https and ftp can also be used.
|
||||
|
||||
hypervisor
|
||||
The hypervisor to use for the new virtual machine. Default is 'kvm'.
|
||||
The hypervisor to use for the new virtual machine. Default is `kvm`.
|
||||
|
||||
host
|
||||
The host to use for the new virtual machine, if this is omitted
|
||||
Salt will automatically detect what host to use.
|
||||
|
||||
seed
|
||||
Set to False to prevent Salt from seeding the new virtual machine.
|
||||
Set to `False` to prevent Salt from seeding the new virtual machine.
|
||||
|
||||
nic
|
||||
The nic profile to use, defaults to the "default" nic profile which
|
||||
|
@ -217,6 +219,17 @@ def init(
|
|||
|
||||
saltenv
|
||||
The Salt environment to use
|
||||
|
||||
enable_vnc
|
||||
Whether a VNC screen is attached to resulting VM. Default is `False`.
|
||||
|
||||
seed_cmd
|
||||
If seed is `True`, use this execution module function to seed new VM.
|
||||
Default is `seed.apply`.
|
||||
|
||||
enable_qcow
|
||||
Clone disk image as a copy-on-write qcow2 image, using downloaded
|
||||
`image` as backing file.
|
||||
'''
|
||||
__jid_event__.fire_event({'message': 'Searching for hosts'}, 'progress')
|
||||
data = query(host, quiet=True)
|
||||
|
@ -257,25 +270,29 @@ def init(
|
|||
)
|
||||
try:
|
||||
cmd_ret = client.cmd_iter(
|
||||
host,
|
||||
'virt.init',
|
||||
[
|
||||
name,
|
||||
cpu,
|
||||
mem,
|
||||
image,
|
||||
nic,
|
||||
hypervisor,
|
||||
start,
|
||||
disk,
|
||||
saltenv,
|
||||
seed,
|
||||
install,
|
||||
pub_key,
|
||||
priv_key,
|
||||
enable_vnc,
|
||||
],
|
||||
timeout=600)
|
||||
host,
|
||||
'virt.init',
|
||||
[
|
||||
name,
|
||||
cpu,
|
||||
mem
|
||||
],
|
||||
timeout=600,
|
||||
kwarg={
|
||||
'image': image,
|
||||
'nic': nic,
|
||||
'hypervisor': hypervisor,
|
||||
'start': start,
|
||||
'disk': disk,
|
||||
'saltenv': saltenv,
|
||||
'seed': seed,
|
||||
'install': install,
|
||||
'pub_key': pub_key,
|
||||
'priv_key': priv_key,
|
||||
'seed_cmd': seed_cmd,
|
||||
'enable_vnc': enable_vnc,
|
||||
'enable_qcow': enable_qcow,
|
||||
})
|
||||
except SaltClientError as client_error:
|
||||
# Fall through to ret error handling below
|
||||
print(client_error)
|
||||
|
|
|
@ -73,7 +73,7 @@ def _create_new_policy(name, rules):
|
|||
payload = {'rules': rules}
|
||||
url = "v1/sys/policy/{0}".format(name)
|
||||
response = __utils__['vault.make_request']('PUT', url, json=payload)
|
||||
if response.status_code != 204:
|
||||
if response.status_code not in [200, 204]:
|
||||
return {
|
||||
'name': name,
|
||||
'changes': {},
|
||||
|
@ -108,7 +108,7 @@ def _handle_existing_policy(name, new_rules, existing_rules):
|
|||
|
||||
url = "v1/sys/policy/{0}".format(name)
|
||||
response = __utils__['vault.make_request']('PUT', url, json=payload)
|
||||
if response.status_code != 204:
|
||||
if response.status_code not in [200, 204]:
|
||||
return {
|
||||
'name': name,
|
||||
'changes': {},
|
||||
|
|
|
@ -1534,9 +1534,9 @@ class Pygit2(GitProvider):
|
|||
|
||||
elif tag_ref in refs:
|
||||
tag_obj = self.repo.revparse_single(tag_ref)
|
||||
if not isinstance(tag_obj, pygit2.Tag):
|
||||
if not isinstance(tag_obj, pygit2.Commit):
|
||||
log.error(
|
||||
'%s does not correspond to pygit2.Tag object',
|
||||
'%s does not correspond to pygit2.Commit object',
|
||||
tag_ref
|
||||
)
|
||||
else:
|
||||
|
@ -1556,9 +1556,10 @@ class Pygit2(GitProvider):
|
|||
exc_info=True
|
||||
)
|
||||
return None
|
||||
log.debug('SHA of tag %s: %s', tgt_ref, tag_sha)
|
||||
|
||||
if head_sha != target_sha:
|
||||
if not _perform_checkout(local_ref, branch=False):
|
||||
if head_sha != tag_sha:
|
||||
if not _perform_checkout(tag_ref, branch=False):
|
||||
return None
|
||||
|
||||
# Return the relative root, if present
|
||||
|
|
3
tests/integration/files/file/base/issue-46672-a.sls
Normal file
3
tests/integration/files/file/base/issue-46672-a.sls
Normal file
|
@ -0,0 +1,3 @@
|
|||
echo1:
|
||||
cmd.run:
|
||||
- name: "echo 'This is Æ test!'"
|
|
@ -1,6 +1,6 @@
|
|||
Scene 24
|
||||
|
||||
|
||||
|
||||
OLD MAN: Ah, hee he he ha!
|
||||
ARTHUR: And this enchanter of whom you speak, he has seen the grail?
|
||||
OLD MAN: Ha ha he he he he!
|
||||
|
|
|
@ -1942,6 +1942,20 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin):
|
|||
_expected = "cmd_|-echo1_|-echo 'This is Æ test!'_|-run"
|
||||
self.assertIn(_expected, ret)
|
||||
|
||||
def test_state_sls_unicode_characters_cmd_output(self):
|
||||
'''
|
||||
test the output from running and echo command with non-ascii
|
||||
characters.
|
||||
'''
|
||||
ret = self.run_function('state.sls', ['issue-46672-a'])
|
||||
key = list(ret.keys())[0]
|
||||
log.debug('== ret %s ==', type(ret))
|
||||
_expected = 'This is Æ test!'
|
||||
if salt.utils.platform.is_windows():
|
||||
# Windows cmd.exe will mangle the output using cmd's codepage.
|
||||
_expected = "'This is ’ test!'"
|
||||
self.assertEqual(_expected, ret[key]['changes']['stdout'])
|
||||
|
||||
def tearDown(self):
|
||||
nonbase_file = os.path.join(TMP, 'nonbase_env')
|
||||
if os.path.isfile(nonbase_file):
|
||||
|
|
|
@ -30,6 +30,7 @@ from tests.support.helpers import (
|
|||
with_tempdir,
|
||||
with_tempfile,
|
||||
Webserver,
|
||||
destructiveTest
|
||||
)
|
||||
from tests.support.mixins import SaltReturnAssertsMixin
|
||||
|
||||
|
@ -3653,3 +3654,54 @@ class RemoteFileTest(ModuleCase, SaltReturnAssertsMixin):
|
|||
skip_verify=True)
|
||||
log.debug('ret = %s', ret)
|
||||
self.assertSaltTrueReturn(ret)
|
||||
|
||||
WIN_TEST_FILE = 'c:/testfile'
|
||||
|
||||
|
||||
@destructiveTest
|
||||
@skipIf(not IS_WINDOWS, 'windows test only')
|
||||
class WinFileTest(ModuleCase):
|
||||
'''
|
||||
Test for the file state on Windows
|
||||
'''
|
||||
def setUp(self):
|
||||
self.run_state('file.managed', name=WIN_TEST_FILE, makedirs=True, contents='Only a test')
|
||||
|
||||
def tearDown(self):
|
||||
self.run_state('file.absent', name=WIN_TEST_FILE)
|
||||
|
||||
def test_file_managed(self):
|
||||
'''
|
||||
Test file.managed on Windows
|
||||
'''
|
||||
self.assertTrue(self.run_state('file.exists', name=WIN_TEST_FILE))
|
||||
|
||||
def test_file_copy(self):
|
||||
'''
|
||||
Test file.copy on Windows
|
||||
'''
|
||||
ret = self.run_state('file.copy', name='c:/testfile_copy', makedirs=True, source=WIN_TEST_FILE)
|
||||
self.assertTrue(ret)
|
||||
|
||||
def test_file_comment(self):
|
||||
'''
|
||||
Test file.comment on Windows
|
||||
'''
|
||||
self.run_state('file.comment', name=WIN_TEST_FILE, regex='^Only')
|
||||
with salt.utils.files.fopen(WIN_TEST_FILE, 'r') as fp_:
|
||||
self.assertTrue(fp_.read().startswith('#Only'))
|
||||
|
||||
def test_file_replace(self):
|
||||
'''
|
||||
Test file.replace on Windows
|
||||
'''
|
||||
self.run_state('file.replace', name=WIN_TEST_FILE, pattern='test', repl='testing')
|
||||
with salt.utils.files.fopen(WIN_TEST_FILE, 'r') as fp_:
|
||||
self.assertIn('testing', fp_.read())
|
||||
|
||||
def test_file_absent(self):
|
||||
'''
|
||||
Test file.absent on Windows
|
||||
'''
|
||||
ret = self.run_state('file.absent', name=WIN_TEST_FILE)
|
||||
self.assertTrue(ret)
|
||||
|
|
|
@ -598,7 +598,7 @@ class TestSaltProgram(six.with_metaclass(TestSaltProgramMeta, TestProgram)):
|
|||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
connect = sock.bind(('localhost', port))
|
||||
except:
|
||||
except OSError:
|
||||
# these ports are already in use, use different ones
|
||||
pub_port = 4606
|
||||
ret_port = 4607
|
||||
|
|
|
@ -12,7 +12,7 @@ import tempfile
|
|||
# Import Salt Testing libs
|
||||
from tests.integration import AdaptedConfigurationTestCaseMixin
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.paths import FILES, TMP, TMP_STATE_TREE
|
||||
from tests.support.paths import BASE_FILES, TMP, TMP_STATE_TREE
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON
|
||||
|
||||
|
@ -20,6 +20,7 @@ from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON
|
|||
import salt.fileserver.roots as roots
|
||||
import salt.fileclient
|
||||
import salt.utils.files
|
||||
import salt.utils.hashutils
|
||||
import salt.utils.platform
|
||||
|
||||
try:
|
||||
|
@ -63,13 +64,11 @@ class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMix
|
|||
else:
|
||||
cls.test_symlink_list_file_roots = None
|
||||
cls.tmp_dir = tempfile.mkdtemp(dir=TMP)
|
||||
full_path_to_file = os.path.join(FILES, 'file', 'base', 'testfile')
|
||||
full_path_to_file = os.path.join(BASE_FILES, 'testfile')
|
||||
with salt.utils.files.fopen(full_path_to_file, 'rb') as s_fp:
|
||||
with salt.utils.files.fopen(os.path.join(cls.tmp_dir, 'testfile'), 'wb') as d_fp:
|
||||
for line in s_fp:
|
||||
d_fp.write(
|
||||
line.rstrip(b'\n').rstrip(b'\r') + os.linesep.encode('utf-8')
|
||||
)
|
||||
d_fp.write(line.rstrip(b'\n').rstrip(b'\r') + b'\n')
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
|
@ -95,7 +94,7 @@ class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMix
|
|||
ret = roots.find_file('testfile')
|
||||
self.assertEqual('testfile', ret['rel'])
|
||||
|
||||
full_path_to_file = os.path.join(FILES, 'file', 'base', 'testfile')
|
||||
full_path_to_file = os.path.join(BASE_FILES, 'testfile')
|
||||
self.assertEqual(full_path_to_file, ret['path'])
|
||||
|
||||
def test_serve_file(self):
|
||||
|
@ -108,34 +107,9 @@ class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMix
|
|||
'rel': 'testfile'}
|
||||
ret = roots.serve_file(load, fnd)
|
||||
|
||||
data = 'Scene 24\n\n \n OLD MAN: Ah, hee he he ha!\n ' \
|
||||
'ARTHUR: And this enchanter of whom you speak, he ' \
|
||||
'has seen the grail?\n OLD MAN: Ha ha he he he ' \
|
||||
'he!\n ARTHUR: Where does he live? Old man, where ' \
|
||||
'does he live?\n OLD MAN: He knows of a cave, a ' \
|
||||
'cave which no man has entered.\n ARTHUR: And the ' \
|
||||
'Grail... The Grail is there?\n OLD MAN: Very much ' \
|
||||
'danger, for beyond the cave lies the Gorge\n ' \
|
||||
'of Eternal Peril, which no man has ever crossed.\n ' \
|
||||
'ARTHUR: But the Grail! Where is the Grail!?\n ' \
|
||||
'OLD MAN: Seek you the Bridge of Death.\n ARTHUR: ' \
|
||||
'The Bridge of Death, which leads to the Grail?\n ' \
|
||||
'OLD MAN: Hee hee ha ha!\n\n'
|
||||
if salt.utils.platform.is_windows():
|
||||
data = 'Scene 24\r\n\r\n \r\n OLD MAN: Ah, hee he he ' \
|
||||
'ha!\r\n ARTHUR: And this enchanter of whom you ' \
|
||||
'speak, he has seen the grail?\r\n OLD MAN: Ha ha ' \
|
||||
'he he he he!\r\n ARTHUR: Where does he live? Old ' \
|
||||
'man, where does he live?\r\n OLD MAN: He knows of ' \
|
||||
'a cave, a cave which no man has entered.\r\n ' \
|
||||
'ARTHUR: And the Grail... The Grail is there?\r\n ' \
|
||||
'OLD MAN: Very much danger, for beyond the cave lies ' \
|
||||
'the Gorge\r\n of Eternal Peril, which no man ' \
|
||||
'has ever crossed.\r\n ARTHUR: But the Grail! ' \
|
||||
'Where is the Grail!?\r\n OLD MAN: Seek you the ' \
|
||||
'Bridge of Death.\r\n ARTHUR: The Bridge of Death, ' \
|
||||
'which leads to the Grail?\r\n OLD MAN: Hee hee ha ' \
|
||||
'ha!\r\n\r\n'
|
||||
with salt.utils.files.fopen(
|
||||
os.path.join(BASE_FILES, 'testfile'), 'rb') as fp_:
|
||||
data = fp_.read()
|
||||
|
||||
self.assertDictEqual(
|
||||
ret,
|
||||
|
@ -163,9 +137,9 @@ class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMix
|
|||
|
||||
# Hashes are different in Windows. May be how git translates line
|
||||
# endings
|
||||
hsum = 'baba5791276eb99a7cc498fb1acfbc3b4bd96d24cfe984b4ed6b5be2418731df'
|
||||
if salt.utils.platform.is_windows():
|
||||
hsum = '754aa260e1f3e70f43aaf92149c7d1bad37f708c53304c37660e628d7553f687'
|
||||
with salt.utils.files.fopen(
|
||||
os.path.join(BASE_FILES, 'testfile'), 'rb') as fp_:
|
||||
hsum = salt.utils.hashutils.sha256_digest(fp_.read())
|
||||
|
||||
self.assertDictEqual(
|
||||
ret,
|
||||
|
|
|
@ -43,6 +43,7 @@ integration.states.test_pip_state
|
|||
integration.states.test_pkg
|
||||
integration.states.test_reg
|
||||
integration.states.test_renderers
|
||||
integration.states.test_file
|
||||
integration.states.test_user
|
||||
integration.utils.testprogram
|
||||
integration.wheel.test_client
|
||||
|
|
Loading…
Add table
Reference in a new issue