From c5035118bf0c4857d53e1a9cf6de22e6466e953b Mon Sep 17 00:00:00 2001 From: Ch3LL Date: Thu, 7 Jul 2016 14:42:27 -0600 Subject: [PATCH 001/100] add skip_verify option to archive.extracted --- salt/states/archive.py | 9 ++++ tests/integration/states/archive.py | 70 +++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 tests/integration/states/archive.py diff --git a/salt/states/archive.py b/salt/states/archive.py index 8485c97240..0ddb526072 100644 --- a/salt/states/archive.py +++ b/salt/states/archive.py @@ -95,6 +95,7 @@ def extracted(name, if_missing=None, keep=False, trim_output=False, + skip_verify=False, source_hash_update=None): ''' .. versionadded:: 2014.1.0 @@ -174,6 +175,13 @@ def extracted(name, .. versionadded:: 2016.3.0 + skip_verify:False + If ``True``, hash verification of remote file sources (``http://``, + ``https://``, ``ftp://``) will be skipped, and the ``source_hash`` + argument will be ignored. + + .. versionadded:: 2016.3.2 + archive_format ``tar``, ``zip`` or ``rar`` @@ -324,6 +332,7 @@ def extracted(name, source=source, source_hash=source_hash, makedirs=True, + skip_verify=skip_verify, saltenv=__env__) log.debug('file.managed: {0}'.format(file_result)) # get value of first key diff --git a/tests/integration/states/archive.py b/tests/integration/states/archive.py new file mode 100644 index 0000000000..7a60370cd9 --- /dev/null +++ b/tests/integration/states/archive.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +''' +Tests for the file state +''' +# Import python libs +from __future__ import absolute_import +import os +import shutil + +# Import Salt Testing libs +from salttesting.helpers import ensure_in_syspath +ensure_in_syspath('../../') + +# Import salt libs +import integration +import salt.utils + +STATE_DIR = os.path.join(integration.FILES, 'file', 'base') +ARCHIVE_DIR = '/tmp/archive/' +ARCHIVE_TAR_SOURCE = 'https://github.com/downloads/Graylog2/'\ + 'graylog2-server/graylog2-server-0.9.6p1.tar.gz' +UNTAR_FILE = ARCHIVE_DIR + 'graylog2-server-0.9.6p1/README' +ARCHIVE_TAR_HASH = 'md5=499ae16dcae71eeb7c3a30c75ea7a1a6' + + +class ArchiveTest(integration.ModuleCase, + integration.SaltReturnAssertsMixIn): + ''' + Validate the archive state + ''' + def _check_ext_remove(self, dir, file): + ''' + function to check if file was extracted + and remove the directory. + ''' + + #check to see if it extracted + check_dir = os.path.isfile(file) + self.assertTrue(check_dir) + + #wipe away dir. Can't do this in teardown + #because it needs to be wiped before each test + shutil.rmtree(dir) + + def test_archive_extracted_skip_verify(self): + ''' + test archive.extracted with skip_verify + ''' + ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, + source=ARCHIVE_TAR_SOURCE, archive_format='tar', + skip_verify=True) + self.assertSaltTrueReturn(ret) + + self._check_ext_remove(ARCHIVE_DIR, UNTAR_FILE) + + def test_archive_extracted_with_source_hash(self): + ''' + test archive.extracted without skip_verify + ''' + ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, + source=ARCHIVE_TAR_SOURCE, archive_format='tar', + source_hash=ARCHIVE_TAR_HASH) + self.assertSaltTrueReturn(ret) + + self._check_ext_remove(ARCHIVE_DIR, UNTAR_FILE) + + +if __name__ == '__main__': + from integration import run_tests + run_tests(ArchiveTest) From 617f5680e440ca2f1fa9b9bf6503236a20ed9d32 Mon Sep 17 00:00:00 2001 From: Ch3LL Date: Fri, 22 Jul 2016 17:51:41 -0600 Subject: [PATCH 002/100] add windows path and add custom tar --- salt/states/archive.py | 2 +- .../integration/files/file/base/custom.tar.gz | Bin 0 -> 152 bytes tests/integration/states/archive.py | 18 +++++++++++++++--- 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 tests/integration/files/file/base/custom.tar.gz diff --git a/salt/states/archive.py b/salt/states/archive.py index 0ddb526072..9e05687b75 100644 --- a/salt/states/archive.py +++ b/salt/states/archive.py @@ -175,7 +175,7 @@ def extracted(name, .. versionadded:: 2016.3.0 - skip_verify:False + skip_verify:False If ``True``, hash verification of remote file sources (``http://``, ``https://``, ``ftp://``) will be skipped, and the ``source_hash`` argument will be ignored. diff --git a/tests/integration/files/file/base/custom.tar.gz b/tests/integration/files/file/base/custom.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..584852716c8ead74cc1a9a88fed42eb2c7e7d22e GIT binary patch literal 152 zcmb2|=3tn;W>Po<^V^Gixtt7n+8)Muy+5F~?5$3PoA`T&T^2Hee|FYa2RMo!U3mE5 z_eiEpMb7xW^Yo7&cB<;~)y!NKHEG{9^+4@+CpXRX4%vE*Yvt=O{Tc!({ij{R|om3;<=J BMb-cS literal 0 HcmV?d00001 diff --git a/tests/integration/states/archive.py b/tests/integration/states/archive.py index 7a60370cd9..c573edbc79 100644 --- a/tests/integration/states/archive.py +++ b/tests/integration/states/archive.py @@ -16,7 +16,17 @@ import integration import salt.utils STATE_DIR = os.path.join(integration.FILES, 'file', 'base') -ARCHIVE_DIR = '/tmp/archive/' +if salt.utils.is_windows(): + ARCHIVE_DIR = os.path.join("c:/", "tmp") +else: + ARCHIVE_DIR = '/tmp/archive/' + +#local tar file +LOCAL_ARCHIVE_TAR_SOURCE = 'salt://custom.tar.gz' +LOCAL_UNTAR_FILE = os.path.join(ARCHIVE_DIR, 'custom', 'README') + +#external sources. Only external sources verify source_hash. +#Therefore need to keep to verify source_hash test ARCHIVE_TAR_SOURCE = 'https://github.com/downloads/Graylog2/'\ 'graylog2-server/graylog2-server-0.9.6p1.tar.gz' UNTAR_FILE = ARCHIVE_DIR + 'graylog2-server-0.9.6p1/README' @@ -47,15 +57,17 @@ class ArchiveTest(integration.ModuleCase, test archive.extracted with skip_verify ''' ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=ARCHIVE_TAR_SOURCE, archive_format='tar', + source=LOCAL_ARCHIVE_TAR_SOURCE, archive_format='tar', skip_verify=True) self.assertSaltTrueReturn(ret) - self._check_ext_remove(ARCHIVE_DIR, UNTAR_FILE) + self._check_ext_remove(ARCHIVE_DIR, LOCAL_UNTAR_FILE) def test_archive_extracted_with_source_hash(self): ''' test archive.extracted without skip_verify + only external resources work to check to + ensure source_hash is verified correctly ''' ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, source=ARCHIVE_TAR_SOURCE, archive_format='tar', From 38203e3d2c50ae77eca940d577df71576262ca2a Mon Sep 17 00:00:00 2001 From: Ch3LL Date: Tue, 30 Aug 2016 15:43:28 -0600 Subject: [PATCH 003/100] add tornado web app to serve up static file for test --- salt/states/archive.py | 2 +- tests/integration/states/archive.py | 64 +++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/salt/states/archive.py b/salt/states/archive.py index 9e05687b75..97dcc4b60a 100644 --- a/salt/states/archive.py +++ b/salt/states/archive.py @@ -180,7 +180,7 @@ def extracted(name, ``https://``, ``ftp://``) will be skipped, and the ``source_hash`` argument will be ignored. - .. versionadded:: 2016.3.2 + .. versionadded:: 2016.3.4 archive_format ``tar``, ``zip`` or ``rar`` diff --git a/tests/integration/states/archive.py b/tests/integration/states/archive.py index c573edbc79..d5f16288d3 100644 --- a/tests/integration/states/archive.py +++ b/tests/integration/states/archive.py @@ -1,13 +1,17 @@ # -*- coding: utf-8 -*- ''' -Tests for the file state +Tests for the archive state ''' # Import python libs from __future__ import absolute_import import os import shutil +import threading +import tornado.ioloop +import tornado.web # Import Salt Testing libs +from salttesting import TestCase from salttesting.helpers import ensure_in_syspath ensure_in_syspath('../../') @@ -21,19 +25,44 @@ if salt.utils.is_windows(): else: ARCHIVE_DIR = '/tmp/archive/' -#local tar file -LOCAL_ARCHIVE_TAR_SOURCE = 'salt://custom.tar.gz' -LOCAL_UNTAR_FILE = os.path.join(ARCHIVE_DIR, 'custom', 'README') - -#external sources. Only external sources verify source_hash. -#Therefore need to keep to verify source_hash test -ARCHIVE_TAR_SOURCE = 'https://github.com/downloads/Graylog2/'\ - 'graylog2-server/graylog2-server-0.9.6p1.tar.gz' -UNTAR_FILE = ARCHIVE_DIR + 'graylog2-server-0.9.6p1/README' -ARCHIVE_TAR_HASH = 'md5=499ae16dcae71eeb7c3a30c75ea7a1a6' +PORT = 9999 +ARCHIVE_TAR_SOURCE = 'http://localhost:{0}/custom.tar.gz'.format(PORT) +UNTAR_FILE = ARCHIVE_DIR + 'custom/README' +ARCHIVE_TAR_HASH = 'md5=7643861ac07c30fe7d2310e9f25ca514' +STATE_DIR = os.path.join(integration.FILES, 'file', 'base') -class ArchiveTest(integration.ModuleCase, +class SetupWebServer(TestCase): + ''' + Setup and Teardown of Web Server + Only need to set this up once not + before all tests + ''' + @classmethod + def webserver(cls): + ''' + method to start tornado + static web app + ''' + application = tornado.web.Application([(r"/(.*)", tornado.web.StaticFileHandler, + {"path": STATE_DIR})]) + application.listen(PORT) + tornado.ioloop.IOLoop.instance().start() + + @classmethod + def setUpClass(cls): + cls.server_thread = threading.Thread(target=cls.webserver) + cls.server_thread.daemon = True + cls.server_thread.start() + + @classmethod + def tearDownClass(cls): + tornado.ioloop.IOLoop.instance().stop() + cls.server_thread.join() + + +class ArchiveTest(SetupWebServer, + integration.ModuleCase, integration.SaltReturnAssertsMixIn): ''' Validate the archive state @@ -43,13 +72,12 @@ class ArchiveTest(integration.ModuleCase, function to check if file was extracted and remove the directory. ''' - - #check to see if it extracted + # check to see if it extracted check_dir = os.path.isfile(file) self.assertTrue(check_dir) - #wipe away dir. Can't do this in teardown - #because it needs to be wiped before each test + # wipe away dir. Can't do this in teardown + # because it needs to be wiped before each test shutil.rmtree(dir) def test_archive_extracted_skip_verify(self): @@ -57,11 +85,11 @@ class ArchiveTest(integration.ModuleCase, test archive.extracted with skip_verify ''' ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=LOCAL_ARCHIVE_TAR_SOURCE, archive_format='tar', + source=ARCHIVE_TAR_SOURCE, archive_format='tar', skip_verify=True) self.assertSaltTrueReturn(ret) - self._check_ext_remove(ARCHIVE_DIR, LOCAL_UNTAR_FILE) + self._check_ext_remove(ARCHIVE_DIR, UNTAR_FILE) def test_archive_extracted_with_source_hash(self): ''' From e0978220f71097b7fd2189b6d90a16a0c33eb485 Mon Sep 17 00:00:00 2001 From: Morgan Willcock Date: Wed, 31 Aug 2016 20:34:49 +0100 Subject: [PATCH 004/100] win_pkg: allow minion upgrade when using batteries Don't prevent the Scheduled Task option from doing package operations, when the system is running on batteries --- salt/modules/win_pkg.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/salt/modules/win_pkg.py b/salt/modules/win_pkg.py index 18a6f1e5c4..0ad4c22b92 100644 --- a/salt/modules/win_pkg.py +++ b/salt/modules/win_pkg.py @@ -694,7 +694,9 @@ def install(name=None, refresh=False, pkgs=None, saltenv='base', **kwargs): start_in=cache_path, trigger_type='Once', start_date='1975-01-01', - start_time='01:00') + start_time='01:00', + ac_only=False, + stop_if_on_batteries=False) # Run Scheduled Task __salt__['task.run_wait'](name='update-salt-software') else: @@ -953,7 +955,9 @@ def remove(name=None, pkgs=None, version=None, **kwargs): start_in=cache_path, trigger_type='Once', start_date='1975-01-01', - start_time='01:00') + start_time='01:00', + ac_only=False, + stop_if_on_batteries=False) # Run Scheduled Task __salt__['task.run_wait'](name='update-salt-software') else: From 108f9470f2ec5d3f735acc18cb397d60ca665610 Mon Sep 17 00:00:00 2001 From: Morgan Willcock Date: Wed, 31 Aug 2016 21:02:02 +0100 Subject: [PATCH 005/100] win_pkg: report failure for failed launch of Scheduled Task If the Scheduled Task fails to launch, log this and report the failed status. Don't check for success as the Task Scheduler doesn't give return codes for actions it ran, and only the minion upgrade should be using this (so successful installation or removal would never be reported anyway). --- salt/modules/win_pkg.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/salt/modules/win_pkg.py b/salt/modules/win_pkg.py index 0ad4c22b92..c91bbcd00a 100644 --- a/salt/modules/win_pkg.py +++ b/salt/modules/win_pkg.py @@ -698,7 +698,10 @@ def install(name=None, refresh=False, pkgs=None, saltenv='base', **kwargs): ac_only=False, stop_if_on_batteries=False) # Run Scheduled Task - __salt__['task.run_wait'](name='update-salt-software') + if not __salt__['task.run_wait'](name='update-salt-software'): + log.error('Failed to install {0}'.format(pkg_name)) + log.error('Scheduled Task failed to run') + ret[pkg_name] = {'install status': 'failed'} else: # Build the install command cmd = [] @@ -959,7 +962,10 @@ def remove(name=None, pkgs=None, version=None, **kwargs): ac_only=False, stop_if_on_batteries=False) # Run Scheduled Task - __salt__['task.run_wait'](name='update-salt-software') + if not __salt__['task.run_wait'](name='update-salt-software'): + log.error('Failed to remove {0}'.format(target)) + log.error('Scheduled Task failed to run') + ret[target] = {'uninstall status': 'failed'} else: # Build the install command cmd = [] From 778ae9a9ffdf551862c993a3d8729a8809cf2a23 Mon Sep 17 00:00:00 2001 From: Dmitry Kuzmenko Date: Thu, 1 Sep 2016 11:57:48 +0300 Subject: [PATCH 006/100] Update auth data on reauth. If minion reauth in a subprocess, like scheduler jobs, update the auth data in the main process by sending and handling a local event. --- salt/crypt.py | 6 +++++- salt/minion.py | 6 ++++++ salt/utils/event.py | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/salt/crypt.py b/salt/crypt.py index 5a7492ab4f..34c97a92ab 100644 --- a/salt/crypt.py +++ b/salt/crypt.py @@ -469,10 +469,14 @@ class AsyncAuth(object): error = SaltClientError('Attempt to authenticate with the salt master failed') self._authenticate_future.set_exception(error) else: - AsyncAuth.creds_map[self.__key(self.opts)] = creds + key = self.__key(self.opts) + AsyncAuth.creds_map[key] = creds self._creds = creds self._crypticle = Crypticle(self.opts, creds['aes']) self._authenticate_future.set_result(True) # mark the sign-in as complete + # Notify the bus about creds change + event = salt.utils.event.get_event(self.opts.get('__role'), opts=self.opts, listen=False) + event.fire_event({'key': key, 'creds': creds}, salt.utils.event.tagify(prefix='auth', suffix='creds')) @tornado.gen.coroutine def sign_in(self, timeout=60, safe=True, tries=1, channel=None): diff --git a/salt/minion.py b/salt/minion.py index e65251b2c2..51696c19de 100644 --- a/salt/minion.py +++ b/salt/minion.py @@ -1659,6 +1659,12 @@ class Minion(MinionBase): tag, data = salt.utils.event.MinionEvent.unpack(package) log.debug('Forwarding salt error event tag={tag}'.format(tag=tag)) self._fire_master(data, tag) + elif package.startswith('salt/auth/creds'): + tag, data = salt.utils.event.MinionEvent.unpack(package) + key = tuple(data['key']) + log.debug('Updating auth data for {0}: {1} -> {2}'.format( + key, salt.crypt.AsyncAuth.creds_map.get(key), data['creds'])) + salt.crypt.AsyncAuth.creds_map[tuple(data['key'])] = data['creds'] def _fallback_cleanups(self): ''' diff --git a/salt/utils/event.py b/salt/utils/event.py index a8df3d62c4..fd6607f380 100644 --- a/salt/utils/event.py +++ b/salt/utils/event.py @@ -581,7 +581,7 @@ class SaltEvent(object): self.opts['max_event_size'], is_msgpacked=True, ) - log.debug('Sending event - data = {0}'.format(data)) + log.debug('Sending event: tag = {0}; data = {1}'.format(tag, data)) event = '{0}{1}{2}'.format(tag, tagend, serialized_data) try: self.push.send(salt.utils.to_bytes(event, 'utf-8')) From 6569267afc6e0ff9b1b8c4f27124e9f01d392f13 Mon Sep 17 00:00:00 2001 From: kstreee Date: Fri, 2 Sep 2016 06:35:49 +0000 Subject: [PATCH 007/100] Fixes a bug that Ctrl-c not working on Salt CLI. --- salt/scripts.py | 135 +++++++++++++++--------------------------------- 1 file changed, 42 insertions(+), 93 deletions(-) diff --git a/salt/scripts.py b/salt/scripts.py index 2f1d1da774..d716cd7695 100644 --- a/salt/scripts.py +++ b/salt/scripts.py @@ -8,7 +8,9 @@ from __future__ import absolute_import, print_function import os import sys import time +import signal import logging +import functools import threading import traceback from random import randint @@ -38,6 +40,29 @@ def _handle_interrupt(exc, original_exc, hardfail=False, trace=''): raise exc +def _handle_signals(client, signum, sigframe): + trace = traceback.format_exc() + try: + hardcrash = client.options.hard_crash + except (AttributeError, KeyError): + hardcrash = False + _handle_interrupt( + SystemExit('\nExiting gracefully on Ctrl-c'), + Exception('\nExiting with hard crash Ctrl-c'), + hardcrash, trace=trace) + + +def _install_signal_handlers(client): + # Install the SIGINT/SIGTERM handlers if not done so far + if signal.getsignal(signal.SIGINT) is signal.SIG_DFL: + # No custom signal handling was added, install our own + signal.signal(signal.SIGINT, functools.partial(_handle_signals, client)) + + if signal.getsignal(signal.SIGTERM) is signal.SIG_DFL: + # No custom signal handling was added, install our own + signal.signal(signal.SIGINT, functools.partial(_handle_signals, client)) + + def salt_master(): ''' Start the salt master. @@ -101,7 +126,6 @@ def salt_minion(): Auto restart minion on error. ''' import signal - import functools import salt.utils.process salt.utils.process.notify_systemd() @@ -298,20 +322,10 @@ def salt_key(): Manage the authentication keys with salt-key. ''' import salt.cli.key - client = None try: client = salt.cli.key.SaltKey() + _install_signal_handlers(client) client.run() - except KeyboardInterrupt as err: - trace = traceback.format_exc() - try: - hardcrash = client.options.hard_crash - except (AttributeError, KeyError): - hardcrash = False - _handle_interrupt( - SystemExit('\nExiting gracefully on Ctrl-c'), - err, - hardcrash, trace=trace) except Exception as err: sys.stderr.write("Error: {0}\n".format(err.message)) @@ -322,20 +336,9 @@ def salt_cp(): master. ''' import salt.cli.cp - client = None - try: - client = salt.cli.cp.SaltCPCli() - client.run() - except KeyboardInterrupt as err: - trace = traceback.format_exc() - try: - hardcrash = client.options.hard_crash - except (AttributeError, KeyError): - hardcrash = False - _handle_interrupt( - SystemExit('\nExiting gracefully on Ctrl-c'), - err, - hardcrash, trace=trace) + client = salt.cli.cp.SaltCPCli() + _install_signal_handlers(client) + client.run() def salt_call(): @@ -346,20 +349,9 @@ def salt_call(): import salt.cli.call if '' in sys.path: sys.path.remove('') - client = None - try: - client = salt.cli.call.SaltCall() - client.run() - except KeyboardInterrupt as err: - trace = traceback.format_exc() - try: - hardcrash = client.options.hard_crash - except (AttributeError, KeyError): - hardcrash = False - _handle_interrupt( - SystemExit('\nExiting gracefully on Ctrl-c'), - err, - hardcrash, trace=trace) + client = salt.cli.call.SaltCall() + _install_signal_handlers(client) + client.run() def salt_run(): @@ -369,20 +361,9 @@ def salt_run(): import salt.cli.run if '' in sys.path: sys.path.remove('') - client = None - try: - client = salt.cli.run.SaltRun() - client.run() - except KeyboardInterrupt as err: - trace = traceback.format_exc() - try: - hardcrash = client.options.hard_crash - except (AttributeError, KeyError): - hardcrash = False - _handle_interrupt( - SystemExit('\nExiting gracefully on Ctrl-c'), - err, - hardcrash, trace=trace) + client = salt.cli.run.SaltRun() + _install_signal_handlers(client) + client.run() def salt_ssh(): @@ -392,20 +373,10 @@ def salt_ssh(): import salt.cli.ssh if '' in sys.path: sys.path.remove('') - client = None try: client = salt.cli.ssh.SaltSSH() + _install_signal_handlers(client) client.run() - except KeyboardInterrupt as err: - trace = traceback.format_exc() - try: - hardcrash = client.options.hard_crash - except (AttributeError, KeyError): - hardcrash = False - _handle_interrupt( - SystemExit('\nExiting gracefully on Ctrl-c'), - err, - hardcrash, trace=trace) except SaltClientError as err: trace = traceback.format_exc() try: @@ -436,20 +407,9 @@ def salt_cloud(): print('salt-cloud is not available in this system') sys.exit(salt.defaults.exitcodes.EX_UNAVAILABLE) - client = None - try: - client = salt.cloud.cli.SaltCloud() - client.run() - except KeyboardInterrupt as err: - trace = traceback.format_exc() - try: - hardcrash = client.options.hard_crash - except (AttributeError, KeyError): - hardcrash = False - _handle_interrupt( - SystemExit('\nExiting gracefully on Ctrl-c'), - err, - hardcrash, trace=trace) + client = salt.cloud.cli.SaltCloud() + _install_signal_handlers(client) + client.run() def salt_api(): @@ -472,20 +432,9 @@ def salt_main(): import salt.cli.salt if '' in sys.path: sys.path.remove('') - client = None - try: - client = salt.cli.salt.SaltCMD() - client.run() - except KeyboardInterrupt as err: - trace = traceback.format_exc() - try: - hardcrash = client.options.hard_crash - except (AttributeError, KeyError): - hardcrash = False - _handle_interrupt( - SystemExit('\nExiting gracefully on Ctrl-c'), - err, - hardcrash, trace=trace) + client = salt.cli.salt.SaltCMD() + _install_signal_handlers(client) + client.run() def salt_spm(): From b5fe6100ee156842047f273f315f4db00cbebf6c Mon Sep 17 00:00:00 2001 From: Eric Jackson Date: Tue, 30 Aug 2016 15:47:07 -0400 Subject: [PATCH 008/100] Check for single quote before splitting on single quote Signed-off-by: Eric Jackson --- salt/modules/zypper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py index 3348d0c3a9..53c80ba640 100644 --- a/salt/modules/zypper.py +++ b/salt/modules/zypper.py @@ -866,11 +866,11 @@ def refresh_db(): for line in out.splitlines(): if not line: continue - if line.strip().startswith('Repository'): + if line.strip().startswith('Repository') and '\'' in line: key = line.split('\'')[1].strip() if 'is up to date' in line: ret[key] = False - elif line.strip().startswith('Building'): + elif line.strip().startswith('Building') and '\'' in line: key = line.split('\'')[1].strip() if 'done' in line: ret[key] = True From 8c05d2aac593344cab3096ca98d8fe841adffe1b Mon Sep 17 00:00:00 2001 From: rallytime Date: Thu, 1 Sep 2016 11:30:14 -0600 Subject: [PATCH 009/100] Lint for #35916 Merges #35916 --- salt/modules/zypper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py index 53c80ba640..3d305ae6f5 100644 --- a/salt/modules/zypper.py +++ b/salt/modules/zypper.py @@ -870,7 +870,7 @@ def refresh_db(): key = line.split('\'')[1].strip() if 'is up to date' in line: ret[key] = False - elif line.strip().startswith('Building') and '\'' in line: + elif line.strip().startswith('Building') and '\'' in line: key = line.split('\'')[1].strip() if 'done' in line: ret[key] = True From 8b4f46fbd0c566589c79e5e5889c3f6027111b32 Mon Sep 17 00:00:00 2001 From: Eric Jackson Date: Tue, 30 Aug 2016 15:47:07 -0400 Subject: [PATCH 010/100] Check for single quote before splitting on single quote Signed-off-by: Eric Jackson --- salt/modules/zypper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py index c243c48523..35df6ba543 100644 --- a/salt/modules/zypper.py +++ b/salt/modules/zypper.py @@ -871,11 +871,11 @@ def refresh_db(): for line in out.splitlines(): if not line: continue - if line.strip().startswith('Repository'): + if line.strip().startswith('Repository') and '\'' in line: key = line.split('\'')[1].strip() if 'is up to date' in line: ret[key] = False - elif line.strip().startswith('Building'): + elif line.strip().startswith('Building') and '\'' in line: key = line.split('\'')[1].strip() if 'done' in line: ret[key] = True From 1b2abeabd1a23a8260bf91ede206f5213ae8e81e Mon Sep 17 00:00:00 2001 From: rallytime Date: Thu, 1 Sep 2016 11:30:14 -0600 Subject: [PATCH 011/100] Lint for #35916 Merges #35916 --- salt/modules/zypper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/zypper.py b/salt/modules/zypper.py index 35df6ba543..15e6260a13 100644 --- a/salt/modules/zypper.py +++ b/salt/modules/zypper.py @@ -875,7 +875,7 @@ def refresh_db(): key = line.split('\'')[1].strip() if 'is up to date' in line: ret[key] = False - elif line.strip().startswith('Building') and '\'' in line: + elif line.strip().startswith('Building') and '\'' in line: key = line.split('\'')[1].strip() if 'done' in line: ret[key] = True From 20a361d1f30560c6e20d3b5dd5fe2f7893f1e961 Mon Sep 17 00:00:00 2001 From: Seth House Date: Fri, 2 Sep 2016 14:16:00 -0600 Subject: [PATCH 012/100] Add include_* kwargs to the *_dict key functions (#36030) --- salt/wheel/key.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/salt/wheel/key.py b/salt/wheel/key.py index 68f1cc4aaf..09c069064d 100644 --- a/salt/wheel/key.py +++ b/salt/wheel/key.py @@ -41,7 +41,7 @@ def accept(match, include_rejected=False, include_denied=False): return skey.accept(match, include_rejected=include_rejected, include_denied=include_denied) -def accept_dict(match): +def accept_dict(match, include_rejected=False, include_denied=False): ''' Accept keys based on a dict of keys @@ -59,7 +59,9 @@ def accept_dict(match): } ''' skey = salt.key.Key(__opts__) - return skey.accept(match_dict=match) + return skey.accept(match_dict=match, + include_rejected=include_rejected, + include_denied=include_denied) def delete(match): @@ -86,12 +88,14 @@ def reject(match, include_accepted=False, include_denied=False): return skey.reject(match, include_accepted=include_accepted, include_denied=include_denied) -def reject_dict(match): +def reject_dict(match, include_accepted=False, include_denied=False): ''' Reject keys based on a dict of keys ''' skey = salt.key.Key(__opts__) - return skey.reject(match_dict=match) + return skey.reject(match_dict=match, + include_accepted=include_accepted, + include_denied=include_denied) def key_str(match): From b9fc51a1727a3b434291acdd2b7d78692731431f Mon Sep 17 00:00:00 2001 From: Thomas S Hatch Date: Fri, 2 Sep 2016 14:30:39 -0600 Subject: [PATCH 013/100] Fix error when profiling is turned on and minions don't return (#36028) * Fix error when profiling is turned on adn minions don't return * Another try --- salt/output/highstate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/salt/output/highstate.py b/salt/output/highstate.py index 5a1ac6c8b2..6c74eb053c 100644 --- a/salt/output/highstate.py +++ b/salt/output/highstate.py @@ -296,7 +296,7 @@ def _format_host(host, data): u' {tcolor} Result: {ret[result]!s}{colors[ENDC]}', u' {tcolor} Comment: {comment}{colors[ENDC]}', ] - if __opts__.get('state_output_profile', True): + if __opts__.get('state_output_profile', True) and 'start_time' in ret: state_lines.extend([ u' {tcolor} Started: {ret[start_time]!s}{colors[ENDC]}', u' {tcolor}Duration: {ret[duration]!s}{colors[ENDC]}', @@ -533,7 +533,7 @@ def _format_terse(tcolor, comps, ret, colors, tabular): c=colors, w='\n'.join(ret['warnings']) ) fmt_string += u'{0}' - if __opts__.get('state_output_profile', True): + if __opts__.get('state_output_profile', True) and 'start_time' in ret: fmt_string += u'{6[start_time]!s} [{6[duration]!s} ms] ' fmt_string += u'{2:>10}.{3:<10} {4:7} Name: {1}{5}' elif isinstance(tabular, str): @@ -545,7 +545,7 @@ def _format_terse(tcolor, comps, ret, colors, tabular): c=colors, w='\n'.join(ret['warnings']) ) fmt_string += u' {0} Name: {1} - Function: {2}.{3} - Result: {4}' - if __opts__.get('state_output_profile', True): + if __opts__.get('state_output_profile', True) and 'start_time' in ret: fmt_string += u' Started: - {6[start_time]!s} Duration: {6[duration]!s} ms' fmt_string += u'{5}' From b652271ddcbdbd05b6e2deeae68b8b9c60be9255 Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Fri, 2 Sep 2016 14:36:41 -0600 Subject: [PATCH 014/100] Fix type error in networkfbsd osmajorrelease compare (#36016) --- salt/modules/network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/network.py b/salt/modules/network.py index 284e40408e..3b3f91f79f 100644 --- a/salt/modules/network.py +++ b/salt/modules/network.py @@ -385,7 +385,7 @@ def _netstat_route_freebsd(): out = __salt__['cmd.run'](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - if __grains__['os'] == 'FreeBSD' and __grains__.get('osmajorrelease', 0) < 10: + if __grains__['os'] == 'FreeBSD' and int(__grains__.get('osmajorrelease', 0)) < 10: ret.append({ 'addr_family': 'inet', 'destination': comps[0], From 95591c2fe6690798e66a24ce0bdd8b9d9ea95e6a Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Fri, 2 Sep 2016 14:37:04 -0600 Subject: [PATCH 015/100] Add documentation about salt_interface to EC2 docs (#36015) Fixes #35618 --- doc/topics/cloud/aws.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/topics/cloud/aws.rst b/doc/topics/cloud/aws.rst index 976a9ee3f0..670807953b 100644 --- a/doc/topics/cloud/aws.rst +++ b/doc/topics/cloud/aws.rst @@ -526,6 +526,31 @@ Tags can be set once an instance has been launched. .. _`AWS documentation`: http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html .. _`AWS Spot Instances`: http://aws.amazon.com/ec2/purchasing-options/spot-instances/ +Setting up a Master inside EC2 +------------------------------ + +Salt Cloud can configure Salt Masters as well as Minions. Use the ``make_master`` setting to use +this functionality. + +.. code-block:: yaml + + my-ec2-config: + # Optionally install a Salt Master in addition to the Salt Minion + make_master: True + +When creating a Salt Master inside EC2 with ``make_master: True``, or when the Salt Master is already +located and configured inside EC2, by default, minions connect to the master's public IP address during +Salt Cloud's provisioning process. Depending on how your security groups are defined, the minions +may or may not be able to communicate with the master. In order to use the master's private IP in EC2 +instead of the public IP, set the ``salt_interface`` to ``private_ips``. + +.. code-block:: yaml + + my-ec2-config: + # Optionally set the IP configuration to private_ips + salt_interface: private_ips + + Modify EC2 Tags =============== One of the features of EC2 is the ability to tag resources. In fact, under the From b68d293f86a48e2d0046439c90463a9df534d02d Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Fri, 2 Sep 2016 14:37:27 -0600 Subject: [PATCH 016/100] fix redis_return's clean_old_jobs. (#36014) Refs #33969 --- salt/returners/redis_return.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/salt/returners/redis_return.py b/salt/returners/redis_return.py index 6ca061edec..d2bf67e5c9 100644 --- a/salt/returners/redis_return.py +++ b/salt/returners/redis_return.py @@ -204,6 +204,11 @@ def get_minions(): def clean_old_jobs(): ''' Clean out minions's return data for old jobs. + + Normally, hset 'ret:' are saved with a TTL, and will eventually + get cleaned by redis.But for jobs with some very late minion return, the + corresponding hset's TTL will be refreshed to a too late timestamp, we'll + do manually cleaning here. ''' serv = _get_serv(ret=None) living_jids = set(serv.keys('load:*')) @@ -212,7 +217,7 @@ def clean_old_jobs(): load_key = ret_key.replace('ret:', 'load:', 1) if load_key not in living_jids: to_remove.append(ret_key) - serv.delete(**to_remove) # pylint: disable=E1134 + serv.delete(*to_remove) def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument From 822481ef9ecf9df56c081bd93d1923726c2dfbe7 Mon Sep 17 00:00:00 2001 From: Elias Probst Date: Fri, 2 Sep 2016 22:54:40 +0200 Subject: [PATCH 017/100] modules.service: Do not default to OpenRC on Gentoo, also allow systemd (#36010) --- salt/modules/gentoo_service.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/salt/modules/gentoo_service.py b/salt/modules/gentoo_service.py index 92cf48a2b0..228a3fe4c8 100644 --- a/salt/modules/gentoo_service.py +++ b/salt/modules/gentoo_service.py @@ -13,6 +13,9 @@ to the correct service manager # Import Python libs from __future__ import absolute_import +# Import Salt modules +import salt.utils.systemd + # Define the module's virtual name __virtualname__ = 'service' @@ -21,7 +24,8 @@ def __virtual__(): ''' Only work on systems which default to systemd ''' - if __grains__['os'] == 'Gentoo': + if __grains__['os'] == 'Gentoo' \ + and not salt.utils.systemd.booted(__context__): return __virtualname__ return (False, 'The gentoo_service execution module cannot be loaded: ' 'only available on Gentoo systems.') From 65b6734c042d47199ee9ea3876a967745b07f6c4 Mon Sep 17 00:00:00 2001 From: Daniel Wallace Date: Fri, 2 Sep 2016 15:56:09 -0500 Subject: [PATCH 018/100] catch unicode encoding errors in json outputter (#36033) Add testing too --- salt/output/json_out.py | 4 ++ tests/unit/output/json_out_test.py | 67 ++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 tests/unit/output/json_out_test.py diff --git a/salt/output/json_out.py b/salt/output/json_out.py index d775e6f30d..c29c29fe17 100644 --- a/salt/output/json_out.py +++ b/salt/output/json_out.py @@ -74,6 +74,10 @@ def output(data): return json.dumps(data, default=repr, indent=indent, sort_keys=sort_keys) + except UnicodeDecodeError as exc: + log.error('Unable to serialize output to json') + return json.dumps({'error': 'Unable to serialize output to json', 'message': str(exc)}) + except TypeError: log.debug('An error occurred while outputting JSON', exc_info=True) # Return valid JSON for unserializable objects diff --git a/tests/unit/output/json_out_test.py b/tests/unit/output/json_out_test.py new file mode 100644 index 0000000000..794e358db9 --- /dev/null +++ b/tests/unit/output/json_out_test.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +''' +unittests for json outputter +''' + +# Import Python Libs +from __future__ import absolute_import + +# Import Salt Testing Libs +from salttesting import TestCase +from salttesting.helpers import ensure_in_syspath + +ensure_in_syspath('../../') + +# Import Salt Libs +from salt.output import json_out as json + + +class JsonTestCase(TestCase): + ''' + Test cases for salt.output.json_out + ''' + def setUp(self): + json.__opts__ = {} + self.data = {'test': 'two', 'example': 'one'} + + def test_default_output(self): + ret = json.output(self.data) + expect = '{\n "test": "two", \n "example": "one"\n}' + self.assertEqual(expect, ret) + + def test_pretty_output(self): + json.__opts__['output_indent'] = 'pretty' + ret = json.output(self.data) + expect = '{\n "example": "one", \n "test": "two"\n}' + self.assertEqual(expect, ret) + + def test_indent_output(self): + json.__opts__['output_indent'] = 2 + expect = '{\n "test": "two", \n "example": "one"\n}' + ret = json.output(self.data) + self.assertEqual(expect, ret) + + def test_negative_zero_output(self): + json.__opts__['output_indent'] = 0 + expect = '{\n"test": "two", \n"example": "one"\n}' + ret = json.output(self.data) + self.assertEqual(expect, ret) + + def test_negative_int_output(self): + json.__opts__['output_indent'] = -1 + expect = '{"test": "two", "example": "one"}' + ret = json.output(self.data) + self.assertEqual(expect, ret) + + def test_unicode_output(self): + json.__opts__['output_indent'] = 'pretty' + data = {'test': '\xe1', 'example': 'one'} + expect = ('{"message": "\'utf8\' codec can\'t decode byte 0xe1 in position 0: unexpected end of data", ' + '"error": "Unable to serialize output to json"}') + ret = json.output(data) + self.assertEqual(expect, ret) + + +if __name__ == '__main__': + from integration import run_tests + run_tests(JsonTestCase, needs_daemon=False) From 1d90c42142537e0498e4be215bb31cbcb831b2a3 Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Fri, 2 Sep 2016 14:58:27 -0600 Subject: [PATCH 019/100] Back-port #35824 to 2016.3 (#36038) * Add more documentation to the wheel key module Fixes #35683 * Shorten some of the pub/priv key documentation examples. --- salt/wheel/key.py | 229 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 212 insertions(+), 17 deletions(-) diff --git a/salt/wheel/key.py b/salt/wheel/key.py index 09c069064d..ec94c98423 100644 --- a/salt/wheel/key.py +++ b/salt/wheel/key.py @@ -1,6 +1,30 @@ # -*- coding: utf-8 -*- ''' -Wheel system wrapper for key system +Wheel system wrapper for the Salt key system to be used in interactions with +the Salt Master programmatically. + +The key module for the wheel system is meant to provide an internal interface +for other Salt systems to interact with the Salt Master. The following usage +examples assume that a WheelClient is available: + +.. code-block:: python + + import salt.config + import salt.wheel + opts = salt.config.master_config('/etc/salt/master') + wheel = salt.wheel.WheelClient(opts) + +Note that importing and using the ``WheelClient`` must be performed on the same +machine as the Salt Master and as the same user that runs the Salt Master, +unless :conf_master:`external_auth` is configured and the user is authorized +to execute wheel functions. + +The function documentation starts with the ``wheel`` reference from the code +sample above and use the :py:class:`WheelClient` functions to show how they can +be called from a Python interpreter. + +The wheel key functions can also be called via a ``salt`` command at the CLI +using the :ref:`saltutil execution module `. ''' from __future__ import absolute_import @@ -19,7 +43,18 @@ __func_alias__ = { def list_(match): ''' - List all the keys under a named status + List all the keys under a named status. Returns a dictionary. + + match + The type of keys to list. The ``pre``, ``un``, and ``unaccepted`` + options will list unaccepted/unsigned keys. ``acc`` or ``accepted`` will + list accepted/signed keys. ``rej`` or ``rejected`` will list rejected keys. + Finally, ``all`` will list all keys. + + .. code-block:: python + + >>> wheel.cmd('key.list', ['accepted']) + {'minions': ['minion1', 'minion2', 'minion3']} ''' skey = salt.key.Key(__opts__) return skey.list_status(match) @@ -27,7 +62,16 @@ def list_(match): def list_all(): ''' - List all the keys + List all the keys. Returns a dictionary containing lists of the minions in + each salt-key category, including ``minions``, ``minions_rejected``, + ``minions_denied``, etc. Returns a dictionary. + + .. code-block:: python + + >>> wheel.cmd('key.list_all') + {'local': ['master.pem', 'master.pub'], 'minions_rejected': [], + 'minions_denied': [], 'minions_pre': [], + 'minions': ['minion1', 'minion2', 'minion3']} ''' skey = salt.key.Key(__opts__) return skey.all_keys() @@ -35,7 +79,23 @@ def list_all(): def accept(match, include_rejected=False, include_denied=False): ''' - Accept keys based on a glob match + Accept keys based on a glob match. Returns a dictionary. + + match + The glob match of keys to accept. + + include_rejected + To include rejected keys in the match along with pending keys, set this + to ``True``. Defaults to ``False``. + + include_denied + To include denied keys in the match along with pending keys, set this + to ``True``. Defaults to ``False``. + + .. code-block:: python + + >>> wheel.cmd('key.accept', ['minion1']) + {'minions': ['minion1']} ''' skey = salt.key.Key(__opts__) return skey.accept(match, include_rejected=include_rejected, include_denied=include_denied) @@ -43,20 +103,25 @@ def accept(match, include_rejected=False, include_denied=False): def accept_dict(match, include_rejected=False, include_denied=False): ''' - Accept keys based on a dict of keys + Accept keys based on a dict of keys. Returns a dictionary. - Example to move a list of keys from the `minions_pre` (pending) directory - to the `minions` (accepted) directory: + match + The dictionary of keys to accept. + + Example to move a list of keys from the ``minions_pre`` (pending) directory + to the ``minions`` (accepted) directory: .. code-block:: python + >>> wheel.cmd('accept_dict', { 'minions_pre': [ 'jerry', 'stuart', 'bob', ], - } + }) + {'minions': ['jerry', 'stuart', 'bob']} ''' skey = salt.key.Key(__opts__) return skey.accept(match_dict=match, @@ -66,7 +131,15 @@ def accept_dict(match, include_rejected=False, include_denied=False): def delete(match): ''' - Delete keys based on a glob match + Delete keys based on a glob match. Returns a dictionary. + + match + The glob match of keys to delete. + + .. code-block:: python + + >>> wheel.cmd_async({'fun': 'key.delete', 'match': 'minion1'}) + {'jid': '20160826201244808521', 'tag': 'salt/wheel/20160826201244808521'} ''' skey = salt.key.Key(__opts__) return skey.delete_key(match) @@ -74,7 +147,22 @@ def delete(match): def delete_dict(match): ''' - Delete keys based on a dict of keys + Delete keys based on a dict of keys. Returns a dictionary. + + match + The dictionary of keys to delete. + + .. code-block:: python + + >>> wheel.cmd_async({'fun': 'key.delete_dict', + 'match': { + 'minions': [ + 'jerry', + 'stuart', + 'bob', + ], + }) + {'jid': '20160826201244808521', 'tag': 'salt/wheel/20160826201244808521'} ''' skey = salt.key.Key(__opts__) return skey.delete_key(match_dict=match) @@ -82,7 +170,23 @@ def delete_dict(match): def reject(match, include_accepted=False, include_denied=False): ''' - Reject keys based on a glob match + Reject keys based on a glob match. Returns a dictionary. + + match + The glob match of keys to reject. + + include_accepted + To include accepted keys in the match along with pending keys, set this + to ``True``. Defaults to ``False``. + + include_denied + To include denied keys in the match along with pending keys, set this + to ``True``. Defaults to ``False``. + + .. code-block:: python + + >>> wheel.cmd_async({'fun': 'key.reject', 'match': 'minion1'}) + {'jid': '20160826201244808521', 'tag': 'salt/wheel/20160826201244808521'} ''' skey = salt.key.Key(__opts__) return skey.reject(match, include_accepted=include_accepted, include_denied=include_denied) @@ -90,7 +194,22 @@ def reject(match, include_accepted=False, include_denied=False): def reject_dict(match, include_accepted=False, include_denied=False): ''' - Reject keys based on a dict of keys + Reject keys based on a dict of keys. Returns a dictionary. + + match + The dictionary of keys to reject. + + .. code-block:: python + + >>> wheel.cmd_async({'fun': 'key.reject_dict', + 'match': { + 'minions': [ + 'jerry', + 'stuart', + 'bob', + ], + }) + {'jid': '20160826201244808521', 'tag': 'salt/wheel/20160826201244808521'} ''' skey = salt.key.Key(__opts__) return skey.reject(match_dict=match, @@ -100,7 +219,17 @@ def reject_dict(match, include_accepted=False, include_denied=False): def key_str(match): ''' - Return the key strings + Return information about the key. Returns a dictionary. + + match + The key to return information about. + + .. code-block:: python + + >>> wheel.cmd('key.key_str', ['minion1']) + {'minions': {'minion1': '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0B + ... + TWugEQpPt\niQIDAQAB\n-----END PUBLIC KEY-----'}} ''' skey = salt.key.Key(__opts__) return skey.key_str(match) @@ -108,7 +237,16 @@ def key_str(match): def finger(match): ''' - Return the matching key fingerprints + Return the matching key fingerprints. Returns a dictionary. + + match + The key for with to retrieve the fingerprint. + + .. code-block:: python + + >>> wheel.cmd('key.finger', ['minion1']) + {'minions': {'minion1': '5d:f6:79:43:5e:d4:42:3f:57:b8:45:a8:7e:a4:6e:ca'}} + ''' skey = salt.key.Key(__opts__) return skey.finger(match) @@ -116,8 +254,30 @@ def finger(match): def gen(id_=None, keysize=2048): ''' - Generate a key pair. No keys are stored on the master, a keypair is - returned as a dict containing pub and priv keys + Generate a key pair. No keys are stored on the master. A key pair is + returned as a dict containing pub and priv keys. Returns a dictionary + containing the the ``pub`` and ``priv`` keys with their generated values. + + id_ + Set a name to generate a key pair for use with salt. If not specified, + a random name will be specified. + + keysize + The size of the key pair to generate. The size must be ``2048``, which + is the default, or greater. If set to a value less than ``2048``, the + key size will be rounded up to ``2048``. + + .. code-block:: python + + >>> wheel.cmd('key.gen') + {'pub': '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBC + ... + BBPfamX9gGPQTpN9e8HwcZjXQnmg8OrcUl10WHw09SDWLOlnW+ueTWugEQpPt\niQIDAQAB\n + -----END PUBLIC KEY-----', + 'priv': '-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA42Kf+w9XeZWgguzv + ... + QH3/W74X1+WTBlx4R2KGLYBiH+bCCFEQ/Zvcu4Xp4bIOPtRKozEQ==\n + -----END RSA PRIVATE KEY-----'} ''' if id_ is None: id_ = hashlib.sha512(os.urandom(32)).hexdigest() @@ -137,7 +297,42 @@ def gen(id_=None, keysize=2048): def gen_accept(id_, keysize=2048, force=False): ''' Generate a key pair then accept the public key. This function returns the - key pair in a dict, only the public key is preserved on the master. + key pair in a dict, only the public key is preserved on the master. Returns + a dictionary. + + id_ + The name of the minion for which to generate a key pair. + + keysize + The size of the key pair to generate. The size must be ``2048``, which + is the default, or greater. If set to a value less than ``2048``, the + key size will be rounded up to ``2048``. + + force + If a public key has already been accepted for the given minion on the + master, then the gen_accept function will return an empty dictionary + and not create a new key. This is the default behavior. If ``force`` + is set to ``True``, then the minion's previously accepted key will be + overwritten. + + .. code-block:: python + + >>> wheel.cmd('key.gen_accept', ['foo']) + {'pub': '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBC + ... + BBPfamX9gGPQTpN9e8HwcZjXQnmg8OrcUl10WHw09SDWLOlnW+ueTWugEQpPt\niQIDAQAB\n + -----END PUBLIC KEY-----', + 'priv': '-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA42Kf+w9XeZWgguzv + ... + QH3/W74X1+WTBlx4R2KGLYBiH+bCCFEQ/Zvcu4Xp4bIOPtRKozEQ==\n + -----END RSA PRIVATE KEY-----'} + + We can now see that the ``foo`` minion's key has been accepted by the master: + + .. code-block:: python + + >>> wheel.cmd('key.list', ['accepted']) + {'minions': ['foo', 'minion1', 'minion2', 'minion3']} ''' ret = gen(id_, keysize) acc_path = os.path.join(__opts__['pki_dir'], 'minions', id_) From ab7939cdea95a9938e44635409e2ee9a1aa1c256 Mon Sep 17 00:00:00 2001 From: Justin Findlay Date: Tue, 9 Aug 2016 15:31:52 -0600 Subject: [PATCH 020/100] modules.parallels: expand module documentation --- salt/modules/parallels.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/salt/modules/parallels.py b/salt/modules/parallels.py index 0290624773..e11ed34418 100644 --- a/salt/modules/parallels.py +++ b/salt/modules/parallels.py @@ -1,8 +1,20 @@ # -*- coding: utf-8 -*- ''' -Manage Parallels Desktop VMs with prlctl +Manage Parallels Desktop VMs with ``prlctl`` and ``prlsrvctl``. Only some of +the prlctl commands implemented so far. Of those that have been implemented, +not all of the options may have been provided yet. For a complete reference, +see the `Parallels Desktop Reference Guide +`_. -http://download.parallels.com/desktop/v9/ga/docs/en_US/Parallels%20Command%20Line%20Reference%20Guide.pdf +What has not been implemented yet can be accessed through ``parallels.prlctl`` +and ``parallels.prlsrvctl`` (note the preceeding double dash ``--`` as +necessary): + +.. code-block:: + + salt '*' parallels.prlctl installtools macvm runas=macdev + salt -- '*' parallels.prlctl capture 'macvm --file macvm.display.png' runas=macdev + salt -- '*' parallels.prlsrvctl set '--mem-limit auto' runas=macdev .. versionadded:: 2016.3.0 ''' From d913f2cbe72ae43567e5d66efd5a6a80c6539956 Mon Sep 17 00:00:00 2001 From: Justin Findlay Date: Tue, 9 Aug 2016 15:32:34 -0600 Subject: [PATCH 021/100] modules.parallels: add prlsrvctl function --- salt/modules/parallels.py | 37 +++++++++++++++++++++++++++- tests/unit/modules/parallels_test.py | 36 ++++++++++++++++++++++++++- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/salt/modules/parallels.py b/salt/modules/parallels.py index e11ed34418..531ccade53 100644 --- a/salt/modules/parallels.py +++ b/salt/modules/parallels.py @@ -48,7 +48,9 @@ def __virtual__(): Load this module if prlctl is available ''' if not salt.utils.which('prlctl'): - return (False, 'Cannot load prlctl module: prlctl utility not available') + return (False, 'prlctl utility not available') + if not salt.utils.which('prlsrvctl'): + return (False, 'prlsrvctl utility not available') return __virtualname__ @@ -88,6 +90,38 @@ def _find_guids(guid_string): return sorted(list(set(guids))) +def prlsrvctl(sub_cmd, args=None, runas=None): + ''' + Execute a prlsrvctl command + + .. versionadded:: Carbon + + :param str sub_cmd: + prlsrvctl subcommand to execute + + :param str args: + The arguments supplied to ``prlsrvctl `` + + :param str runas: + The user that the prlsrvctl command will be run as + + Example: + + .. code-block:: bash + + salt '*' parallels.prlsrvctl info runas=macdev + salt '*' parallels.prlsrvctl usb list runas=macdev + salt -- '*' parallels.prlsrvctl set '--mem-limit auto' runas=macdev + ''' + # Construct command + cmd = ['prlsrvctl', sub_cmd] + if args: + cmd.extend(_normalize_args(args)) + + # Execute command and return output + return __salt__['cmd.run'](cmd, runas=runas) + + def prlctl(sub_cmd, args=None, runas=None): ''' Execute a prlctl command @@ -107,6 +141,7 @@ def prlctl(sub_cmd, args=None, runas=None): salt '*' parallels.prlctl user list runas=macdev salt '*' parallels.prlctl exec 'macvm uname' runas=macdev + salt -- '*' parallels.prlctl capture 'macvm --file macvm.display.png' runas=macdev ''' # Construct command cmd = ['prlctl', sub_cmd] diff --git a/tests/unit/modules/parallels_test.py b/tests/unit/modules/parallels_test.py index 25046b06b5..092fe36e18 100644 --- a/tests/unit/modules/parallels_test.py +++ b/tests/unit/modules/parallels_test.py @@ -88,6 +88,33 @@ class ParallelsTestCase(TestCase): self.assertEqual(parallels._find_guids(guid_str), guids) + def test_prlsrvctl(self): + ''' + Test parallels.prlsrvctl + ''' + runas = 'macdev' + + # Validate 'prlsrvctl info' + info_cmd = ['prlsrvctl', 'info'] + info_fcn = MagicMock() + with patch.dict(parallels.__salt__, {'cmd.run': info_fcn}): + parallels.prlsrvctl('info', runas=runas) + info_fcn.assert_called_once_with(info_cmd, runas=runas) + + # Validate 'prlsrvctl usb list' + usb_cmd = ['prlsrvctl', 'usb', 'list'] + usb_fcn = MagicMock() + with patch.dict(parallels.__salt__, {'cmd.run': usb_fcn}): + parallels.prlsrvctl('usb', 'list', runas=runas) + usb_fcn.assert_called_once_with(usb_cmd, runas=runas) + + # Validate 'prlsrvctl set "--mem-limit auto"' + set_cmd = ['prlsrvctl', 'set', '--mem-limit', 'auto'] + set_fcn = MagicMock() + with patch.dict(parallels.__salt__, {'cmd.run': set_fcn}): + parallels.prlsrvctl('set', '--mem-limit auto', runas=runas) + set_fcn.assert_called_once_with(set_cmd, runas=runas) + def test_prlctl(self): ''' Test parallels.prlctl @@ -101,13 +128,20 @@ class ParallelsTestCase(TestCase): parallels.prlctl('user', 'list', runas=runas) user_fcn.assert_called_once_with(user_cmd, runas=runas) - # Validate 'prlctl exec macvm uname' + # Validate 'prlctl exec "macvm uname"' exec_cmd = ['prlctl', 'exec', 'macvm', 'uname'] exec_fcn = MagicMock() with patch.dict(parallels.__salt__, {'cmd.run': exec_fcn}): parallels.prlctl('exec', 'macvm uname', runas=runas) exec_fcn.assert_called_once_with(exec_cmd, runas=runas) + # Validate 'prlctl capture "macvm --file macvm.display.png"' + cap_cmd = ['prlctl', 'capture', 'macvm', '--file', 'macvm.display.png'] + cap_fcn = MagicMock() + with patch.dict(parallels.__salt__, {'cmd.run': cap_fcn}): + parallels.prlctl('capture', 'macvm --file macvm.display.png', runas=runas) + cap_fcn.assert_called_once_with(cap_cmd, runas=runas) + def test_list_vms(self): ''' Test parallels.list_vms From 9eb0f637e99d41cdecd343429b57f6dc205bb52e Mon Sep 17 00:00:00 2001 From: Justin Findlay Date: Tue, 23 Aug 2016 15:53:00 -0600 Subject: [PATCH 022/100] modules.parallels.list_vms: add template arg, update name arg --- salt/modules/parallels.py | 28 +++++++++++++++++++--------- tests/unit/modules/parallels_test.py | 10 +++++++++- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/salt/modules/parallels.py b/salt/modules/parallels.py index 531ccade53..d86d10d99a 100644 --- a/salt/modules/parallels.py +++ b/salt/modules/parallels.py @@ -152,32 +152,41 @@ def prlctl(sub_cmd, args=None, runas=None): return __salt__['cmd.run'](cmd, runas=runas) -def list_vms(name=None, info=False, all=False, args=None, runas=None): +def list_vms(name=None, info=False, all=False, args=None, runas=None, template=False): ''' List information about the VMs :param str name: - Name/ID of VM to list; implies ``info=True`` + Name/ID of VM to list + + .. versionchanged:: Carbon + + No longer implies ``info=True`` :param str info: List extra information :param bool all: - Also list non-running VMs + List all non-template VMs :param tuple args: - Additional arguments given to ``prctl list``. This argument is - mutually exclusive with the other arguments + Additional arguments given to ``prctl list`` :param str runas: The user that the prlctl command will be run as + :param bool template: + List the available virtual machine templates. The real virtual + machines will not be included in the output + + .. versionadded:: Carbon + Example: .. code-block:: bash salt '*' parallels.list_vms runas=macdev - salt '*' parallels.list_vms name=macvm runas=macdev + salt '*' parallels.list_vms name=macvm info=True runas=macdev salt '*' parallels.list_vms info=True runas=macdev salt '*' parallels.list_vms ' -o uuid,status' all=True runas=macdev ''' @@ -188,12 +197,13 @@ def list_vms(name=None, info=False, all=False, args=None, runas=None): args = _normalize_args(args) if name: - args.extend(['--info', name]) - elif info: + args.extend([name]) + if info: args.append('--info') - if all: args.append('--all') + if template: + args.append('--template') # Execute command and return output return prlctl('list', args, runas=runas) diff --git a/tests/unit/modules/parallels_test.py b/tests/unit/modules/parallels_test.py index 092fe36e18..9e9f2a6b26 100644 --- a/tests/unit/modules/parallels_test.py +++ b/tests/unit/modules/parallels_test.py @@ -159,9 +159,17 @@ class ParallelsTestCase(TestCase): with patch.object(parallels, 'prlctl', mock_name): parallels.list_vms(name='macvm', runas=runas) mock_name.assert_called_once_with('list', - ['--info', 'macvm'], + ['macvm'], runas=runas) + # Validate listing templates + mock_templ = MagicMock() + with patch.object(parallels, 'prlctl', mock_templ): + parallels.list_vms(template=True, runas=runas) + mock_templ.assert_called_once_with('list', + ['--template'], + runas=runas) + # Validate listing extra info mock_info = MagicMock() with patch.object(parallels, 'prlctl', mock_info): From 0f7d704eb64171dfd499937609911769bf017be3 Mon Sep 17 00:00:00 2001 From: Justin Findlay Date: Tue, 9 Aug 2016 15:32:55 -0600 Subject: [PATCH 023/100] modules.parallels: add clone and delete functions --- salt/modules/parallels.py | 57 ++++++++++++++++++++++++++++ tests/unit/modules/parallels_test.py | 44 +++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/salt/modules/parallels.py b/salt/modules/parallels.py index d86d10d99a..0961824787 100644 --- a/salt/modules/parallels.py +++ b/salt/modules/parallels.py @@ -209,6 +209,63 @@ def list_vms(name=None, info=False, all=False, args=None, runas=None, template=F return prlctl('list', args, runas=runas) +def clone(name, new_name, linked=False, template=False, runas=None): + ''' + Clone a VM + + .. versionadded:: Carbon + + :param str name: + Name/ID of VM to clone + + :param str new_name: + Name of the new VM + + :param bool linked: + Create a linked virtual machine. + + :param bool template: + Create a virtual machine template instead of a real virtual machine. + + :param str runas: + The user that the prlctl command will be run as + + Example: + + .. code-block:: bash + + salt '*' parallels.clone macvm macvm_new runas=macdev + salt '*' parallels.clone macvm macvm_templ template=True runas=macdev + ''' + args = [_sdecode(name), '--name', _sdecode(new_name)] + if linked: + args.append('--linked') + if template: + args.append('--template') + return prlctl('clone', args, runas=runas) + + +def delete(name, runas=None): + ''' + Delete a VM + + .. versionadded:: Carbon + + :param str name: + Name/ID of VM to clone + + :param str runas: + The user that the prlctl command will be run as + + Example: + + .. code-block:: bash + + salt '*' parallels.exec macvm 'find /etc/paths.d' runas=macdev + ''' + return prlctl('delete', _sdecode(name), runas=runas) + + def start(name, runas=None): ''' Start a VM diff --git a/tests/unit/modules/parallels_test.py b/tests/unit/modules/parallels_test.py index 9e9f2a6b26..a1aeab7c16 100644 --- a/tests/unit/modules/parallels_test.py +++ b/tests/unit/modules/parallels_test.py @@ -186,6 +186,50 @@ class ParallelsTestCase(TestCase): ['-o', 'uuid,status', '--all'], runas=runas) + def test_clone(self): + ''' + Test parallels.clone + ''' + name = 'macvm' + runas = 'macdev' + + # Validate clone + mock_clone = MagicMock() + with patch.object(parallels, 'prlctl', mock_clone): + parallels.clone(name, 'macvm_new', runas=runas) + mock_clone.assert_called_once_with('clone', + [name, '--name', 'macvm_new'], + runas=runas) + + # Validate linked clone + mock_linked = MagicMock() + with patch.object(parallels, 'prlctl', mock_linked): + parallels.clone(name, 'macvm_link', linked=True, runas=runas) + mock_linked.assert_called_once_with('clone', + [name, '--name', 'macvm_link', '--linked'], + runas=runas) + + # Validate template clone + mock_template = MagicMock() + with patch.object(parallels, 'prlctl', mock_template): + parallels.clone(name, 'macvm_templ', template=True, runas=runas) + mock_template.assert_called_once_with('clone', + [name, '--name', 'macvm_templ', '--template'], + runas=runas) + + def test_delete(self): + ''' + Test parallels.delete + ''' + name = 'macvm' + runas = 'macdev' + + # Validate delete + mock_delete = MagicMock() + with patch.object(parallels, 'prlctl', mock_delete): + parallels.delete(name, runas=runas) + mock_delete.assert_called_once_with('delete', name, runas=runas) + def test_start(self): ''' Test parallels.start From 430ec201d92b76366842456c68a888e9cf8a9f15 Mon Sep 17 00:00:00 2001 From: Justin Findlay Date: Fri, 26 Aug 2016 19:38:21 -0600 Subject: [PATCH 024/100] modules.parallels: add exists and state fcns --- salt/modules/parallels.py | 56 +++++++++++++++++++++++++++- tests/unit/modules/parallels_test.py | 37 ++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/salt/modules/parallels.py b/salt/modules/parallels.py index 0961824787..59406f0bfe 100644 --- a/salt/modules/parallels.py +++ b/salt/modules/parallels.py @@ -266,6 +266,58 @@ def delete(name, runas=None): return prlctl('delete', _sdecode(name), runas=runas) +def exists(name, runas=None): + ''' + Query whether a VM exists + + .. versionadded:: Carbon + + :param str name: + Name/ID of VM + + :param str runas: + The user that the prlctl command will be run as + + Example: + + .. code-block:: bash + + salt '*' parallels.exists macvm runas=macdev + ''' + vm_info = list_vms(name, info=True, runas=runas).splitlines() + for info_line in vm_info: + if 'Name: {0}'.format(name) in info_line: + return True + return False + + +def state(name, runas=None): + ''' + Return the state of the VM + + .. versionadded:: Carbon + + :param str name: + Name/ID of VM + + :param str runas: + The user that the prlctl command will be run as + + Example: + + .. code-block:: bash + + salt '*' parallels.state macvm runas=macdev + ''' + vm_info = list_vms(name, info=True, runas=runas).splitlines() + for info_line in vm_info: + if 'State: ' in info_line: + return info_line.split('State: ')[1] + + log.error('Cannot find state of VM named {0}'.format(name)) + return '' + + def start(name, runas=None): ''' Start a VM @@ -454,7 +506,7 @@ def snapshot_id_to_name(name, snap_id, strict=False, runas=None): data = yaml.safe_load(info) except yaml.YAMLError as err: log.warning( - 'Could not interpret snapshot data returned from parallels deskop: ' + 'Could not interpret snapshot data returned from prlctl: ' '{0}'.format(err) ) data = {} @@ -467,7 +519,7 @@ def snapshot_id_to_name(name, snap_id, strict=False, runas=None): snap_name = '' else: log.warning( - u'Could not interpret snapshot data returned from parallels deskop: ' + u'Could not interpret snapshot data returned from prlctl: ' u'data is not formed as a dictionary: {0}'.format(data) ) snap_name = '' diff --git a/tests/unit/modules/parallels_test.py b/tests/unit/modules/parallels_test.py index a1aeab7c16..9b0b191ff4 100644 --- a/tests/unit/modules/parallels_test.py +++ b/tests/unit/modules/parallels_test.py @@ -230,6 +230,43 @@ class ParallelsTestCase(TestCase): parallels.delete(name, runas=runas) mock_delete.assert_called_once_with('delete', name, runas=runas) + def test_exists(self): + ''' + Test parallels.exists + ''' + name = 'macvm' + runas = 'macdev' + + # Validate exists + mock_list = MagicMock(return_value='Name: {0}\nState: running'.format(name)) + with patch.object(parallels, 'list_vms', mock_list): + self.assertTrue(parallels.exists(name, runas=runas)) + + # Validate not exists + mock_list = MagicMock(return_value='Name: {0}\nState: running'.format(name)) + with patch.object(parallels, 'list_vms', mock_list): + self.assertFalse(parallels.exists('winvm', runas=runas)) + + def test_state(self): + ''' + Test parallels.state + ''' + name = 'macvm' + runas = 'macdev' + + # Validate state + mock_list = MagicMock(return_value='Name: {0}\nState: cantering'.format(name)) + with patch.object(parallels, 'list_vms', mock_list): + self.assertEqual(parallels.state(name, runas=runas), 'cantering') + + # Validate cannot find state + mock_list = MagicMock(return_value='Name: {0}\nFavorite Color: unknown'.format(name)) + mock_log_error = MagicMock() + with patch.object(parallels, 'list_vms', mock_list): + with patch.object(parallels.log, 'error', mock_log_error): + self.assertEqual(parallels.state(name, runas=runas), '') + mock_log_error.assert_called_once_with('Cannot find state of VM named {0}'.format(name)) + def test_start(self): ''' Test parallels.start From 9828a0dbfa90415e577583d09605f84aa86714c7 Mon Sep 17 00:00:00 2001 From: rallytime Date: Mon, 5 Sep 2016 17:30:59 -0600 Subject: [PATCH 025/100] Comment all lines in the minion config file --- conf/minion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/minion b/conf/minion index 301e5276d2..ad7a3749e6 100644 --- a/conf/minion +++ b/conf/minion @@ -415,7 +415,7 @@ # Note that this is a very large hammer and it can be quite difficult to keep the minion working # the way you think it should since Salt uses many modules internally itself. At a bare minimum # you need the following enabled or else the minion won't start. -whitelist_modules: +#whitelist_modules: # - cmdmod # - test # - config From b04cc629279dc6d8cf09b4ed3e559e7693a77e02 Mon Sep 17 00:00:00 2001 From: rallytime Date: Mon, 5 Sep 2016 18:08:02 -0600 Subject: [PATCH 026/100] Add unit tests to check for all files in conf/ to be commented out --- tests/unit/conf_test.py | 140 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 tests/unit/conf_test.py diff --git a/tests/unit/conf_test.py b/tests/unit/conf_test.py new file mode 100644 index 0000000000..536bd002d8 --- /dev/null +++ b/tests/unit/conf_test.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +''' +Unit tests for the files in the salt/conf directory. +''' + +# Import Python libs +from __future__ import absolute_import +import os + +# Import Salt Testing libs +from salttesting import skipIf, TestCase +from salttesting.helpers import ensure_in_syspath +from salttesting.mock import ( + NO_MOCK, + NO_MOCK_REASON, +) + +ensure_in_syspath('../') + +# Import Salt libs +import salt.config + +SAMPLE_CONF_DIR = os.path.dirname(os.path.realpath(__file__)).split('tests')[0] + 'conf/' + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +class ConfTest(TestCase): + ''' + Validate files in the salt/conf directory. + ''' + + def test_conf_master_sample_is_commented(self): + ''' + The sample config file located in salt/conf/master must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + master_config = SAMPLE_CONF_DIR + 'master' + ret = salt.config._read_conf_file(master_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + master_config + ) + ) + + def test_conf_minion_sample_is_commented(self): + ''' + The sample config file located in salt/conf/minion must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + minion_config = SAMPLE_CONF_DIR + 'minion' + ret = salt.config._read_conf_file(minion_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + minion_config + ) + ) + + def test_conf_cloud_sample_is_commented(self): + ''' + The sample config file located in salt/conf/cloud must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + cloud_config = SAMPLE_CONF_DIR + 'cloud' + ret = salt.config._read_conf_file(cloud_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + cloud_config + ) + ) + + def test_conf_cloud_profiles_sample_is_commented(self): + ''' + The sample config file located in salt/conf/cloud.profiles must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + cloud_profiles_config = SAMPLE_CONF_DIR + 'cloud.profiles' + ret = salt.config._read_conf_file(cloud_profiles_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + cloud_profiles_config + ) + ) + + def test_conf_cloud_providers_sample_is_commented(self): + ''' + The sample config file located in salt/conf/cloud.providers must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + cloud_providers_config = SAMPLE_CONF_DIR + 'cloud.providers' + ret = salt.config._read_conf_file(cloud_providers_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + cloud_providers_config + ) + ) + + def test_conf_proxy_sample_is_commented(self): + ''' + The sample config file located in salt/conf/proxy must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + proxy_config = SAMPLE_CONF_DIR + 'proxy' + ret = salt.config._read_conf_file(proxy_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + proxy_config + ) + ) + + def test_conf_roster_sample_is_commented(self): + ''' + The sample config file located in salt/conf/roster must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + roster_config = SAMPLE_CONF_DIR + 'roster' + ret = salt.config._read_conf_file(roster_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + roster_config + ) + ) + + +if __name__ == '__main__': + from integration import run_tests + run_tests(ConfTest, needs_daemon=False) From 14907e15d38c045ebfc22497dd45b7d21e204e43 Mon Sep 17 00:00:00 2001 From: rallytime Date: Mon, 5 Sep 2016 18:13:02 -0600 Subject: [PATCH 027/100] Make sure cont/cloud.profiles file is commented out Matches the cloud.providers file format and other sample config files. --- conf/cloud.profiles | 46 ++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/conf/cloud.profiles b/conf/cloud.profiles index d53aa58d50..0d02d5d2c4 100644 --- a/conf/cloud.profiles +++ b/conf/cloud.profiles @@ -1,23 +1,27 @@ -base_ec2: - provider: my-ec2-config - image: ami-e565ba8c - size: t1.micro - script: python-bootstrap - minion: - cheese: edam +# This file may be used in addition to, or instead of, the files in the +# cloud.profiles.d/ directory. The format for this file, and all files in that +# directory, is identical. -ubuntu_rs: - provider: my-openstack-rackspace-config - image: Ubuntu 12.04 LTS - size: 256 server - script: Ubuntu - minion: - cheese: edam +#base_ec2: +# provider: my-ec2-config +# image: ami-e565ba8c +# size: t1.micro +# script: python-bootstrap +# minion: +# cheese: edam -fedora_rs: - provider: my-openstack-rackspace-config - image: Fedora 17 - size: 256 server - script: Fedora - minion: - cheese: edam +#ubuntu_rs: +# provider: my-openstack-rackspace-config +# image: Ubuntu 12.04 LTS +# size: 256 server +# script: Ubuntu +# minion: +# cheese: edam + +#fedora_rs: +# provider: my-openstack-rackspace-config +# image: Fedora 17 +# size: 256 server +# script: Fedora +# minion: +# cheese: edam From dd53a26af5a96d2e9a56dcb34ee8da94e583ba09 Mon Sep 17 00:00:00 2001 From: rallytime Date: Mon, 5 Sep 2016 19:11:19 -0600 Subject: [PATCH 028/100] Add unit tests for sample files in cloud.profiles.d, cloud.providers.d, and cloud.maps.d --- tests/unit/conf_test.py | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/unit/conf_test.py b/tests/unit/conf_test.py index 536bd002d8..cce0023423 100644 --- a/tests/unit/conf_test.py +++ b/tests/unit/conf_test.py @@ -134,6 +134,60 @@ class ConfTest(TestCase): ) ) + def test_conf_cloud_profiles_d_files_are_commented(self): + ''' + All cloud profile sample configs in salt/conf/cloud.profiles.d/* must be completely + commented out. This test loops through all of the files in that directory to check + for any lines that are not commented or blank. + ''' + cloud_sample_files = os.listdir(SAMPLE_CONF_DIR + 'cloud.profiles.d/') + for conf_file in cloud_sample_files: + profile_conf = SAMPLE_CONF_DIR + 'cloud.profiles.d/' + conf_file + ret = salt.config._read_conf_file(profile_conf) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + conf_file + ) + ) + + def test_conf_cloud_providers_d_files_are_commented(self): + ''' + All cloud profile sample configs in salt/conf/cloud.providers.d/* must be completely + commented out. This test loops through all of the files in that directory to check + for any lines that are not commented or blank. + ''' + cloud_sample_files = os.listdir(SAMPLE_CONF_DIR + 'cloud.providers.d/') + for conf_file in cloud_sample_files: + provider_conf = SAMPLE_CONF_DIR + 'cloud.providers.d/' + conf_file + ret = salt.config._read_conf_file(provider_conf) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + conf_file + ) + ) + + def test_conf_cloud_maps_d_files_are_commented(self): + ''' + All cloud profile sample configs in salt/conf/cloud.maps.d/* must be completely + commented out. This test loops through all of the files in that directory to check + for any lines that are not commented or blank. + ''' + cloud_sample_files = os.listdir(SAMPLE_CONF_DIR + 'cloud.maps.d/') + for conf_file in cloud_sample_files: + map_conf = SAMPLE_CONF_DIR + 'cloud.maps.d/' + conf_file + ret = salt.config._read_conf_file(map_conf) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + conf_file + ) + ) + if __name__ == '__main__': from integration import run_tests From 832af68badd5191daff3996250d042e862185760 Mon Sep 17 00:00:00 2001 From: rallytime Date: Mon, 5 Sep 2016 19:12:27 -0600 Subject: [PATCH 029/100] Make sure all sample config files in cloud.maps.d are commented out --- conf/cloud.maps.d/cloud.map | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/conf/cloud.maps.d/cloud.map b/conf/cloud.maps.d/cloud.map index bfbe52de69..4cf8e715dd 100644 --- a/conf/cloud.maps.d/cloud.map +++ b/conf/cloud.maps.d/cloud.map @@ -4,17 +4,16 @@ # Because the location to this file must be explicitly declared when using it, # its actual location on disk is up to the user. +#fedora_rs: +# - fedora1 +# - fedora2 +# - fedora3 +# - fedora4 +# - fedora5 -fedora_rs: - - fedora1 - - fedora2 - - fedora3 - - fedora4 - - fedora5 - -ubuntu_rs: - - ubuntu1 - - ubuntu2 - - ubuntu3 - - ubuntu4 - - ubuntu5 +#ubuntu_rs: +# - ubuntu1 +# - ubuntu2 +# - ubuntu3 +# - ubuntu4 +# - ubuntu5 From f1f1b84611f61c03cde0ae46a05692a4dabe7d78 Mon Sep 17 00:00:00 2001 From: rallytime Date: Mon, 5 Sep 2016 19:20:33 -0600 Subject: [PATCH 030/100] Make sure all sample config files in cloud.profiles.d are commented out --- conf/cloud.profiles.d/EC2-us-east-1.profiles | 181 +++++++++---------- conf/cloud.profiles.d/EC2-us-west-1.profiles | 163 +++++++++-------- conf/cloud.profiles.d/EC2-us-west-2.profiles | 181 +++++++++---------- 3 files changed, 261 insertions(+), 264 deletions(-) diff --git a/conf/cloud.profiles.d/EC2-us-east-1.profiles b/conf/cloud.profiles.d/EC2-us-east-1.profiles index c348543cf0..c11dbdb673 100644 --- a/conf/cloud.profiles.d/EC2-us-east-1.profiles +++ b/conf/cloud.profiles.d/EC2-us-east-1.profiles @@ -2,115 +2,114 @@ # Arch Linux # https://wiki.archlinux.org/index.php/Arch_Linux_AMIs_for_Amazon_Web_Services -arch_ec2: - provider: my-ec2-config - image: ami-6ee95107 - size: t1.micro - ssh_username: root - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#arch_ec2: +# provider: my-ec2-config +# image: ami-6ee95107 +# size: t1.micro +# ssh_username: root +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 -arch_cloud-init_ec2: - provider: my-ec2-config - image: ami-596de730 - size: t1.micro - ssh_username: root - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#arch_cloud-init_ec2: +# provider: my-ec2-config +# image: ami-596de730 +# size: t1.micro +# ssh_username: root +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 # Centos 6, available from ec2 marketplace for no-charge # http://wiki.centos.org/Cloud/AWS -centos_6: - provider: my-ec2-config - image: ami-86e15bef - size: t1.micro - ssh_username: root - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#centos_6: +# provider: my-ec2-config +# image: ami-86e15bef +# size: t1.micro +# ssh_username: root +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 # official Debian, available at no-charge from ec2 marketplace: # http://wiki.debian.org/Cloud/AmazonEC2Image -debian_squeeze_ec2: - provider: my-ec2-config - image: ami-a121a6c8 - size: t1.micro - ssh_username: admin - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#debian_squeeze_ec2: +# provider: my-ec2-config +# image: ami-a121a6c8 +# size: t1.micro +# ssh_username: admin +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 # Fedora project cloud images # https://fedoraproject.org/wiki/Cloud_images -fedora_17_ec2: - provider: my-ec2-config - image: ami-2ea50247 - size: t1.micro - ssh_username: ec2-user - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#fedora_17_ec2: +# provider: my-ec2-config +# image: ami-2ea50247 +# size: t1.micro +# ssh_username: ec2-user +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 -fedora_18_ec2: - provider: my-ec2-config - image: ami-6145cc08 - size: t1.micro - ssh_username: ec2-user - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#fedora_18_ec2: +# provider: my-ec2-config +# image: ami-6145cc08 +# size: t1.micro +# ssh_username: ec2-user +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 # FreeBSD 9.1 # http://www.daemonology.net/freebsd-on-ec2/ # this t1.micro instance does not auto-populate SSH keys see above link -freebsd_91_ec2: - provider: my-ec2-config - image: ami-5339bb3a - size: t1.micro - ssh_username: ec2-user - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#freebsd_91_ec2: +# provider: my-ec2-config +# image: ami-5339bb3a +# size: t1.micro +# ssh_username: ec2-user +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 -freebsd_91_4XL_ec2: - provider: my-ec2-config - image: ami-79088510 - size: Cluster Compute 4XL - ssh_username: ec2-user - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#freebsd_91_4XL_ec2: +# provider: my-ec2-config +# image: ami-79088510 +# size: Cluster Compute 4XL +# ssh_username: ec2-user +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 # Canonical Ubuntu LTS images # http://cloud-images.ubuntu.com/releases/ -ubuntu_lucid_ec2: - provider: my-ec2-config - image: ami-21e47148 - size: t1.micro - ssh_username: ubuntu - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 - -ubuntu_precise_ec2: - provider: my-ec2-config - image: ami-0145d268 - size: t1.micro - ssh_username: ubuntu - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#ubuntu_lucid_ec2: +# provider: my-ec2-config +# image: ami-21e47148 +# size: t1.micro +# ssh_username: ubuntu +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 +#ubuntu_precise_ec2: +# provider: my-ec2-config +# image: ami-0145d268 +# size: t1.micro +# ssh_username: ubuntu +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 diff --git a/conf/cloud.profiles.d/EC2-us-west-1.profiles b/conf/cloud.profiles.d/EC2-us-west-1.profiles index fc29501e3a..f4c055d3cd 100644 --- a/conf/cloud.profiles.d/EC2-us-west-1.profiles +++ b/conf/cloud.profiles.d/EC2-us-west-1.profiles @@ -2,105 +2,104 @@ # Arch Linux # https://wiki.archlinux.org/index.php/Arch_Linux_AMIs_for_Amazon_Web_Services -arch_ec2: - provider: my-ec2-config - image: ami-337d5b76 - size: t1.micro - ssh_username: root - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#arch_ec2: +# provider: my-ec2-config +# image: ami-337d5b76 +# size: t1.micro +# ssh_username: root +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 -arch_cloud-init_ec2: - provider: my-ec2-config - image: ami-6a5f7c2f - size: t1.micro - ssh_username: root - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#arch_cloud-init_ec2: +# provider: my-ec2-config +# image: ami-6a5f7c2f +# size: t1.micro +# ssh_username: root +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 # Centos 6, available from ec2 marketplace for no-charge # http://wiki.centos.org/Cloud/AWS -centos_6: - provider: my-ec2-config - image: ami-f61630b3 - size: t1.micro - ssh_username: root - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#centos_6: +# provider: my-ec2-config +# image: ami-f61630b3 +# size: t1.micro +# ssh_username: root +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 # official Debian, available at no-charge from ec2 marketplace: # http://wiki.debian.org/Cloud/AmazonEC2Image -debian_squeeze_ec2: - provider: my-ec2-config - image: ami-2c735269 - size: t1.micro - ssh_username: admin - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#debian_squeeze_ec2: +# provider: my-ec2-config +# image: ami-2c735269 +# size: t1.micro +# ssh_username: admin +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 # Fedora project cloud images # https://fedoraproject.org/wiki/Cloud_images -fedora_17_ec2: - provider: my-ec2-config - image: ami-877e24c2 - size: t1.micro - ssh_username: ec2-user - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#fedora_17_ec2: +# provider: my-ec2-config +# image: ami-877e24c2 +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 -fedora_18_ec2: - provider: my-ec2-config - image: ami-0899b94d - size: t1.micro - ssh_username: ec2-user - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#fedora_18_ec2: +# provider: my-ec2-config +# image: ami-0899b94d +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 # FreeBSD 9.1 # http://www.daemonology.net/freebsd-on-ec2/ # this t1.micro instance does not auto-populate SSH keys see above link -freebsd_91_ec2: - provider: my-ec2-config - image: ami-4c8baa09 - size: t1.micro - ssh_username: ec2-user - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#freebsd_91_ec2: +# provider: my-ec2-config +# image: ami-4c8baa09 +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 # Canonical Ubuntu LTS images # http://cloud-images.ubuntu.com/releases/ -ubuntu_lucid_ec2: - provider: my-ec2-config - image: ami-e63013a3 - size: t1.micro - ssh_username: ubuntu - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 - -ubuntu_precise_ec2: - provider: my-ec2-config - image: ami-3ed8fb7b - size: t1.micro - ssh_username: ubuntu - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#ubuntu_lucid_ec2: +# provider: my-ec2-config +# image: ami-e63013a3 +# size: t1.micro +# ssh_username: ubuntu +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 +#ubuntu_precise_ec2: +# provider: my-ec2-config +# image: ami-3ed8fb7b +# size: t1.micro +# ssh_username: ubuntu +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 diff --git a/conf/cloud.profiles.d/EC2-us-west-2.profiles b/conf/cloud.profiles.d/EC2-us-west-2.profiles index e7d7ba8ce6..f5520eddb2 100644 --- a/conf/cloud.profiles.d/EC2-us-west-2.profiles +++ b/conf/cloud.profiles.d/EC2-us-west-2.profiles @@ -2,115 +2,114 @@ # Arch Linux # https://wiki.archlinux.org/index.php/Arch_Linux_AMIs_for_Amazon_Web_Services -arch_ec2: - provider: my-ec2-config - image: ami-bcf77e8c - size: t1.micro - ssh_username: root - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#arch_ec2: +# provider: my-ec2-config +# image: ami-bcf77e8c +# size: t1.micro +# ssh_username: root +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 -arch_cloud-init_ec2: - provider: my-ec2-config - image: ami-6a5f7c2f - size: t1.micro - ssh_username: root - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#arch_cloud-init_ec2: +# provider: my-ec2-config +# image: ami-6a5f7c2f +# size: t1.micro +# ssh_username: root +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 # Centos 6, available from ec2 marketplace for no-charge # http://wiki.centos.org/Cloud/AWS -centos_6: - provider: my-ec2-config - image: ami-de5bd2ee - size: t1.micro - ssh_username: root - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#centos_6: +# provider: my-ec2-config +# image: ami-de5bd2ee +# size: t1.micro +# ssh_username: root +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 # official Debian, available at no-charge from ec2 marketplace: # http://wiki.debian.org/Cloud/AmazonEC2Image -debian_squeeze_ec2: - provider: my-ec2-config - image: ami-e4da52d4 - size: t1.micro - ssh_username: admin - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#debian_squeeze_ec2: +# provider: my-ec2-config +# image: ami-e4da52d4 +# size: t1.micro +# ssh_username: admin +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 # Fedora project cloud images # https://fedoraproject.org/wiki/Cloud_images -fedora_17_ec2: - provider: my-ec2-config - image: ami-8e69e5be - size: t1.micro - ssh_username: ec2-user - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#fedora_17_ec2: +# provider: my-ec2-config +# image: ami-8e69e5be +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 -fedora_18_ec2: - provider: my-ec2-config - image: ami-0266ed32 - size: t1.micro - ssh_username: ec2-user - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#fedora_18_ec2: +# provider: my-ec2-config +# image: ami-0266ed32 +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 # FreeBSD 9.1 # http://www.daemonology.net/freebsd-on-ec2/ # this t1.micro instance does not auto-populate SSH keys see above link -freebsd_91_ec2: - provider: my-ec2-config - image: ami-aa09819a - size: t1.micro - ssh_username: ec2-user - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#freebsd_91_ec2: +# provider: my-ec2-config +# image: ami-aa09819a +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 -freebsd_91_4XL_ec2: - provider: my-ec2-config - image: ami-66169e56 - size: Cluster Compute 4XL - ssh_username: ec2-user - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#freebsd_91_4XL_ec2: +# provider: my-ec2-config +# image: ami-66169e56 +# size: Cluster Compute 4XL +# ssh_username: ec2-user +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 # Canonical Ubuntu LTS images # http://cloud-images.ubuntu.com/releases/ -ubuntu_lucid_ec2: - provider: my-ec2-config - image: ami-6ec8425e - size: t1.micro - ssh_username: ubuntu - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 - -ubuntu_precise_ec2: - provider: my-ec2-config - image: ami-e0941ed0 - size: t1.micro - ssh_username: ubuntu - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#ubuntu_lucid_ec2: +# provider: my-ec2-config +# image: ami-6ec8425e +# size: t1.micro +# ssh_username: ubuntu +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 +#ubuntu_precise_ec2: +# provider: my-ec2-config +# image: ami-e0941ed0 +# size: t1.micro +# ssh_username: ubuntu +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 From 71ebf2c8cd24aaa03f85db065e5701130cfe0d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Tue, 6 Sep 2016 11:21:05 +0100 Subject: [PATCH 031/100] Fixing skipped boto tests to prevent errors if boto3 does not exists. --- tests/unit/modules/boto_cloudtrail_test.py | 10 +++++----- tests/unit/modules/boto_iot_test.py | 10 +++++----- tests/unit/modules/boto_lambda_test.py | 10 +++++----- tests/unit/modules/boto_s3_bucket_test.py | 10 +++++----- tests/unit/modules/boto_vpc_test.py | 14 +++++++------- tests/unit/states/boto_cloudtrail_test.py | 10 +++++----- tests/unit/states/boto_iot_test.py | 10 +++++----- tests/unit/states/boto_lambda_test.py | 10 +++++----- tests/unit/states/boto_s3_bucket_test.py | 10 +++++----- 9 files changed, 47 insertions(+), 47 deletions(-) diff --git a/tests/unit/modules/boto_cloudtrail_test.py b/tests/unit/modules/boto_cloudtrail_test.py index 2f8610102a..264a795825 100644 --- a/tests/unit/modules/boto_cloudtrail_test.py +++ b/tests/unit/modules/boto_cloudtrail_test.py @@ -103,6 +103,11 @@ if _has_required_boto(): StopLoggingTime=None) +@skipIf(HAS_BOTO is False, 'The boto module must be installed.') +@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' + ' or equal to version {0}' + .format(required_boto3_version)) +@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCloudTrailTestCaseBase(TestCase): conn = None @@ -128,11 +133,6 @@ class BotoCloudTrailTestCaseMixin(object): pass -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCloudTrailTestCase(BotoCloudTrailTestCaseBase, BotoCloudTrailTestCaseMixin): ''' TestCase for salt.modules.boto_cloudtrail module diff --git a/tests/unit/modules/boto_iot_test.py b/tests/unit/modules/boto_iot_test.py index 73c362fc7c..520bfe939f 100644 --- a/tests/unit/modules/boto_iot_test.py +++ b/tests/unit/modules/boto_iot_test.py @@ -103,6 +103,11 @@ if _has_required_boto(): ruleDisabled=True) +@skipIf(HAS_BOTO is False, 'The boto module must be installed.') +@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' + ' or equal to version {0}' + .format(required_boto3_version)) +@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoIoTTestCaseBase(TestCase): conn = None @@ -128,11 +133,6 @@ class BotoIoTTestCaseMixin(object): pass -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoIoTPolicyTestCase(BotoIoTTestCaseBase, BotoIoTTestCaseMixin): ''' TestCase for salt.modules.boto_iot module diff --git a/tests/unit/modules/boto_lambda_test.py b/tests/unit/modules/boto_lambda_test.py index ec6d6d2ca3..02316a9cec 100644 --- a/tests/unit/modules/boto_lambda_test.py +++ b/tests/unit/modules/boto_lambda_test.py @@ -109,6 +109,11 @@ def _has_required_boto(): return True +@skipIf(HAS_BOTO is False, 'The boto module must be installed.') +@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' + ' or equal to version {0}' + .format(required_boto3_version)) +@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoLambdaTestCaseBase(TestCase): conn = None @@ -145,11 +150,6 @@ class BotoLambdaTestCaseMixin(object): pass -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoLambdaFunctionTestCase(BotoLambdaTestCaseBase, BotoLambdaTestCaseMixin): ''' TestCase for salt.modules.boto_lambda module diff --git a/tests/unit/modules/boto_s3_bucket_test.py b/tests/unit/modules/boto_s3_bucket_test.py index f4b1992198..5e7d6be7ba 100644 --- a/tests/unit/modules/boto_s3_bucket_test.py +++ b/tests/unit/modules/boto_s3_bucket_test.py @@ -205,6 +205,11 @@ if _has_required_boto(): } +@skipIf(HAS_BOTO is False, 'The boto module must be installed.') +@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' + ' or equal to version {0}' + .format(required_boto3_version)) +@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoS3BucketTestCaseBase(TestCase): conn = None @@ -230,11 +235,6 @@ class BotoS3BucketTestCaseMixin(object): pass -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoS3BucketTestCase(BotoS3BucketTestCaseBase, BotoS3BucketTestCaseMixin): ''' TestCase for salt.modules.boto_s3_bucket module diff --git a/tests/unit/modules/boto_vpc_test.py b/tests/unit/modules/boto_vpc_test.py index 64c7976685..162bcae4bc 100644 --- a/tests/unit/modules/boto_vpc_test.py +++ b/tests/unit/modules/boto_vpc_test.py @@ -124,6 +124,13 @@ def _has_required_moto(): context = {} +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(HAS_BOTO is False, 'The boto module must be installed.') +@skipIf(HAS_MOTO is False, 'The moto module must be installed.') +@skipIf(_has_required_boto() is False, 'The boto module must be greater than' + ' or equal to version {0}' + .format(required_boto_version)) +@skipIf(_has_required_moto() is False, 'The moto version must be >= to version {0}'.format(required_moto_version)) class BotoVpcTestCaseBase(TestCase): def setUp(self): boto_vpc.__context__ = {} @@ -249,13 +256,6 @@ class BotoVpcTestCaseMixin(object): return rtbl -@skipIf(NO_MOCK, NO_MOCK_REASON) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {0}' - .format(required_boto_version)) -@skipIf(_has_required_moto() is False, 'The moto version must be >= to version {0}'.format(required_moto_version)) class BotoVpcTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): ''' TestCase for salt.modules.boto_vpc module diff --git a/tests/unit/states/boto_cloudtrail_test.py b/tests/unit/states/boto_cloudtrail_test.py index 48fbd32bc1..9e6dd95257 100644 --- a/tests/unit/states/boto_cloudtrail_test.py +++ b/tests/unit/states/boto_cloudtrail_test.py @@ -104,6 +104,11 @@ if _has_required_boto(): StopLoggingTime=None) +@skipIf(HAS_BOTO is False, 'The boto module must be installed.') +@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' + ' or equal to version {0}' + .format(required_boto3_version)) +@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCloudTrailStateTestCaseBase(TestCase): conn = None @@ -124,11 +129,6 @@ class BotoCloudTrailStateTestCaseBase(TestCase): session_instance.client.return_value = self.conn -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoCloudTrailTestCase(BotoCloudTrailStateTestCaseBase, BotoCloudTrailTestCaseMixin): ''' TestCase for salt.modules.boto_cloudtrail state.module diff --git a/tests/unit/states/boto_iot_test.py b/tests/unit/states/boto_iot_test.py index 8c2549def1..81d68c8637 100644 --- a/tests/unit/states/boto_iot_test.py +++ b/tests/unit/states/boto_iot_test.py @@ -103,6 +103,11 @@ if _has_required_boto(): principal = 'arn:aws:iot:us-east-1:1234:cert/21fc104aaaf6043f5756c1b57bda84ea8395904c43f28517799b19e4c42514' +@skipIf(HAS_BOTO is False, 'The boto module must be installed.') +@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' + ' or equal to version {0}' + .format(required_boto3_version)) +@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoIoTStateTestCaseBase(TestCase): conn = None @@ -123,11 +128,6 @@ class BotoIoTStateTestCaseBase(TestCase): session_instance.client.return_value = self.conn -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoIoTPolicyTestCase(BotoIoTStateTestCaseBase, BotoIoTTestCaseMixin): ''' TestCase for salt.modules.boto_iot state.module diff --git a/tests/unit/states/boto_lambda_test.py b/tests/unit/states/boto_lambda_test.py index 4557aedbba..7b023912cd 100644 --- a/tests/unit/states/boto_lambda_test.py +++ b/tests/unit/states/boto_lambda_test.py @@ -101,6 +101,11 @@ def _has_required_boto(): return True +@skipIf(HAS_BOTO is False, 'The boto module must be installed.') +@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' + ' or equal to version {0}' + .format(required_boto3_version)) +@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoLambdaStateTestCaseBase(TestCase): conn = None @@ -121,11 +126,6 @@ class BotoLambdaStateTestCaseBase(TestCase): session_instance.client.return_value = self.conn -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoLambdaFunctionTestCase(BotoLambdaStateTestCaseBase, BotoLambdaTestCaseMixin): ''' TestCase for salt.modules.boto_lambda state.module diff --git a/tests/unit/states/boto_s3_bucket_test.py b/tests/unit/states/boto_s3_bucket_test.py index 4049e9ae20..03c406f26a 100644 --- a/tests/unit/states/boto_s3_bucket_test.py +++ b/tests/unit/states/boto_s3_bucket_test.py @@ -277,6 +277,11 @@ if _has_required_boto(): } +@skipIf(HAS_BOTO is False, 'The boto module must be installed.') +@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' + ' or equal to version {0}' + .format(required_boto3_version)) +@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoS3BucketStateTestCaseBase(TestCase): conn = None @@ -297,11 +302,6 @@ class BotoS3BucketStateTestCaseBase(TestCase): session_instance.client.return_value = self.conn -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -@skipIf(NO_MOCK, NO_MOCK_REASON) class BotoS3BucketTestCase(BotoS3BucketStateTestCaseBase, BotoS3BucketTestCaseMixin): ''' TestCase for salt.modules.boto_s3_bucket state.module From 25e3f2b4b8c39403a38de523e88006ab45686a44 Mon Sep 17 00:00:00 2001 From: rallytime Date: Tue, 6 Sep 2016 09:50:33 -0600 Subject: [PATCH 032/100] Use __utils__ instead of salt.utils.cloud in opennebula driver Also make sure to import salt.utils completely when using is_true and fopen functions. --- salt/cloud/clouds/opennebula.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/salt/cloud/clouds/opennebula.py b/salt/cloud/clouds/opennebula.py index 64c40c1f3d..df2188fb7f 100644 --- a/salt/cloud/clouds/opennebula.py +++ b/salt/cloud/clouds/opennebula.py @@ -61,10 +61,7 @@ from salt.exceptions import ( SaltCloudNotFound, SaltCloudSystemExit ) -from salt.utils import is_true - -# Import Salt Cloud Libs -import salt.utils.cloud +import salt.utils # Import Third Party Libs try: @@ -322,7 +319,7 @@ def list_nodes_select(call=None): 'The list_nodes_full function must be called with -f or --function.' ) - return salt.utils.cloud.list_nodes_select( + return __utils__['cloud.list_nodes_select']( list_nodes_full('function'), __opts__['query.selection'], call, ) @@ -913,7 +910,7 @@ def create(vm_): return node_data try: - data = salt.utils.cloud.wait_for_ip( + data = __utils__['cloud.wait_for_ip']( __query_node_data, update_args=(vm_['name'],), timeout=config.get_cloud_config_value( @@ -953,7 +950,7 @@ def create(vm_): vm_['key_filename'] = key_filename vm_['ssh_host'] = private_ip - ret = salt.utils.cloud.bootstrap(vm_, __opts__) + ret = __utils__['cloud.bootstrap'](vm_, __opts__) ret['id'] = data['id'] ret['image'] = vm_['image'] @@ -1028,7 +1025,7 @@ def destroy(name, call=None): ) if __opts__.get('update_cachedir', False) is True: - salt.utils.cloud.delete_minion_cachedir( + __utils__['cloud.delete_minion_cachedir']( name, __active_provider_name__.split(':')[0], __opts__ @@ -1373,7 +1370,7 @@ def image_persistent(call=None, kwargs=None): server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - response = server.one.image.persistent(auth, int(image_id), is_true(persist)) + response = server.one.image.persistent(auth, int(image_id), salt.utils.is_true(persist)) data = { 'action': 'image.persistent', @@ -1720,7 +1717,7 @@ def show_instance(name, call=None): ) node = _get_node(name) - salt.utils.cloud.cache_node(node, __active_provider_name__, __opts__) + __utils__['cloud.cache_node'](node, __active_provider_name__, __opts__) return node @@ -2586,7 +2583,7 @@ def vm_allocate(call=None, kwargs=None): server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) - response = server.one.vm.allocate(auth, data, is_true(hold)) + response = server.one.vm.allocate(auth, data, salt.utils.is_true(hold)) ret = { 'action': 'vm.allocate', @@ -2816,7 +2813,7 @@ def vm_deploy(name, kwargs=None, call=None): response = server.one.vm.deploy(auth, int(vm_id), int(host_id), - is_true(capacity_maintained), + salt.utils.is_true(capacity_maintained), int(datastore_id)) data = { @@ -3285,8 +3282,8 @@ def vm_migrate(name, kwargs=None, call=None): response = server.one.vm.migrate(auth, vm_id, int(host_id), - is_true(live_migration), - is_true(capacity_maintained), + salt.utils.is_true(live_migration), + salt.utils.is_true(capacity_maintained), int(datastore_id)) data = { @@ -3404,7 +3401,7 @@ def vm_resize(name, kwargs=None, call=None): server, user, password = _get_xml_rpc() auth = ':'.join([user, password]) vm_id = int(get_vm_id(kwargs={'name': name})) - response = server.one.vm.resize(auth, vm_id, data, is_true(capacity_maintained)) + response = server.one.vm.resize(auth, vm_id, data, salt.utils.is_true(capacity_maintained)) ret = { 'action': 'vm.resize', From 2934fc1a57b2d67ddb46fb46fd26e457271f3382 Mon Sep 17 00:00:00 2001 From: Seth House Date: Tue, 6 Sep 2016 10:18:52 -0600 Subject: [PATCH 033/100] Doc cherrypy deemphasize urlencoded (#36047) * Reword and clarify usage, clients & arg/kwarg, and content negotiation * Replace all urlencoded examples with JSON examples Using urlencoded format is a fast shorthand at the CLI with curl but it should really be avoided for any real work. It requires having to know a lot about the limitations and use of the format, and for complex tasks it requires knowing a lot about how Salt's CLI `name=val` string parsing works. It causes a lot of confusion and support issues and it should be the exception, not the rule. * Add one more mention of the full list of client interfaces --- salt/netapi/rest_cherrypy/app.py | 396 ++++++++++++++++++++----------- 1 file changed, 255 insertions(+), 141 deletions(-) diff --git a/salt/netapi/rest_cherrypy/app.py b/salt/netapi/rest_cherrypy/app.py index 3ba07f7247..2cde7576ae 100644 --- a/salt/netapi/rest_cherrypy/app.py +++ b/salt/netapi/rest_cherrypy/app.py @@ -125,7 +125,8 @@ Authentication Authentication is performed by passing a session token with each request. Tokens are generated via the :py:class:`Login` URL. -The token may be sent in one of two ways: +The token may be sent in one of two ways: as a custom header or as a session +cookie. The latter is far more convenient for clients that support cookies. * Include a custom header named :mailheader:`X-Auth-Token`. @@ -178,54 +179,204 @@ The token may be sent in one of two ways: Usage ----- -Commands are sent to a running Salt master via this module by sending HTTP -requests to the URLs detailed below. +This interface directly exposes Salt's :ref:`Python API `. +Everything possible at the CLI is possible through the Python API. Commands are +executed on the Salt Master. -.. admonition:: Content negotiation +The root URL (``/``) is RPC-like in that it accepts instructions in the request +body for what Salt functions to execute, and the response contains the result +of those function calls. - This REST interface is flexible in what data formats it will accept as well - as what formats it will return (e.g., JSON, YAML, x-www-form-urlencoded). +For example: - * Specify the format of data in the request body by including the - :mailheader:`Content-Type` header. - * Specify the desired data format for the response body with the - :mailheader:`Accept` header. +.. code-block:: text -Data sent in :http:method:`post` and :http:method:`put` requests must be in -the format of a list of lowstate dictionaries. This allows multiple commands to -be executed in a single HTTP request. The order of commands in the request -corresponds to the return for each command in the response. + % curl -sSi https://localhost:8000 \ + -H 'Content-type: application/json' \ + -d '[{ + "client": "local", + "tgt": "*", + "fun": "test.ping" + }]' + HTTP/1.1 200 OK + Content-Type: application/json + [...snip...] -Lowstate, broadly, is a dictionary of values that are mapped to a function -call. This pattern is used pervasively throughout Salt. The functions called -from netapi modules are described in :ref:`Client Interfaces `. + {"return": [{"jerry": true}]} -The following example (in JSON format) causes Salt to execute two commands, a -command sent to minions as well as a runner function on the master:: +The request body must be an array of commands. Use this workflow to build a +command: - [{ - "client": "local", - "tgt": "*", - "fun": "test.fib", - "arg": ["10"] - }, +1. Choose a client interface. +2. Choose a function. +3. Fill out the remaining parameters needed for the chosen client. + +The ``client`` field is a reference to the main Python classes used in Salt's +Python API. Read the full :ref:`client interfaces ` +documentation, but in short: + +* "local" uses :py:class:`LocalClient ` which sends + commands to Minions. Equivalent to the ``salt`` CLI command. +* "runner" uses :py:class:`RunnerClient ` which + invokes runner modules on the Master. Equivalent to the ``salt-run`` CLI + command. +* "wheel" uses :py:class:`WheelClient ` which invokes + wheel modules on the Master. Wheel modules do not have a direct CLI + equivalent but they typically manage Master-side resources such as state + files, pillar files, the Salt config files, and the :py:mod:`key wheel module + ` exposes similar functionality as the ``salt-key`` CLI + command. + +Most clients have variants like synchronous or asyncronous execution as well as +others like batch execution. See the :ref:`full list of client interfaces +`. + +Each client requires different arguments and sometimes has different syntax. +For example, ``LocalClient`` requires the ``tgt`` argument because it forwards +the command to Minions and the other client interfaces do not. ``LocalClient`` +also takes ``arg`` (array) and ``kwarg`` (dictionary) arguments because these +values are sent to the Minions and used to execute the requested function +there. ``RunnerClient`` and ``WheelClient`` are executed directly on the Master +and thus do not need or accept those arguments. + +Read the method signatures in the client documentation linked above, but +hopefully an example will help illustrate the concept. This example causes Salt +to execute two functions -- the :py:func:`test.arg execution function +` using ``LocalClient`` and the :py:func:`test.arg +runner function ` using ``RunnerClient``; note the +different structure for each command. The results for both are combined and +returned as one response. + +.. code-block:: text + + % curl -b ~/cookies.txt -sSi localhost:8000 \ + -H 'Content-type: application/json' \ + -d ' + [ + { + "client": "local", + "tgt": "*", + "fun": "test.arg", + "arg": ["positional arg one", "positional arg two"], + "kwarg": { + "keyword arg one": "Hello from a minion", + "keyword arg two": "Hello again from a minion" + } + }, + { + "client": "runner", + "fun": "test.arg", + "keyword arg one": "Hello from a master", + "keyword arg two": "Runners do not support positional args" + } + ] + ' + HTTP/1.1 200 OK + [...snip...] { - "client": "runner", - "fun": "jobs.lookup_jid", - "jid": "20130603122505459265" - }] + "return": [ + { + "jerry": { + "args": [ + "positional arg one", + "positional arg two" + ], + "kwargs": { + "keyword arg one": "Hello from a minion", + "keyword arg two": "Hello again from a minion", + [...snip...] + } + }, + [...snip; other minion returns here...] + }, + { + "args": [], + "kwargs": { + "keyword arg two": "Runners do not support positional args", + "keyword arg one": "Hello from a master" + } + } + ] + } -.. admonition:: x-www-form-urlencoded +One more example, this time with more commonly used functions: - Sending JSON or YAML in the request body is simple and most flexible, - however sending data in urlencoded format is also supported with the - caveats below. It is the default format for HTML forms, many JavaScript - libraries, and the :command:`curl` command. +.. code-block:: text - For example, the equivalent to running ``salt '*' test.ping`` is sending - ``fun=test.ping&arg&client=local&tgt=*`` in the HTTP request body. + curl -b /tmp/cookies.txt -sSi localhost:8000 \ + -H 'Content-type: application/json' \ + -d ' + [ + { + "client": "local", + "tgt": "*", + "fun": "state.sls", + "kwarg": { + "mods": "apache", + "pillar": { + "lookup": { + "wwwdir": "/srv/httpd/htdocs" + } + } + } + }, + { + "client": "runner", + "fun": "cloud.create", + "provider": "my-ec2-provider", + "instances": "my-centos-6", + "image": "ami-1624987f", + "delvol_on_destroy", true + } + ] + ' + HTTP/1.1 200 OK + [...snip...] + { + "return": [ + { + "jerry": { + "pkg_|-install_apache_|-httpd_|-installed": { + [...snip full state return here...] + } + } + [...snip other minion returns here...] + }, + { + [...snip full salt-cloud output here...] + } + ] + } - Caveats: +Content negotiation +------------------- + +This REST interface is flexible in what data formats it will accept as well +as what formats it will return (e.g., JSON, YAML, urlencoded). + +* Specify the format of data in the request body by including the + :mailheader:`Content-Type` header. +* Specify the desired data format for the response body with the + :mailheader:`Accept` header. + +We recommend the JSON format for most HTTP requests. urlencoded data is simple +and cannot express complex data structures -- and that is often required for +some Salt commands, such as starting a state run that uses Pillar data. Salt's +CLI tool can reformat strings passed in at the CLI into complex data +structures, and that behavior also works via salt-api, but that can be brittle +and since salt-api can accept JSON it is best just to send JSON. + +Here is an example of sending urlencoded data: + +.. code-block:: bash + + curl -sSik https://localhost:8000 \\ + -b ~/cookies.txt \\ + -d client=runner \\ + -d fun='jobs.lookup_jid' \\ + -d jid='20150129182456704682' + +.. admonition:: urlencoded data caveats * Only a single command may be sent per HTTP request. * Repeating the ``arg`` parameter multiple times will cause those @@ -233,9 +384,23 @@ command sent to minions as well as a runner function on the master:: Note, some popular frameworks and languages (notably jQuery, PHP, and Ruby on Rails) will automatically append empty brackets onto repeated - parameters. E.g., ``arg=one``, ``arg=two`` will be sent as ``arg[]=one``, - ``arg[]=two``. This is not supported; send JSON or YAML instead. + query string parameters. E.g., ``?foo[]=fooone&foo[]=footwo``. This is + **not** supported; send ``?foo=fooone&foo=footwo`` instead, or send JSON + or YAML. + A note about ``curl`` + + The ``-d`` flag to curl does *not* automatically urlencode data which can + affect passwords and other data that contains characters that must be + encoded. Use the ``--data-urlencode`` flag instead. E.g.: + + .. code-block:: bash + + curl -ksi http://localhost:8000/login \\ + -H "Accept: application/json" \\ + -d username='myapiuser' \\ + --data-urlencode password='1234+' \\ + -d eauth='pam' .. |req_token| replace:: a session token from :py:class:`~Login`. .. |req_accept| replace:: the desired response format. @@ -248,21 +413,6 @@ command sent to minions as well as a runner function on the master:: .. |401| replace:: authentication required .. |406| replace:: requested Content-Type not available -A Note About Curl -================= - -When sending passwords and data that might need to be urlencoded, you must set -the ``-d`` flag to indicate the content type, and the ``--data-urlencode`` flag -to urlencode the input. - -.. code-block:: bash - - curl -ksi http://localhost:8000/login \\ - -H "Accept: application/json" \\ - -d username='myapiuser' \\ - --data-urlencode password='1234+' \\ - -d eauth='pam' - ''' # We need a custom pylintrc here... # pylint: disable=W0212,E1101,C0103,R0201,W0221,W0613 @@ -882,11 +1032,10 @@ class LowDataAdapter(object): .. code-block:: bash curl -sSik https://localhost:8000 \\ - -H "Accept: application/x-yaml" \\ - -H "X-Auth-Token: d40d1e1e<...snip...>" \\ - -d client=local \\ - -d tgt='*' \\ - -d fun='test.ping' \\ + -b ~/cookies.txt \\ + -H "Accept: application/x-yaml" \\ + -H "Content-type: application/json" \\ + -d '[{"client": "local", "tgt": "*", "fun": "test.ping"}]' .. code-block:: http @@ -894,10 +1043,9 @@ class LowDataAdapter(object): Host: localhost:8000 Accept: application/x-yaml X-Auth-Token: d40d1e1e - Content-Length: 36 - Content-Type: application/x-www-form-urlencoded + Content-Type: application/json - fun=test.ping&client=local&tgt=* + [{"client": "local", "tgt": "*", "fun": "test.ping"}] **Example response:** @@ -910,55 +1058,10 @@ class LowDataAdapter(object): return: - ms-0: true - ms-1: true - ms-2: true - ms-3: true - ms-4: true - - **Other examples**: - - .. code-block:: bash - - # Sending multiple positional args with urlencoded: - curl -sSik https://localhost:8000 \\ - -d client=local \\ - -d tgt='*' \\ - -d fun='cmd.run' \\ - -d arg='du -sh .' \\ - -d arg='/path/to/dir' - - # Sending positional args and Keyword args with JSON: - echo '[ - { - "client": "local", - "tgt": "*", - "fun": "cmd.run", - "arg": [ - "du -sh .", - "/path/to/dir" - ], - "kwarg": { - "shell": "/bin/sh", - "template": "jinja" - } - } - ]' | curl -sSik https://localhost:8000 \\ - -H 'Content-type: application/json' \\ - -d@- - - # Calling runner functions: - curl -sSik https://localhost:8000 \\ - -d client=runner \\ - -d fun='jobs.lookup_jid' \\ - -d jid='20150129182456704682' \\ - -d outputter=highstate - - # Calling wheel functions: - curl -sSik https://localhost:8000 \\ - -d client=wheel \\ - -d fun='key.gen_accept' \\ - -d id_=dave \\ - -d keysize=4096 + ms-1: true + ms-2: true + ms-3: true + ms-4: true ''' return { 'return': list(self.exec_lowstate( @@ -1047,17 +1150,16 @@ class Minions(LowDataAdapter): .. code-block:: bash curl -sSi localhost:8000/minions \\ + -b ~/cookies.txt \\ -H "Accept: application/x-yaml" \\ - -d tgt='*' \\ - -d fun='status.diskusage' + -d '[{"tgt": "*", "fun": "status.diskusage"}]' .. code-block:: http POST /minions HTTP/1.1 Host: localhost:8000 Accept: application/x-yaml - Content-Length: 26 - Content-Type: application/x-www-form-urlencoded + Content-Type: application/json tgt=*&fun=status.diskusage @@ -1473,20 +1575,25 @@ class Login(LowDataAdapter): .. code-block:: bash curl -si localhost:8000/login \\ - -H "Accept: application/json" \\ - -d username='saltuser' \\ - -d password='saltpass' \\ - -d eauth='pam' + -c ~/cookies.txt \\ + -H "Accept: application/json" \\ + -H "Content-type: application/json" \\ + -d '{ + "username": "saltuser", + "password": "saltuser", + "eauth": "auto" + }' .. code-block:: http POST / HTTP/1.1 Host: localhost:8000 Content-Length: 42 - Content-Type: application/x-www-form-urlencoded + Content-Type: application/json Accept: application/json - username=saltuser&password=saltpass&eauth=pam + {"username": "saltuser", "password": "saltuser", "eauth": "auto"} + **Example response:** @@ -1626,12 +1733,15 @@ class Run(LowDataAdapter): curl -sS localhost:8000/run \\ -H 'Accept: application/x-yaml' \\ - -d client='local' \\ - -d tgt='*' \\ - -d fun='test.ping' \\ - -d username='saltdev' \\ - -d password='saltdev' \\ - -d eauth='pam' + -H 'Content-type: application/json' \\ + -d '[{ + "client": "local", + "tgt": "*", + "fun": "test.ping", + "username": "saltdev", + "password": "saltdev", + "eauth": "auto" + }]' .. code-block:: http @@ -1639,9 +1749,9 @@ class Run(LowDataAdapter): Host: localhost:8000 Accept: application/x-yaml Content-Length: 75 - Content-Type: application/x-www-form-urlencoded + Content-Type: application/json - client=local&tgt=*&fun=test.ping&username=saltdev&password=saltdev&eauth=pam + [{"client": "local", "tgt": "*", "fun": "test.ping", "username": "saltdev", "password": "saltdev", "eauth": "auto"}] **Example response:** @@ -1653,17 +1763,19 @@ class Run(LowDataAdapter): return: - ms-0: true - ms-1: true - ms-2: true - ms-3: true - ms-4: true + ms-1: true + ms-2: true + ms-3: true + ms-4: true - The /run enpoint can also be used to issue commands using the salt-ssh subsystem. + The /run enpoint can also be used to issue commands using the salt-ssh + subsystem. - When using salt-ssh, eauth credentials should not be supplied. Instad, authentication - should be handled by the SSH layer itself. The use of the salt-ssh client does not - require a salt master to be running. Instead, only a roster file must be present - in the salt configuration directory. + When using salt-ssh, eauth credentials should not be supplied. Instad, + authentication should be handled by the SSH layer itself. The use of + the salt-ssh client does not require a salt master to be running. + Instead, only a roster file must be present in the salt configuration + directory. All SSH client requests are synchronous. @@ -2176,16 +2288,18 @@ class Webhook(object): .. code-block:: bash - curl -sS localhost:8000/hook -d foo='Foo!' -d bar='Bar!' + curl -sS localhost:8000/hook \\ + -H 'Content-type: application/json' \\ + -d '{"foo": "Foo!", "bar": "Bar!"}' .. code-block:: http POST /hook HTTP/1.1 Host: localhost:8000 Content-Length: 16 - Content-Type: application/x-www-form-urlencoded + Content-Type: application/json - foo=Foo&bar=Bar! + {"foo": "Foo!", "bar": "Bar!"} **Example response**: From a86e36cd310157e0df8254798ea1e62cc7b2cd97 Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Tue, 6 Sep 2016 10:19:24 -0600 Subject: [PATCH 034/100] Add docs for new kwargs added to the wheel key module (#36040) * Add docs for new kwargs added to the wheel key module Refs #36030 * Fix copy-paste error: use the correct kwarg name/description --- salt/wheel/key.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/salt/wheel/key.py b/salt/wheel/key.py index ec94c98423..94f8a81530 100644 --- a/salt/wheel/key.py +++ b/salt/wheel/key.py @@ -108,6 +108,18 @@ def accept_dict(match, include_rejected=False, include_denied=False): match The dictionary of keys to accept. + include_rejected + To include rejected keys in the match along with pending keys, set this + to ``True``. Defaults to ``False``. + + .. versionadded:: 2016.3.4 + + include_denied + To include denied keys in the match along with pending keys, set this + to ``True``. Defaults to ``False``. + + .. versionadded:: 2016.3.4 + Example to move a list of keys from the ``minions_pre`` (pending) directory to the ``minions`` (accepted) directory: @@ -199,6 +211,18 @@ def reject_dict(match, include_accepted=False, include_denied=False): match The dictionary of keys to reject. + include_accepted + To include accepted keys in the match along with pending keys, set this + to ``True``. Defaults to ``False``. + + .. versionadded:: 2016.3.4 + + include_denied + To include denied keys in the match along with pending keys, set this + to ``True``. Defaults to ``False``. + + .. versionadded:: 2016.3.4 + .. code-block:: python >>> wheel.cmd_async({'fun': 'key.reject_dict', From fa0905015023a73653de1ee0d060612677b8bc8e Mon Sep 17 00:00:00 2001 From: Jonathan Ballet Date: Tue, 6 Sep 2016 18:43:04 +0200 Subject: [PATCH 035/100] consul: fix formatting of consul.agent_join (#36061) --- salt/modules/consul.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/salt/modules/consul.py b/salt/modules/consul.py index 0c5e3e6b75..73b661c455 100644 --- a/salt/modules/consul.py +++ b/salt/modules/consul.py @@ -609,11 +609,10 @@ def agent_join(consul_url=None, address=None, **kwargs): query_params=query_params) if res['res']: ret['res'] = True - ret['message'] = ('Agent maintenance mode ' - '{0}ed.'.format(kwargs['enable'])) + ret['message'] = 'Agent joined the cluster' else: ret['res'] = False - ret['message'] = 'Unable to change maintenance mode for agent.' + ret['message'] = 'Unable to join the cluster.' return ret From 9b36904149d6bc04656866467fc4c5be64018cf9 Mon Sep 17 00:00:00 2001 From: Thomas S Hatch Date: Tue, 6 Sep 2016 12:32:01 -0600 Subject: [PATCH 036/100] Fix failhard causing test=True to failhard too soon --- salt/state.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/salt/state.py b/salt/state.py index 4e4643af79..96971e8f2f 100644 --- a/salt/state.py +++ b/salt/state.py @@ -1809,6 +1809,8 @@ class State(object): tag = _gen_tag(low) if (low.get('failhard', False) or self.opts['failhard'] and tag in running): + if running[tag]['result'] is None: + return False return not running[tag]['result'] return False From af0d4268cd5c0e9bc013c308b827b7408cd5850c Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Tue, 6 Sep 2016 13:46:51 -0600 Subject: [PATCH 037/100] Import "salt.ext.six" instead of "six" (#36076) The Windows distribution doesn't come with `six` as a pre-installed python package. Since `salt.ext.six` already exists, it makes sense to use that instead of adding `six` to the Windows distribution. Signed-off-by: Sergey Kizunov --- salt/modules/cisconso.py | 2 +- salt/returners/sentry_return.py | 2 +- salt/returners/syslog_return.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/salt/modules/cisconso.py b/salt/modules/cisconso.py index 30f3fb7f3f..8755ab17dd 100644 --- a/salt/modules/cisconso.py +++ b/salt/modules/cisconso.py @@ -9,8 +9,8 @@ for :doc:`salt.proxy.cisconso`. ''' from __future__ import absolute_import -import six import salt.utils +import salt.ext.six as six __proxyenabled__ = ['cisconso'] __virtualname__ = 'cisconso' diff --git a/salt/returners/sentry_return.py b/salt/returners/sentry_return.py index 58816946ea..52bc9f39a6 100644 --- a/salt/returners/sentry_return.py +++ b/salt/returners/sentry_return.py @@ -45,10 +45,10 @@ from __future__ import absolute_import # Import Python libs import logging -import six # Import Salt libs import salt.utils.jid +import salt.ext.six as six try: from raven import Client diff --git a/salt/returners/syslog_return.py b/salt/returners/syslog_return.py index a34ea847f2..4300b53d50 100644 --- a/salt/returners/syslog_return.py +++ b/salt/returners/syslog_return.py @@ -88,7 +88,6 @@ To override individual configuration items, append ''' from __future__ import absolute_import import logging -import six # Import python libs import json @@ -101,6 +100,7 @@ except ImportError: # Import Salt libs import salt.utils.jid import salt.returners +import salt.ext.six as six log = logging.getLogger(__name__) # Define the module's virtual name From fa24c8e789cc6aa9ca7aaa3a110ae885581e2515 Mon Sep 17 00:00:00 2001 From: Joseph Hall Date: Tue, 6 Sep 2016 14:18:36 -0600 Subject: [PATCH 038/100] Add docstrings for thorium modules (#36077) * Add docstrings for thorium modules * Formatting --- salt/thorium/calc.py | 18 +++---- salt/thorium/check.py | 111 +++++++++++++++++++++++++++++++++++++++-- salt/thorium/file.py | 13 ++++- salt/thorium/key.py | 2 +- salt/thorium/local.py | 23 +++++++++ salt/thorium/reg.py | 32 ++++++++++-- salt/thorium/runner.py | 11 +++- salt/thorium/timer.py | 8 +++ salt/thorium/wheel.py | 2 + 9 files changed, 200 insertions(+), 20 deletions(-) diff --git a/salt/thorium/calc.py b/salt/thorium/calc.py index f7b6e92b28..042d624bae 100644 --- a/salt/thorium/calc.py +++ b/salt/thorium/calc.py @@ -40,7 +40,7 @@ def calc(name, num, oper, minimum=0, maximum=0, ref=None): - mode: Calculate mode of last ``num`` values - USAGE:: + USAGE: code-block:: yaml @@ -113,7 +113,7 @@ def add(name, num, minimum=0, maximum=0, ref=None): ''' Adds together the ``num`` most recent values. Requires a list. - USAGE:: + USAGE: code-block:: yaml @@ -129,7 +129,7 @@ def mul(name, num, minimum=0, maximum=0, ref=None): ''' Multiplies together the ``num`` most recent values. Requires a list. - USAGE:: + USAGE: code-block:: yaml @@ -145,7 +145,7 @@ def mean(name, num, minimum=0, maximum=0, ref=None): ''' Calculates the mean of the ``num`` most recent values. Requires a list. - USAGE:: + USAGE: code-block:: yaml @@ -161,7 +161,7 @@ def median(name, num, minimum=0, maximum=0, ref=None): ''' Calculates the mean of the ``num`` most recent values. Requires a list. - USAGE:: + USAGE: code-block:: yaml @@ -177,7 +177,7 @@ def median_low(name, num, minimum=0, maximum=0, ref=None): ''' Calculates the low mean of the ``num`` most recent values. Requires a list. - USAGE:: + USAGE: code-block:: yaml @@ -193,7 +193,7 @@ def median_high(name, num, minimum=0, maximum=0, ref=None): ''' Calculates the high mean of the ``num`` most recent values. Requires a list. - USAGE:: + USAGE: code-block:: yaml @@ -210,7 +210,7 @@ def median_grouped(name, num, minimum=0, maximum=0, ref=None): Calculates the grouped mean of the ``num`` most recent values. Requires a list. - USAGE:: + USAGE: code-block:: yaml @@ -226,7 +226,7 @@ def mode(name, num, minimum=0, maximum=0, ref=None): ''' Calculates the mode of the ``num`` most recent values. Requires a list. - USAGE:: + USAGE: code-block:: yaml diff --git a/salt/thorium/check.py b/salt/thorium/check.py index 715a80bc4c..6bde8eb8d3 100644 --- a/salt/thorium/check.py +++ b/salt/thorium/check.py @@ -14,6 +14,21 @@ def gt(name, value): ''' Only succeed if the value in the given register location is greater than the given value + + USAGE: + + .. code-block:: yaml + + foo: + check.gt: + - value: 42 + + run_remote_ex: + local.cmd: + - tgt: '*' + - func: test.ping + - require: + - check: foo ''' ret = {'name': name, 'result': False, @@ -32,6 +47,21 @@ def gte(name, value): ''' Only succeed if the value in the given register location is greater or equal than the given value + + USAGE: + + .. code-block:: yaml + + foo: + check.gte: + - value: 42 + + run_remote_ex: + local.cmd: + - tgt: '*' + - func: test.ping + - require: + - check: foo ''' ret = {'name': name, 'result': False, @@ -50,6 +80,21 @@ def lt(name, value): ''' Only succeed if the value in the given register location is less than the given value + + USAGE: + + .. code-block:: yaml + + foo: + check.lt: + - value: 42 + + run_remote_ex: + local.cmd: + - tgt: '*' + - func: test.ping + - require: + - check: foo ''' ret = {'name': name, 'result': False, @@ -68,6 +113,21 @@ def lte(name, value): ''' Only succeed if the value in the given register location is less than or equal the given value + + USAGE: + + .. code-block:: yaml + + foo: + check.lte: + - value: 42 + + run_remote_ex: + local.cmd: + - tgt: '*' + - func: test.ping + - require: + - check: foo ''' ret = {'name': name, 'result': False, @@ -86,6 +146,21 @@ def eq(name, value): ''' Only succeed if the value in the given register location is equal to the given value + + USAGE: + + .. code-block:: yaml + + foo: + check.eq: + - value: 42 + + run_remote_ex: + local.cmd: + - tgt: '*' + - func: test.ping + - require: + - check: foo ''' ret = {'name': name, 'result': False, @@ -104,6 +179,21 @@ def ne(name, value): ''' Only succeed if the value in the given register location is not equal to the given value + + USAGE: + + .. code-block:: yaml + + foo: + check.ne: + - value: 42 + + run_remote_ex: + local.cmd: + - tgt: '*' + - func: test.ping + - require: + - check: foo ''' ret = {'name': name, 'result': False, @@ -120,8 +210,23 @@ def ne(name, value): def contains(name, value): ''' - Only succeed if the value in the given register location is greater than + Only succeed if the value in the given register location contains the given value + + USAGE: + + .. code-block:: yaml + + foo: + check.contains: + - value: itni + + run_remote_ex: + local.cmd: + - tgt: '*' + - func: test.ping + - require: + - check: foo ''' ret = {'name': name, 'result': False, @@ -144,9 +249,9 @@ def event(name): Chekcs for a specific event match and returns result True if the match happens - USAGE:: + USAGE: - code-block:: yaml + .. code-block:: yaml salt/foo/*/bar: check.event diff --git a/salt/thorium/file.py b/salt/thorium/file.py index 90809b3e91..7260ea74e3 100644 --- a/salt/thorium/file.py +++ b/salt/thorium/file.py @@ -46,7 +46,18 @@ from salt.utils import simple_types_filter def save(name, filter=False): ''' - Save the register to /thorium/saves/ + Save the register to /thorium/saves/, or to an + absolute path. + + USAGE: + + .. code-block:: yaml + + foo: + file.save + + /tmp/foo: + file.save ''' ret = {'name': name, 'changes': {}, diff --git a/salt/thorium/key.py b/salt/thorium/key.py index 33a1b0ae08..a15a876f69 100644 --- a/salt/thorium/key.py +++ b/salt/thorium/key.py @@ -27,7 +27,7 @@ def timeout(name, delete=0, reject=0): given action to the timed out key. This example will remove keys to minions that have not checked in for 300 seconds (5 minutes) - USAGE:: + USAGE: code-block:: yaml statreg: diff --git a/salt/thorium/local.py b/salt/thorium/local.py index 0f701760ee..e17224baaf 100644 --- a/salt/thorium/local.py +++ b/salt/thorium/local.py @@ -20,6 +20,29 @@ def cmd( **kwargs): ''' Execute a remote execution command + + USAGE: + + .. code-block:: yaml + + run_remote_ex: + local.cmd: + - tgt: '*' + - func: test.ping + + run_remote_ex: + local.cmd: + - tgt: '*' + - func: test.sleep + - arg: + - 30 + + run_remote_ex: + local.cmd: + - tgt: '*' + - func: test.sleep + - kwarg: + length: 30 ''' ret = {'name': name, 'changes': {}, diff --git a/salt/thorium/reg.py b/salt/thorium/reg.py index 9e438fc167..ff85c4a800 100644 --- a/salt/thorium/reg.py +++ b/salt/thorium/reg.py @@ -17,6 +17,15 @@ __func_alias__ = { def set_(name, add, match): ''' Add a value to the named set + + USAGE: + + .. code-block:: yaml + + foo: + reg.set: + - add: bar + - match: my/custom/event ''' ret = {'name': name, 'changes': {}, @@ -46,7 +55,7 @@ def list_(name, add, match, stamp=False, prune=0): if ``prune`` is set to an integer higher than ``0``, then only the last ``prune`` values will be kept in the list. - USAGE:: + USAGE: code-block:: yaml @@ -88,6 +97,15 @@ def mean(name, add, match): Accept a numeric value from the matched events and store a running average of the values in the given register. If the specified value is not numeric it will be skipped + + USAGE: + + .. code-block:: yaml + + foo: + reg.mean: + - add: data_field + - match: my/custom/event ''' ret = {'name': name, 'changes': {}, @@ -99,10 +117,14 @@ def mean(name, add, match): __reg__[name]['total'] = 0 __reg__[name]['count'] = 0 for event in __events__: + try: + event_data = event['data']['data'] + except KeyError: + event_data = event['data'] if salt.utils.expr_match(event['tag'], match): - if add in event['data']['data']: + if add in event_data: try: - comp = int(event['data']['data']) + comp = int(event_data) except ValueError: continue __reg__[name]['total'] += comp @@ -115,7 +137,7 @@ def clear(name): ''' Clear the namespace from the register - USAGE:: + USAGE: code-block:: yaml @@ -136,7 +158,7 @@ def delete(name): ''' Delete the namespace from the register - USAGE:: + USAGE: code-block:: yaml diff --git a/salt/thorium/runner.py b/salt/thorium/runner.py index fa80fc52ae..33cf363802 100644 --- a/salt/thorium/runner.py +++ b/salt/thorium/runner.py @@ -16,14 +16,23 @@ def cmd( ''' Execute a runner async: + USAGE: + .. code-block:: yaml run_cloud: runner.cmd: - fun: cloud.create - - args: + - arg: - my-ec2-config - myinstance + + run_cloud: + runner.cmd: + - fun: cloud.create + - kwargs: + provider: my-ec2-config + instances: myinstance ''' ret = {'name': name, 'changes': {}, diff --git a/salt/thorium/timer.py b/salt/thorium/timer.py index 2b6c9cf5ec..9a0141d26f 100644 --- a/salt/thorium/timer.py +++ b/salt/thorium/timer.py @@ -13,6 +13,14 @@ def hold(name, seconds): Wait for a given period of time, then fire a result of True, requiring this state allows for an action to be blocked for evaluation based on time + + USAGE: + + .. code-block:: yaml + + hold_on_a_moment: + timer.hold: + - seconds: 30 ''' ret = {'name': name, 'result': False, diff --git a/salt/thorium/wheel.py b/salt/thorium/wheel.py index 0a77645ffc..73e73552b4 100644 --- a/salt/thorium/wheel.py +++ b/salt/thorium/wheel.py @@ -16,6 +16,8 @@ def cmd( ''' Execute a runner async: + USAGE: + .. code-block:: yaml run_cloud: From 52c580430343f6ca4b529d856289576da0adb7bc Mon Sep 17 00:00:00 2001 From: rallytime Date: Tue, 6 Sep 2016 14:29:38 -0600 Subject: [PATCH 039/100] Make sure all thorium docs have correct formatting tags --- salt/thorium/calc.py | 18 +++++++++--------- salt/thorium/file.py | 4 ++++ salt/thorium/key.py | 3 ++- salt/thorium/reg.py | 6 +++--- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/salt/thorium/calc.py b/salt/thorium/calc.py index 042d624bae..6d46a41f2e 100644 --- a/salt/thorium/calc.py +++ b/salt/thorium/calc.py @@ -42,7 +42,7 @@ def calc(name, num, oper, minimum=0, maximum=0, ref=None): USAGE: - code-block:: yaml + .. code-block:: yaml foo: reg.calc: @@ -115,7 +115,7 @@ def add(name, num, minimum=0, maximum=0, ref=None): USAGE: - code-block:: yaml + .. code-block:: yaml foo: reg.add: @@ -131,7 +131,7 @@ def mul(name, num, minimum=0, maximum=0, ref=None): USAGE: - code-block:: yaml + .. code-block:: yaml foo: reg.mul: @@ -147,7 +147,7 @@ def mean(name, num, minimum=0, maximum=0, ref=None): USAGE: - code-block:: yaml + .. code-block:: yaml foo: reg.mean: @@ -163,7 +163,7 @@ def median(name, num, minimum=0, maximum=0, ref=None): USAGE: - code-block:: yaml + .. code-block:: yaml foo: reg.median: @@ -179,7 +179,7 @@ def median_low(name, num, minimum=0, maximum=0, ref=None): USAGE: - code-block:: yaml + .. code-block:: yaml foo: reg.median_low: @@ -195,7 +195,7 @@ def median_high(name, num, minimum=0, maximum=0, ref=None): USAGE: - code-block:: yaml + .. code-block:: yaml foo: reg.median_high: @@ -212,7 +212,7 @@ def median_grouped(name, num, minimum=0, maximum=0, ref=None): USAGE: - code-block:: yaml + .. code-block:: yaml foo: reg.median_grouped: @@ -228,7 +228,7 @@ def mode(name, num, minimum=0, maximum=0, ref=None): USAGE: - code-block:: yaml + .. code-block:: yaml foo: reg.mode: diff --git a/salt/thorium/file.py b/salt/thorium/file.py index 7260ea74e3..82150670c9 100644 --- a/salt/thorium/file.py +++ b/salt/thorium/file.py @@ -18,6 +18,8 @@ Then the file will be saved to: You may also provide an absolute path for the file to be saved to: +.. code-block:: yaml + /tmp/foo.save: file.save @@ -26,6 +28,8 @@ If you are saving a register entry that contains a ``set()``, then it will fail to save to JSON format. However, you may pass data through a filter which makes it JSON compliant: +.. code-block:: yaml + foo: file.save: filter: True diff --git a/salt/thorium/key.py b/salt/thorium/key.py index a15a876f69..30ca91b43d 100644 --- a/salt/thorium/key.py +++ b/salt/thorium/key.py @@ -28,7 +28,8 @@ def timeout(name, delete=0, reject=0): minions that have not checked in for 300 seconds (5 minutes) USAGE: - code-block:: yaml + + .. code-block:: yaml statreg: status.reg diff --git a/salt/thorium/reg.py b/salt/thorium/reg.py index ff85c4a800..99b856a736 100644 --- a/salt/thorium/reg.py +++ b/salt/thorium/reg.py @@ -57,7 +57,7 @@ def list_(name, add, match, stamp=False, prune=0): USAGE: - code-block:: yaml + .. code-block:: yaml foo: reg.list: @@ -139,7 +139,7 @@ def clear(name): USAGE: - code-block:: yaml + .. code-block:: yaml clearns: reg.clear: @@ -160,7 +160,7 @@ def delete(name): USAGE: - code-block:: yaml + .. code-block:: yaml deletens: reg.delete: From 9be71e5a5d1f5d330e18201cfc6f44186eccedfe Mon Sep 17 00:00:00 2001 From: twangboy Date: Thu, 1 Sep 2016 11:12:32 -0600 Subject: [PATCH 040/100] Check for Desktop, fix some functions --- salt/modules/mac_power.py | 15 +-- tests/integration/modules/mac_power.py | 132 +++++++++++++++---------- 2 files changed, 90 insertions(+), 57 deletions(-) diff --git a/salt/modules/mac_power.py b/salt/modules/mac_power.py index 781c8f25c5..c89ff90250 100644 --- a/salt/modules/mac_power.py +++ b/salt/modules/mac_power.py @@ -281,7 +281,7 @@ def set_wake_on_modem(enabled): state = salt.utils.mac_utils.validate_enabled(enabled) cmd = 'systemsetup -setwakeonmodem {0}'.format(state) salt.utils.mac_utils.execute_return_success(cmd) - return get_wake_on_modem() == state + return salt.utils.mac_utils.validate_enabled(get_wake_on_modem()) == state def get_wake_on_network(): @@ -324,7 +324,8 @@ def set_wake_on_network(enabled): state = salt.utils.mac_utils.validate_enabled(enabled) cmd = 'systemsetup -setwakeonnetworkaccess {0}'.format(state) salt.utils.mac_utils.execute_return_success(cmd) - return get_wake_on_network() == state + return salt.utils.mac_utils.validate_enabled( + get_wake_on_network()) == state def get_restart_power_failure(): @@ -367,7 +368,8 @@ def set_restart_power_failure(enabled): state = salt.utils.mac_utils.validate_enabled(enabled) cmd = 'systemsetup -setrestartpowerfailure {0}'.format(state) salt.utils.mac_utils.execute_return_success(cmd) - return get_restart_power_failure() == state + return salt.utils.mac_utils.validate_enabled( + get_restart_power_failure()) == state def get_restart_freeze(): @@ -412,7 +414,7 @@ def set_restart_freeze(enabled): state = salt.utils.mac_utils.validate_enabled(enabled) cmd = 'systemsetup -setrestartfreeze {0}'.format(state) salt.utils.mac_utils.execute_return_success(cmd) - return get_restart_freeze() == state + return salt.utils.mac_utils.validate_enabled(get_restart_freeze()) == state def get_sleep_on_power_button(): @@ -433,7 +435,7 @@ def get_sleep_on_power_button(): ret = salt.utils.mac_utils.execute_return_result( 'systemsetup -getallowpowerbuttontosleepcomputer') return salt.utils.mac_utils.validate_enabled( - salt.utils.mac_utils.parse_return(ret)) == 'on' + salt.utils.mac_utils.parse_return(ret)) == 'on' def set_sleep_on_power_button(enabled): @@ -456,4 +458,5 @@ def set_sleep_on_power_button(enabled): state = salt.utils.mac_utils.validate_enabled(enabled) cmd = 'systemsetup -setallowpowerbuttontosleepcomputer {0}'.format(state) salt.utils.mac_utils.execute_return_success(cmd) - return get_sleep_on_power_button() == state + return salt.utils.mac_utils.validate_enabled( + get_sleep_on_power_button()) == state diff --git a/tests/integration/modules/mac_power.py b/tests/integration/modules/mac_power.py index c62e0e083f..3d373d90d8 100644 --- a/tests/integration/modules/mac_power.py +++ b/tests/integration/modules/mac_power.py @@ -14,10 +14,10 @@ ensure_in_syspath('../../') # Import salt libs import integration import salt.utils - +from salt.exceptions import CommandExecutionError @skipIf(not salt.utils.is_darwin() - or not salt.utils.which('systemsetup' + or not salt.utils.which('systemsetup') or salt.utils.get_uid(salt.utils.get_user() != 0)), 'Test requirements not met') class MacPowerModuleTest(integration.ModuleCase): ''' @@ -31,6 +31,13 @@ class MacPowerModuleTest(integration.ModuleCase): RESTART_POWER = False RESTART_FREEZE = False SLEEP_ON_BUTTON = False + DESKTOP = False + + def __init__(self, arg): + super(self.__class__, self).__init__(arg) + self.desktop = False + if self.run_function('grains.item', ['model_name']) in ['Mac mini', 'iMac']: + DESKTOP = True def setUp(self): ''' @@ -39,11 +46,12 @@ class MacPowerModuleTest(integration.ModuleCase): self.COMPUTER_SLEEP = self.run_function('power.get_computer_sleep') self.DISPLAY_SLEEP = self.run_function('power.get_display_sleep') self.HARD_DISK_SLEEP = self.run_function('power.get_harddisk_sleep') - self.WAKE_ON_MODEM = self.run_function('power.get_wake_on_modem') - self.WAKE_ON_NET = self.run_function('power.get_wake_on_network') - self.RESTART_POWER = self.run_function('power.get_restart_power_failure') - self.RESTART_FREEZE = self.run_function('power.get_restart_freeze') - self.SLEEP_ON_BUTTON = self.run_function('power.get_sleep_on_power_button') + if self.DESKTOP: + self.WAKE_ON_MODEM = self.run_function('power.get_wake_on_modem') + self.WAKE_ON_NET = self.run_function('power.get_wake_on_network') + self.RESTART_POWER = self.run_function('power.get_restart_power_failure') + self.RESTART_FREEZE = self.run_function('power.get_restart_freeze') + self.SLEEP_ON_BUTTON = self.run_function('power.get_sleep_on_power_button') def tearDown(self): ''' @@ -52,13 +60,14 @@ class MacPowerModuleTest(integration.ModuleCase): self.run_function('power.set_computer_sleep', [self.COMPUTER_SLEEP]) self.run_function('power.set_display_sleep', [self.DISPLAY_SLEEP]) self.run_function('power.set_harddisk_sleep', [self.HARD_DISK_SLEEP]) - self.run_function('power.set_wake_on_modem', [self.WAKE_ON_MODEM]) - self.run_function('power.set_wake_on_network', [self.WAKE_ON_NET]) - self.run_function('power.set_restart_power_failure', - [self.RESTART_POWER]) - self.run_function('power.set_restart_freeze', [self.RESTART_FREEZE]) - self.run_function('power.set_sleep_on_power_button', - [self.SLEEP_ON_BUTTON]) + if self.DESKTOP: + self.run_function('power.set_wake_on_modem', [self.WAKE_ON_MODEM]) + self.run_function('power.set_wake_on_network', [self.WAKE_ON_NET]) + self.run_function('power.set_restart_power_failure', + [self.RESTART_POWER]) + self.run_function('power.set_restart_freeze', [self.RESTART_FREEZE]) + self.run_function('power.set_sleep_on_power_button', + [self.SLEEP_ON_BUTTON]) @destructiveTest def test_computer_sleep(self): @@ -148,72 +157,93 @@ class MacPowerModuleTest(integration.ModuleCase): ''' Test power.get_wake_on_modem Test power.set_wake_on_modem - - Commands don't seem to be supported on el capitan. Perhaps it works on - OS X Server or older versions ''' - self.assertTrue(self.run_function('power.set_wake_on_modem', ['on'])) - self.assertTrue(self.run_function('power.get_wake_on_modem')) - self.assertTrue(self.run_function('power.set_wake_on_modem', ['off'])) - self.assertFalse(self.run_function('power.get_wake_on_modem')) + if self.DESKTOP: + self.assertTrue( + self.run_function('power.set_wake_on_modem', ['on'])) + self.assertTrue(self.run_function('power.get_wake_on_modem')) + self.assertTrue( + self.run_function('power.set_wake_on_modem', ['off'])) + self.assertFalse(self.run_function('power.get_wake_on_modem')) + else: + # Check for failure if not a desktop + ret = self.run_function('power.set_wake_on_modem', ['on']) + self.assertIn('Error', ret) def test_wake_on_network(self): ''' Test power.get_wake_on_network Test power.set_wake_on_network - - Commands don't seem to be supported on el capitan. Perhaps it works on - OS X Server or older versions ''' - self.assertTrue(self.run_function('power.set_wake_on_network', ['on'])) - self.assertTrue(self.run_function('power.get_wake_on_network')) - self.assertTrue(self.run_function('power.set_wake_on_network', ['off'])) - self.assertFalse(self.run_function('power.get_wake_on_network')) + if self.DESKTOP: + self.assertTrue( + self.run_function('power.set_wake_on_network', ['on'])) + self.assertTrue(self.run_function('power.get_wake_on_network')) + self.assertTrue( + self.run_function('power.set_wake_on_network', ['off'])) + self.assertFalse(self.run_function('power.get_wake_on_network')) + else: + # Check for failure if not a desktop + ret = self.run_function('power.set_wake_on_network', ['on']) + self.assertIn('Error', ret) def test_restart_power_failure(self): ''' Test power.get_restart_power_failure Test power.set_restart_power_failure - - Commands don't seem to be supported on el capitan. Perhaps it works on - OS X Server or older versions ''' - self.assertTrue( - self.run_function('power.set_restart_power_failure', ['on'])) - self.assertTrue(self.run_function('power.get_restart_power_failure')) - self.assertTrue( - self.run_function('power.set_restart_power_failure', ['off'])) - self.assertFalse(self.run_function('power.get_restart_power_failure')) + if self.DESKTOP: + self.assertTrue( + self.run_function('power.set_restart_power_failure', ['on'])) + self.assertTrue( + self.run_function('power.get_restart_power_failure')) + self.assertTrue( + self.run_function('power.set_restart_power_failure', ['off'])) + self.assertFalse( + self.run_function('power.get_restart_power_failure')) + else: + # Check for failure if not a desktop + ret = self.run_function('power.set_restart_power_failure', ['on']) + self.assertIn('Error', ret) def test_restart_freeze(self): ''' Test power.get_restart_freeze Test power.set_restart_freeze - - Though the set command completes successfully, the setting isn't - actually changed ''' # Normal Functionality self.assertTrue(self.run_function('power.set_restart_freeze', ['on'])) self.assertTrue(self.run_function('power.get_restart_freeze')) - self.assertTrue(self.run_function('power.set_restart_freeze', ['off'])) - self.assertFalse(self.run_function('power.get_restart_freeze')) + if self.DESKTOP: + self.assertTrue( + self.run_function('power.set_restart_freeze', ['off'])) + self.assertFalse(self.run_function('power.get_restart_freeze')) + else: + # Non desktop will fail to set + self.assertFalse( + self.run_function('power.set_restart_freeze', ['off'])) + # Non desktop is always True + self.assertTrue(self.run_function('power.get_restart_freeze')) def test_sleep_on_power_button(self): ''' Test power.get_sleep_on_power_button Test power.set_sleep_on_power_button - - Commands don't seem to be supported on el capitan. Perhaps it works on - OS X Server or older versions ''' # Normal Functionality - self.assertTrue( - self.run_function('power.set_sleep_on_power_button', ['on'])) - self.assertTrue(self.run_function('power.get_sleep_on_power_button')) - self.assertTrue( - self.run_function('power.set_sleep_on_power_button', ['off'])) - self.assertFalse(self.run_function('power.get_sleep_on_power_button')) + if self.DESKTOP: + self.assertTrue( + self.run_function('power.set_sleep_on_power_button', ['on'])) + self.assertTrue( + self.run_function('power.get_sleep_on_power_button')) + self.assertTrue( + self.run_function('power.set_sleep_on_power_button', ['off'])) + self.assertFalse( + self.run_function('power.get_sleep_on_power_button')) + else: + # Check for failure if not a desktop + ret = self.run_function('power.set_sleep_on_power_button', ['on']) + self.assertIn('Error', ret) if __name__ == '__main__': From d0d36bb874354e2aa327002e65ad6ec3ff966b39 Mon Sep 17 00:00:00 2001 From: twangboy Date: Thu, 1 Sep 2016 13:13:07 -0600 Subject: [PATCH 041/100] Fix syntax error (lint) --- tests/integration/modules/mac_power.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/modules/mac_power.py b/tests/integration/modules/mac_power.py index 3d373d90d8..a070ff228a 100644 --- a/tests/integration/modules/mac_power.py +++ b/tests/integration/modules/mac_power.py @@ -18,7 +18,7 @@ from salt.exceptions import CommandExecutionError @skipIf(not salt.utils.is_darwin() or not salt.utils.which('systemsetup') - or salt.utils.get_uid(salt.utils.get_user() != 0)), 'Test requirements not met') + or salt.utils.get_uid(salt.utils.get_user() != 0), 'Test requirements not met') class MacPowerModuleTest(integration.ModuleCase): ''' Validate the mac_power module From 5e19d4664afc140a9452e8e04ccb7e0a4bf583b2 Mon Sep 17 00:00:00 2001 From: twangboy Date: Thu, 1 Sep 2016 16:34:45 -0600 Subject: [PATCH 042/100] Check the availablity of individual tests --- tests/integration/modules/mac_power.py | 88 ++++++++++++++++---------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/tests/integration/modules/mac_power.py b/tests/integration/modules/mac_power.py index a070ff228a..eed4f3e169 100644 --- a/tests/integration/modules/mac_power.py +++ b/tests/integration/modules/mac_power.py @@ -18,7 +18,7 @@ from salt.exceptions import CommandExecutionError @skipIf(not salt.utils.is_darwin() or not salt.utils.which('systemsetup') - or salt.utils.get_uid(salt.utils.get_user() != 0), 'Test requirements not met') + or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') class MacPowerModuleTest(integration.ModuleCase): ''' Validate the mac_power module @@ -29,28 +29,50 @@ class MacPowerModuleTest(integration.ModuleCase): WAKE_ON_MODEM = False WAKE_ON_NET = False RESTART_POWER = False - RESTART_FREEZE = False SLEEP_ON_BUTTON = False - DESKTOP = False - - def __init__(self, arg): - super(self.__class__, self).__init__(arg) - self.desktop = False - if self.run_function('grains.item', ['model_name']) in ['Mac mini', 'iMac']: - DESKTOP = True + WAKE_ON_MOD_AVAIL = None + WAKE_ON_NET_AVAIL = None + RESTART_POW_AVAIL = None + SLEEP_ON_POW_AVAIL = None def setUp(self): ''' Get current settings ''' + # Determine if these functions are available + # This is necessary because they may not be available if this test is run + # on a laptop vs a desktop or in a vm + if self.RESTART_POW_AVAIL is None: + self.RESTART_POW_AVAIL = True + ret = self.run_function('power.get_restart_power_failure') + if 'Not supported' in ret: + self.RESTART_POW_AVAIL = False + if self.SLEEP_ON_POW_AVAIL is None: + self.SLEEP_ON_POW_AVAIL = True + ret = self.run_function('power.get_sleep_on_power)') + if 'not supported' in ret: + self.SLEEP_ON_POW_AVAIL = False + if self.WAKE_ON_NET_AVAIL is None: + self.WAKE_ON_NET_AVAIL = True + ret = self.run_function('power.get_wake_on_network') + if 'Not supported' in ret: + self.WAKE_ON_NET_AVAIL = False + if self.WAKE_ON_MOD_AVAIL is None: + self.WAKE_ON_MOD_AVAIL = True + ret = self.run_function('power.get_wake_on_modem') + if 'Not supported' in ret: + self.WAKE_ON_MOD_AVAIL = False + # Get current settings self.COMPUTER_SLEEP = self.run_function('power.get_computer_sleep') self.DISPLAY_SLEEP = self.run_function('power.get_display_sleep') self.HARD_DISK_SLEEP = self.run_function('power.get_harddisk_sleep') - if self.DESKTOP: + if self.WAKE_ON_MOD_AVAIL: self.WAKE_ON_MODEM = self.run_function('power.get_wake_on_modem') + if self.WAKE_ON_NET_AVAIL: self.WAKE_ON_NET = self.run_function('power.get_wake_on_network') + if self.RESTART_POW_AVAIL: self.RESTART_POWER = self.run_function('power.get_restart_power_failure') - self.RESTART_FREEZE = self.run_function('power.get_restart_freeze') + if self.SLEEP_ON_POW_AVAIL: self.SLEEP_ON_BUTTON = self.run_function('power.get_sleep_on_power_button') def tearDown(self): @@ -60,12 +82,14 @@ class MacPowerModuleTest(integration.ModuleCase): self.run_function('power.set_computer_sleep', [self.COMPUTER_SLEEP]) self.run_function('power.set_display_sleep', [self.DISPLAY_SLEEP]) self.run_function('power.set_harddisk_sleep', [self.HARD_DISK_SLEEP]) - if self.DESKTOP: + if self.WAKE_ON_MOD_AVAIL: self.run_function('power.set_wake_on_modem', [self.WAKE_ON_MODEM]) + if self.WAKE_ON_NET_AVAIL: self.run_function('power.set_wake_on_network', [self.WAKE_ON_NET]) + if self.RESTART_POW_AVAIL: self.run_function('power.set_restart_power_failure', [self.RESTART_POWER]) - self.run_function('power.set_restart_freeze', [self.RESTART_FREEZE]) + if self.SLEEP_ON_POW_AVAIL: self.run_function('power.set_sleep_on_power_button', [self.SLEEP_ON_BUTTON]) @@ -158,7 +182,8 @@ class MacPowerModuleTest(integration.ModuleCase): Test power.get_wake_on_modem Test power.set_wake_on_modem ''' - if self.DESKTOP: + # If available on this system, test it + if self.WAKE_ON_MOD_AVAIL: self.assertTrue( self.run_function('power.set_wake_on_modem', ['on'])) self.assertTrue(self.run_function('power.get_wake_on_modem')) @@ -167,7 +192,7 @@ class MacPowerModuleTest(integration.ModuleCase): self.assertFalse(self.run_function('power.get_wake_on_modem')) else: # Check for failure if not a desktop - ret = self.run_function('power.set_wake_on_modem', ['on']) + ret = self.run_function('power.get_wake_on_modem') self.assertIn('Error', ret) def test_wake_on_network(self): @@ -175,7 +200,8 @@ class MacPowerModuleTest(integration.ModuleCase): Test power.get_wake_on_network Test power.set_wake_on_network ''' - if self.DESKTOP: + # If available on this system, test it + if self.WAKE_ON_NET_AVAIL: self.assertTrue( self.run_function('power.set_wake_on_network', ['on'])) self.assertTrue(self.run_function('power.get_wake_on_network')) @@ -184,7 +210,7 @@ class MacPowerModuleTest(integration.ModuleCase): self.assertFalse(self.run_function('power.get_wake_on_network')) else: # Check for failure if not a desktop - ret = self.run_function('power.set_wake_on_network', ['on']) + ret = self.run_function('power.get_wake_on_network') self.assertIn('Error', ret) def test_restart_power_failure(self): @@ -192,7 +218,8 @@ class MacPowerModuleTest(integration.ModuleCase): Test power.get_restart_power_failure Test power.set_restart_power_failure ''' - if self.DESKTOP: + # If available on this system, test it + if self.RESTART_POW_AVAIL: self.assertTrue( self.run_function('power.set_restart_power_failure', ['on'])) self.assertTrue( @@ -203,7 +230,7 @@ class MacPowerModuleTest(integration.ModuleCase): self.run_function('power.get_restart_power_failure')) else: # Check for failure if not a desktop - ret = self.run_function('power.set_restart_power_failure', ['on']) + ret = self.run_function('power.get_restart_power_failure') self.assertIn('Error', ret) def test_restart_freeze(self): @@ -214,24 +241,20 @@ class MacPowerModuleTest(integration.ModuleCase): # Normal Functionality self.assertTrue(self.run_function('power.set_restart_freeze', ['on'])) self.assertTrue(self.run_function('power.get_restart_freeze')) - if self.DESKTOP: - self.assertTrue( - self.run_function('power.set_restart_freeze', ['off'])) - self.assertFalse(self.run_function('power.get_restart_freeze')) - else: - # Non desktop will fail to set - self.assertFalse( - self.run_function('power.set_restart_freeze', ['off'])) - # Non desktop is always True - self.assertTrue(self.run_function('power.get_restart_freeze')) + # This will return False because mac fails to actually make the change + self.assertFalse( + self.run_function('power.set_restart_freeze', ['off'])) + # Even setting to off returns true, it actually is never set + # This is an apple bug + self.assertTrue(self.run_function('power.get_restart_freeze')) def test_sleep_on_power_button(self): ''' Test power.get_sleep_on_power_button Test power.set_sleep_on_power_button ''' - # Normal Functionality - if self.DESKTOP: + # If available on this system, test it + if self.SLEEP_ON_POW_AVAIL: self.assertTrue( self.run_function('power.set_sleep_on_power_button', ['on'])) self.assertTrue( @@ -241,8 +264,7 @@ class MacPowerModuleTest(integration.ModuleCase): self.assertFalse( self.run_function('power.get_sleep_on_power_button')) else: - # Check for failure if not a desktop - ret = self.run_function('power.set_sleep_on_power_button', ['on']) + ret = self.run_function('power.get_sleep_on_power_button') self.assertIn('Error', ret) From 701031ff408cd61b5b34fa027ea3ec658e7c8dd4 Mon Sep 17 00:00:00 2001 From: twangboy Date: Thu, 1 Sep 2016 17:28:32 -0600 Subject: [PATCH 043/100] Fix sleep_on_power test --- tests/integration/modules/mac_power.py | 58 +++++++++++++++----------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/tests/integration/modules/mac_power.py b/tests/integration/modules/mac_power.py index eed4f3e169..d0da1585b4 100644 --- a/tests/integration/modules/mac_power.py +++ b/tests/integration/modules/mac_power.py @@ -23,57 +23,67 @@ class MacPowerModuleTest(integration.ModuleCase): ''' Validate the mac_power module ''' - COMPUTER_SLEEP = 0 - DISPLAY_SLEEP = 0 - HARD_DISK_SLEEP = 0 - WAKE_ON_MODEM = False - WAKE_ON_NET = False - RESTART_POWER = False - SLEEP_ON_BUTTON = False + COMPUTER_SLEEP = None + DISPLAY_SLEEP = None + HARD_DISK_SLEEP = None + WAKE_ON_MODEM = None + WAKE_ON_NET = None + RESTART_POWER = None + SLEEP_ON_BUTTON = None WAKE_ON_MOD_AVAIL = None WAKE_ON_NET_AVAIL = None RESTART_POW_AVAIL = None SLEEP_ON_POW_AVAIL = None - def setUp(self): - ''' - Get current settings - ''' + def __init__(self, arg): + super(self.__class__, self).__init__(arg) # Determine if these functions are available # This is necessary because they may not be available if this test is run # on a laptop vs a desktop or in a vm if self.RESTART_POW_AVAIL is None: self.RESTART_POW_AVAIL = True - ret = self.run_function('power.get_restart_power_failure') - if 'Not supported' in ret: + ret = self.run_function('power.get_restart_power_failure') + if 'Error' in ret: self.RESTART_POW_AVAIL = False if self.SLEEP_ON_POW_AVAIL is None: self.SLEEP_ON_POW_AVAIL = True - ret = self.run_function('power.get_sleep_on_power)') - if 'not supported' in ret: + ret = self.run_function('power.get_sleep_on_power_button') + if 'Error' in ret: self.SLEEP_ON_POW_AVAIL = False if self.WAKE_ON_NET_AVAIL is None: self.WAKE_ON_NET_AVAIL = True ret = self.run_function('power.get_wake_on_network') - if 'Not supported' in ret: + if 'Error' in ret: self.WAKE_ON_NET_AVAIL = False if self.WAKE_ON_MOD_AVAIL is None: self.WAKE_ON_MOD_AVAIL = True ret = self.run_function('power.get_wake_on_modem') - if 'Not supported' in ret: + if 'Error' in ret: self.WAKE_ON_MOD_AVAIL = False + + def setUp(self): + ''' + Get current settings + ''' # Get current settings - self.COMPUTER_SLEEP = self.run_function('power.get_computer_sleep') - self.DISPLAY_SLEEP = self.run_function('power.get_display_sleep') - self.HARD_DISK_SLEEP = self.run_function('power.get_harddisk_sleep') + if self.COMPUTER_SLEEP is None: + self.COMPUTER_SLEEP = self.run_function('power.get_computer_sleep') + if self.DISPLAY_SLEEP is None: + self.DISPLAY_SLEEP = self.run_function('power.get_display_sleep') + if self.HARD_DISK_SLEEP is None: + self.HARD_DISK_SLEEP = self.run_function('power.get_harddisk_sleep') if self.WAKE_ON_MOD_AVAIL: - self.WAKE_ON_MODEM = self.run_function('power.get_wake_on_modem') + if self.WAKE_ON_MODEM is None: + self.WAKE_ON_MODEM = self.run_function('power.get_wake_on_modem') if self.WAKE_ON_NET_AVAIL: - self.WAKE_ON_NET = self.run_function('power.get_wake_on_network') + if self.WAKE_ON_NET is None: + self.WAKE_ON_NET = self.run_function('power.get_wake_on_network') if self.RESTART_POW_AVAIL: - self.RESTART_POWER = self.run_function('power.get_restart_power_failure') + if self.RESTART_POWER is None: + self.RESTART_POWER = self.run_function('power.get_restart_power_failure') if self.SLEEP_ON_POW_AVAIL: - self.SLEEP_ON_BUTTON = self.run_function('power.get_sleep_on_power_button') + if self.SLEEP_ON_BUTTON is None: + self.SLEEP_ON_BUTTON = self.run_function('power.get_sleep_on_power_button') def tearDown(self): ''' From 35dd0619cbde13328ac7b0eac3cc422a1c70dd18 Mon Sep 17 00:00:00 2001 From: twangboy Date: Thu, 1 Sep 2016 17:39:11 -0600 Subject: [PATCH 044/100] Check string type --- tests/integration/modules/mac_power.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/integration/modules/mac_power.py b/tests/integration/modules/mac_power.py index d0da1585b4..97817137fb 100644 --- a/tests/integration/modules/mac_power.py +++ b/tests/integration/modules/mac_power.py @@ -5,6 +5,7 @@ integration tests for mac_power # Import python libs from __future__ import absolute_import, print_function +from six import string_types # Import Salt Testing libs from salttesting import skipIf @@ -14,7 +15,6 @@ ensure_in_syspath('../../') # Import salt libs import integration import salt.utils -from salt.exceptions import CommandExecutionError @skipIf(not salt.utils.is_darwin() or not salt.utils.which('systemsetup') @@ -43,22 +43,22 @@ class MacPowerModuleTest(integration.ModuleCase): if self.RESTART_POW_AVAIL is None: self.RESTART_POW_AVAIL = True ret = self.run_function('power.get_restart_power_failure') - if 'Error' in ret: + if isinstance(ret, string_types) and 'Error' in ret: self.RESTART_POW_AVAIL = False if self.SLEEP_ON_POW_AVAIL is None: self.SLEEP_ON_POW_AVAIL = True ret = self.run_function('power.get_sleep_on_power_button') - if 'Error' in ret: + if isinstance(ret, string_types) and 'Error' in ret: self.SLEEP_ON_POW_AVAIL = False if self.WAKE_ON_NET_AVAIL is None: self.WAKE_ON_NET_AVAIL = True ret = self.run_function('power.get_wake_on_network') - if 'Error' in ret: + if isinstance(ret, string_types) and 'Error' in ret: self.WAKE_ON_NET_AVAIL = False if self.WAKE_ON_MOD_AVAIL is None: self.WAKE_ON_MOD_AVAIL = True ret = self.run_function('power.get_wake_on_modem') - if 'Error' in ret: + if isinstance(ret, string_types) and 'Error' in ret: self.WAKE_ON_MOD_AVAIL = False def setUp(self): From efb425781fd9934ad56134ca976d09431b7a8464 Mon Sep 17 00:00:00 2001 From: twangboy Date: Fri, 2 Sep 2016 09:02:28 -0600 Subject: [PATCH 045/100] Fix lint errors --- tests/integration/modules/mac_power.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/modules/mac_power.py b/tests/integration/modules/mac_power.py index 97817137fb..dafa16c1c6 100644 --- a/tests/integration/modules/mac_power.py +++ b/tests/integration/modules/mac_power.py @@ -16,6 +16,7 @@ ensure_in_syspath('../../') import integration import salt.utils + @skipIf(not salt.utils.is_darwin() or not salt.utils.which('systemsetup') or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') @@ -253,7 +254,7 @@ class MacPowerModuleTest(integration.ModuleCase): self.assertTrue(self.run_function('power.get_restart_freeze')) # This will return False because mac fails to actually make the change self.assertFalse( - self.run_function('power.set_restart_freeze', ['off'])) + self.run_function('power.set_restart_freeze', ['off'])) # Even setting to off returns true, it actually is never set # This is an apple bug self.assertTrue(self.run_function('power.get_restart_freeze')) From c2b42f69c1cd2db5cb918b85c4195a2dc798077f Mon Sep 17 00:00:00 2001 From: twangboy Date: Fri, 2 Sep 2016 10:30:23 -0600 Subject: [PATCH 046/100] Move test available into their own classes --- tests/integration/modules/mac_power.py | 309 +++++++++++++++---------- 1 file changed, 183 insertions(+), 126 deletions(-) diff --git a/tests/integration/modules/mac_power.py b/tests/integration/modules/mac_power.py index dafa16c1c6..a108a1c140 100644 --- a/tests/integration/modules/mac_power.py +++ b/tests/integration/modules/mac_power.py @@ -24,67 +24,14 @@ class MacPowerModuleTest(integration.ModuleCase): ''' Validate the mac_power module ''' - COMPUTER_SLEEP = None - DISPLAY_SLEEP = None - HARD_DISK_SLEEP = None - WAKE_ON_MODEM = None - WAKE_ON_NET = None - RESTART_POWER = None - SLEEP_ON_BUTTON = None - WAKE_ON_MOD_AVAIL = None - WAKE_ON_NET_AVAIL = None - RESTART_POW_AVAIL = None - SLEEP_ON_POW_AVAIL = None - - def __init__(self, arg): - super(self.__class__, self).__init__(arg) - # Determine if these functions are available - # This is necessary because they may not be available if this test is run - # on a laptop vs a desktop or in a vm - if self.RESTART_POW_AVAIL is None: - self.RESTART_POW_AVAIL = True - ret = self.run_function('power.get_restart_power_failure') - if isinstance(ret, string_types) and 'Error' in ret: - self.RESTART_POW_AVAIL = False - if self.SLEEP_ON_POW_AVAIL is None: - self.SLEEP_ON_POW_AVAIL = True - ret = self.run_function('power.get_sleep_on_power_button') - if isinstance(ret, string_types) and 'Error' in ret: - self.SLEEP_ON_POW_AVAIL = False - if self.WAKE_ON_NET_AVAIL is None: - self.WAKE_ON_NET_AVAIL = True - ret = self.run_function('power.get_wake_on_network') - if isinstance(ret, string_types) and 'Error' in ret: - self.WAKE_ON_NET_AVAIL = False - if self.WAKE_ON_MOD_AVAIL is None: - self.WAKE_ON_MOD_AVAIL = True - ret = self.run_function('power.get_wake_on_modem') - if isinstance(ret, string_types) and 'Error' in ret: - self.WAKE_ON_MOD_AVAIL = False - def setUp(self): ''' Get current settings ''' # Get current settings - if self.COMPUTER_SLEEP is None: - self.COMPUTER_SLEEP = self.run_function('power.get_computer_sleep') - if self.DISPLAY_SLEEP is None: - self.DISPLAY_SLEEP = self.run_function('power.get_display_sleep') - if self.HARD_DISK_SLEEP is None: - self.HARD_DISK_SLEEP = self.run_function('power.get_harddisk_sleep') - if self.WAKE_ON_MOD_AVAIL: - if self.WAKE_ON_MODEM is None: - self.WAKE_ON_MODEM = self.run_function('power.get_wake_on_modem') - if self.WAKE_ON_NET_AVAIL: - if self.WAKE_ON_NET is None: - self.WAKE_ON_NET = self.run_function('power.get_wake_on_network') - if self.RESTART_POW_AVAIL: - if self.RESTART_POWER is None: - self.RESTART_POWER = self.run_function('power.get_restart_power_failure') - if self.SLEEP_ON_POW_AVAIL: - if self.SLEEP_ON_BUTTON is None: - self.SLEEP_ON_BUTTON = self.run_function('power.get_sleep_on_power_button') + self.COMPUTER_SLEEP = self.run_function('power.get_computer_sleep') + self.DISPLAY_SLEEP = self.run_function('power.get_display_sleep') + self.HARD_DISK_SLEEP = self.run_function('power.get_harddisk_sleep') def tearDown(self): ''' @@ -93,16 +40,6 @@ class MacPowerModuleTest(integration.ModuleCase): self.run_function('power.set_computer_sleep', [self.COMPUTER_SLEEP]) self.run_function('power.set_display_sleep', [self.DISPLAY_SLEEP]) self.run_function('power.set_harddisk_sleep', [self.HARD_DISK_SLEEP]) - if self.WAKE_ON_MOD_AVAIL: - self.run_function('power.set_wake_on_modem', [self.WAKE_ON_MODEM]) - if self.WAKE_ON_NET_AVAIL: - self.run_function('power.set_wake_on_network', [self.WAKE_ON_NET]) - if self.RESTART_POW_AVAIL: - self.run_function('power.set_restart_power_failure', - [self.RESTART_POWER]) - if self.SLEEP_ON_POW_AVAIL: - self.run_function('power.set_sleep_on_power_button', - [self.SLEEP_ON_BUTTON]) @destructiveTest def test_computer_sleep(self): @@ -188,62 +125,6 @@ class MacPowerModuleTest(integration.ModuleCase): 'Invalid Boolean Value for Minutes', self.run_function('power.set_harddisk_sleep', [True])) - def test_wake_on_modem(self): - ''' - Test power.get_wake_on_modem - Test power.set_wake_on_modem - ''' - # If available on this system, test it - if self.WAKE_ON_MOD_AVAIL: - self.assertTrue( - self.run_function('power.set_wake_on_modem', ['on'])) - self.assertTrue(self.run_function('power.get_wake_on_modem')) - self.assertTrue( - self.run_function('power.set_wake_on_modem', ['off'])) - self.assertFalse(self.run_function('power.get_wake_on_modem')) - else: - # Check for failure if not a desktop - ret = self.run_function('power.get_wake_on_modem') - self.assertIn('Error', ret) - - def test_wake_on_network(self): - ''' - Test power.get_wake_on_network - Test power.set_wake_on_network - ''' - # If available on this system, test it - if self.WAKE_ON_NET_AVAIL: - self.assertTrue( - self.run_function('power.set_wake_on_network', ['on'])) - self.assertTrue(self.run_function('power.get_wake_on_network')) - self.assertTrue( - self.run_function('power.set_wake_on_network', ['off'])) - self.assertFalse(self.run_function('power.get_wake_on_network')) - else: - # Check for failure if not a desktop - ret = self.run_function('power.get_wake_on_network') - self.assertIn('Error', ret) - - def test_restart_power_failure(self): - ''' - Test power.get_restart_power_failure - Test power.set_restart_power_failure - ''' - # If available on this system, test it - if self.RESTART_POW_AVAIL: - self.assertTrue( - self.run_function('power.set_restart_power_failure', ['on'])) - self.assertTrue( - self.run_function('power.get_restart_power_failure')) - self.assertTrue( - self.run_function('power.set_restart_power_failure', ['off'])) - self.assertFalse( - self.run_function('power.get_restart_power_failure')) - else: - # Check for failure if not a desktop - ret = self.run_function('power.get_restart_power_failure') - self.assertIn('Error', ret) - def test_restart_freeze(self): ''' Test power.get_restart_freeze @@ -259,13 +140,47 @@ class MacPowerModuleTest(integration.ModuleCase): # This is an apple bug self.assertTrue(self.run_function('power.get_restart_freeze')) + +@skipIf(not salt.utils.is_darwin() + or not salt.utils.which('systemsetup') + or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') +class MacPowerModuleTestSleepOnPowerButton(integration.ModuleCase): + ''' + Test power.get_sleep_on_power_button + Test power.set_sleep_on_power_button + ''' + SLEEP_ON_BUTTON = None + + def setup(self): + ''' + Check if function is available + Get existing value + ''' + # Is the function available + ret = self.run_function('power.get_sleep_on_power_button') + if isinstance(ret, bool): + self.SLEEP_ON_BUTTON = self.run_function( + 'power.get_sleep_on_power_button') + + def teardown(self): + ''' + Reset to original value + ''' + if self.SLEEP_ON_BUTTON is not None: + self.run_function( + 'power.set_sleep_on_power_button', [self.SLEEP_ON_BUTTON]) + def test_sleep_on_power_button(self): ''' Test power.get_sleep_on_power_button Test power.set_sleep_on_power_button ''' # If available on this system, test it - if self.SLEEP_ON_POW_AVAIL: + if self.SLEEP_ON_BUTTON is None: + # Check for not available + ret = self.run_function('power.get_sleep_on_power_button') + self.assertIn('Error', ret) + else: self.assertTrue( self.run_function('power.set_sleep_on_power_button', ['on'])) self.assertTrue( @@ -274,11 +189,153 @@ class MacPowerModuleTest(integration.ModuleCase): self.run_function('power.set_sleep_on_power_button', ['off'])) self.assertFalse( self.run_function('power.get_sleep_on_power_button')) - else: - ret = self.run_function('power.get_sleep_on_power_button') + + +@skipIf(not salt.utils.is_darwin() + or not salt.utils.which('systemsetup') + or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') +class MacPowerModuleTestRestartPowerFailure(integration.ModuleCase): + ''' + Test power.get_restart_power_failure + Test power.set_restart_power_failure + ''' + RESTART_POWER = None + + def setup(self): + ''' + Check if function is available + Get existing value + ''' + # Is the function available + ret = self.run_function('power.get_restart_power_failure') + if isinstance(ret, bool): + self.RESTART_POWER = ret + + def teardown(self): + ''' + Reset to original value + ''' + if self.RESTART_POWER is not None: + self.run_function( + 'power.set_sleep_on_power_button', [self.SLEEP_ON_BUTTON]) + + def test_restart_power_failure(self): + ''' + Test power.get_restart_power_failure + Test power.set_restart_power_failure + ''' + # If available on this system, test it + if self.RESTART_POWER is None: + # Check for not available + ret = self.run_function('power.get_restart_power_failure') self.assertIn('Error', ret) + else: + self.assertTrue( + self.run_function('power.set_restart_power_failure', ['on'])) + self.assertTrue( + self.run_function('power.get_restart_power_failure')) + self.assertTrue( + self.run_function('power.set_restart_power_failure', ['off'])) + self.assertFalse( + self.run_function('power.get_restart_power_failure')) + + +@skipIf(not salt.utils.is_darwin() + or not salt.utils.which('systemsetup') + or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') +class MacPowerModuleTestWakeOnNet(integration.ModuleCase): + ''' + Test power.get_wake_on_network + Test power.set_wake_on_network + ''' + WAKE_ON_NET = None + + def setup(self): + ''' + Check if function is available + Get existing value + ''' + # Is the function available + ret = self.run_function('power.get_wake_on_network') + if isinstance(ret, bool): + self.WAKE_ON_NET = ret + + def teardown(self): + ''' + Reset to original value + ''' + if self.WAKE_ON_NET is not None: + self.run_function('power.set_wake_on_network', [self.WAKE_ON_NET]) + + def test_wake_on_network(self): + ''' + Test power.get_wake_on_network + Test power.set_wake_on_network + ''' + # If available on this system, test it + if self.WAKE_ON_NET is None: + # Check for not available + ret = self.run_function('power.get_wake_on_network') + self.assertIn('Error', ret) + else: + self.assertTrue( + self.run_function('power.set_wake_on_network', ['on'])) + self.assertTrue(self.run_function('power.get_wake_on_network')) + self.assertTrue( + self.run_function('power.set_wake_on_network', ['off'])) + self.assertFalse(self.run_function('power.get_wake_on_network')) + + +@skipIf(not salt.utils.is_darwin() + or not salt.utils.which('systemsetup') + or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') +class MacPowerModuleTestWakeOnModem(integration.ModuleCase): + ''' + Test power.get_wake_on_modem + Test power.set_wake_on_modem + ''' + WAKE_ON_MODEM = None + + def setup(self): + ''' + Check if function is available + Get existing value + ''' + # Is the function available + ret = self.run_function('power.get_wake_on_modem') + if isinstance(ret, bool): + self.WAKE_ON_MODEM = ret + + def teardown(self): + ''' + Reset to original value + ''' + if self.WAKE_ON_MODEM is not None: + self.run_function('power.set_wake_on_modem', [self.WAKE_ON_MODEM]) + + def test_wake_on_modem(self): + ''' + Test power.get_wake_on_modem + Test power.set_wake_on_modem + ''' + # If available on this system, test it + if self.WAKE_ON_MODEM is None: + # Check for not available + ret = self.run_function('power.get_wake_on_modem') + self.assertIn('Error', ret) + else: + self.assertTrue( + self.run_function('power.set_wake_on_modem', ['on'])) + self.assertTrue(self.run_function('power.get_wake_on_modem')) + self.assertTrue( + self.run_function('power.set_wake_on_modem', ['off'])) + self.assertFalse(self.run_function('power.get_wake_on_modem')) if __name__ == '__main__': from integration import run_tests - run_tests(MacPowerModuleTest) + run_tests(MacPowerModuleTest, + MacPowerModuleTestSleepOnPowerButton, + MacPowerModuleTestRestartPowerFailure, + MacPowerModuleTestWakeOnNet, + MacPowerModuleTestWakeOnModem) From af3f70d877f4326f88ad8b5a2c328d5efddb4d24 Mon Sep 17 00:00:00 2001 From: twangboy Date: Fri, 2 Sep 2016 11:15:38 -0600 Subject: [PATCH 047/100] Seperate sleep on power button test --- tests/integration/modules/mac_power.py | 51 ------------- .../modules/mac_power_sleep_on_power.py | 72 +++++++++++++++++++ 2 files changed, 72 insertions(+), 51 deletions(-) create mode 100644 tests/integration/modules/mac_power_sleep_on_power.py diff --git a/tests/integration/modules/mac_power.py b/tests/integration/modules/mac_power.py index a108a1c140..1ff0309a71 100644 --- a/tests/integration/modules/mac_power.py +++ b/tests/integration/modules/mac_power.py @@ -141,56 +141,6 @@ class MacPowerModuleTest(integration.ModuleCase): self.assertTrue(self.run_function('power.get_restart_freeze')) -@skipIf(not salt.utils.is_darwin() - or not salt.utils.which('systemsetup') - or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') -class MacPowerModuleTestSleepOnPowerButton(integration.ModuleCase): - ''' - Test power.get_sleep_on_power_button - Test power.set_sleep_on_power_button - ''' - SLEEP_ON_BUTTON = None - - def setup(self): - ''' - Check if function is available - Get existing value - ''' - # Is the function available - ret = self.run_function('power.get_sleep_on_power_button') - if isinstance(ret, bool): - self.SLEEP_ON_BUTTON = self.run_function( - 'power.get_sleep_on_power_button') - - def teardown(self): - ''' - Reset to original value - ''' - if self.SLEEP_ON_BUTTON is not None: - self.run_function( - 'power.set_sleep_on_power_button', [self.SLEEP_ON_BUTTON]) - - def test_sleep_on_power_button(self): - ''' - Test power.get_sleep_on_power_button - Test power.set_sleep_on_power_button - ''' - # If available on this system, test it - if self.SLEEP_ON_BUTTON is None: - # Check for not available - ret = self.run_function('power.get_sleep_on_power_button') - self.assertIn('Error', ret) - else: - self.assertTrue( - self.run_function('power.set_sleep_on_power_button', ['on'])) - self.assertTrue( - self.run_function('power.get_sleep_on_power_button')) - self.assertTrue( - self.run_function('power.set_sleep_on_power_button', ['off'])) - self.assertFalse( - self.run_function('power.get_sleep_on_power_button')) - - @skipIf(not salt.utils.is_darwin() or not salt.utils.which('systemsetup') or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') @@ -335,7 +285,6 @@ class MacPowerModuleTestWakeOnModem(integration.ModuleCase): if __name__ == '__main__': from integration import run_tests run_tests(MacPowerModuleTest, - MacPowerModuleTestSleepOnPowerButton, MacPowerModuleTestRestartPowerFailure, MacPowerModuleTestWakeOnNet, MacPowerModuleTestWakeOnModem) diff --git a/tests/integration/modules/mac_power_sleep_on_power.py b/tests/integration/modules/mac_power_sleep_on_power.py new file mode 100644 index 0000000000..7644977042 --- /dev/null +++ b/tests/integration/modules/mac_power_sleep_on_power.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +''' +integration tests for mac_power +''' + +# Import python libs +from __future__ import absolute_import, print_function +from six import string_types + +# Import Salt Testing libs +from salttesting import skipIf +from salttesting.helpers import ensure_in_syspath, destructiveTest +ensure_in_syspath('../../') + +# Import salt libs +import integration +import salt.utils + + +@skipIf(not salt.utils.is_darwin() + or not salt.utils.which('systemsetup') + or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') +class MacPowerModuleTestSleepOnPowerButton(integration.ModuleCase): + ''' + Test power.get_sleep_on_power_button + Test power.set_sleep_on_power_button + ''' + SLEEP_ON_BUTTON = None + + def setup(self): + ''' + Check if function is available + Get existing value + ''' + # Is the function available + ret = self.run_function('power.get_sleep_on_power_button') + if isinstance(ret, bool): + self.SLEEP_ON_BUTTON = self.run_function( + 'power.get_sleep_on_power_button') + + def teardown(self): + ''' + Reset to original value + ''' + if self.SLEEP_ON_BUTTON is not None: + self.run_function( + 'power.set_sleep_on_power_button', [self.SLEEP_ON_BUTTON]) + + def test_sleep_on_power_button(self): + ''' + Test power.get_sleep_on_power_button + Test power.set_sleep_on_power_button + ''' + # If available on this system, test it + if self.SLEEP_ON_BUTTON is None: + # Check for not available + ret = self.run_function('power.get_sleep_on_power_button') + self.assertIn('Error', ret) + else: + self.assertTrue( + self.run_function('power.set_sleep_on_power_button', ['on'])) + self.assertTrue( + self.run_function('power.get_sleep_on_power_button')) + self.assertTrue( + self.run_function('power.set_sleep_on_power_button', ['off'])) + self.assertFalse( + self.run_function('power.get_sleep_on_power_button')) + + +if __name__ == '__main__': + from integration import run_tests + run_tests(MacPowerModuleTestSleepOnPowerButton) From ca414e01de69951c0508d997cdb6a37ea6ee0cf8 Mon Sep 17 00:00:00 2001 From: twangboy Date: Fri, 2 Sep 2016 11:24:55 -0600 Subject: [PATCH 048/100] Fix capitalization error in setUp and tearDown --- tests/integration/modules/mac_power.py | 63 ++++++++++++++-- .../modules/mac_power_sleep_on_power.py | 72 ------------------- 2 files changed, 57 insertions(+), 78 deletions(-) delete mode 100644 tests/integration/modules/mac_power_sleep_on_power.py diff --git a/tests/integration/modules/mac_power.py b/tests/integration/modules/mac_power.py index 1ff0309a71..96e26c9993 100644 --- a/tests/integration/modules/mac_power.py +++ b/tests/integration/modules/mac_power.py @@ -141,6 +141,56 @@ class MacPowerModuleTest(integration.ModuleCase): self.assertTrue(self.run_function('power.get_restart_freeze')) +@skipIf(not salt.utils.is_darwin() + or not salt.utils.which('systemsetup') + or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') +class MacPowerModuleTestSleepOnPowerButton(integration.ModuleCase): + ''' + Test power.get_sleep_on_power_button + Test power.set_sleep_on_power_button + ''' + SLEEP_ON_BUTTON = None + + def setUp(self): + ''' + Check if function is available + Get existing value + ''' + # Is the function available + ret = self.run_function('power.get_sleep_on_power_button') + if isinstance(ret, bool): + self.SLEEP_ON_BUTTON = self.run_function( + 'power.get_sleep_on_power_button') + + def tearDown(self): + ''' + Reset to original value + ''' + if self.SLEEP_ON_BUTTON is not None: + self.run_function( + 'power.set_sleep_on_power_button', [self.SLEEP_ON_BUTTON]) + + def test_sleep_on_power_button(self): + ''' + Test power.get_sleep_on_power_button + Test power.set_sleep_on_power_button + ''' + # If available on this system, test it + if self.SLEEP_ON_BUTTON is None: + # Check for not available + ret = self.run_function('power.get_sleep_on_power_button') + self.assertIn('Error', ret) + else: + self.assertTrue( + self.run_function('power.set_sleep_on_power_button', ['on'])) + self.assertTrue( + self.run_function('power.get_sleep_on_power_button')) + self.assertTrue( + self.run_function('power.set_sleep_on_power_button', ['off'])) + self.assertFalse( + self.run_function('power.get_sleep_on_power_button')) + + @skipIf(not salt.utils.is_darwin() or not salt.utils.which('systemsetup') or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') @@ -151,7 +201,7 @@ class MacPowerModuleTestRestartPowerFailure(integration.ModuleCase): ''' RESTART_POWER = None - def setup(self): + def setUp(self): ''' Check if function is available Get existing value @@ -161,7 +211,7 @@ class MacPowerModuleTestRestartPowerFailure(integration.ModuleCase): if isinstance(ret, bool): self.RESTART_POWER = ret - def teardown(self): + def tearDown(self): ''' Reset to original value ''' @@ -200,7 +250,7 @@ class MacPowerModuleTestWakeOnNet(integration.ModuleCase): ''' WAKE_ON_NET = None - def setup(self): + def setUp(self): ''' Check if function is available Get existing value @@ -210,7 +260,7 @@ class MacPowerModuleTestWakeOnNet(integration.ModuleCase): if isinstance(ret, bool): self.WAKE_ON_NET = ret - def teardown(self): + def tearDown(self): ''' Reset to original value ''' @@ -246,7 +296,7 @@ class MacPowerModuleTestWakeOnModem(integration.ModuleCase): ''' WAKE_ON_MODEM = None - def setup(self): + def setUp(self): ''' Check if function is available Get existing value @@ -256,7 +306,7 @@ class MacPowerModuleTestWakeOnModem(integration.ModuleCase): if isinstance(ret, bool): self.WAKE_ON_MODEM = ret - def teardown(self): + def tearDown(self): ''' Reset to original value ''' @@ -285,6 +335,7 @@ class MacPowerModuleTestWakeOnModem(integration.ModuleCase): if __name__ == '__main__': from integration import run_tests run_tests(MacPowerModuleTest, + MacPowerModuleTestSleepOnPowerButton, MacPowerModuleTestRestartPowerFailure, MacPowerModuleTestWakeOnNet, MacPowerModuleTestWakeOnModem) diff --git a/tests/integration/modules/mac_power_sleep_on_power.py b/tests/integration/modules/mac_power_sleep_on_power.py deleted file mode 100644 index 7644977042..0000000000 --- a/tests/integration/modules/mac_power_sleep_on_power.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- coding: utf-8 -*- -''' -integration tests for mac_power -''' - -# Import python libs -from __future__ import absolute_import, print_function -from six import string_types - -# Import Salt Testing libs -from salttesting import skipIf -from salttesting.helpers import ensure_in_syspath, destructiveTest -ensure_in_syspath('../../') - -# Import salt libs -import integration -import salt.utils - - -@skipIf(not salt.utils.is_darwin() - or not salt.utils.which('systemsetup') - or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') -class MacPowerModuleTestSleepOnPowerButton(integration.ModuleCase): - ''' - Test power.get_sleep_on_power_button - Test power.set_sleep_on_power_button - ''' - SLEEP_ON_BUTTON = None - - def setup(self): - ''' - Check if function is available - Get existing value - ''' - # Is the function available - ret = self.run_function('power.get_sleep_on_power_button') - if isinstance(ret, bool): - self.SLEEP_ON_BUTTON = self.run_function( - 'power.get_sleep_on_power_button') - - def teardown(self): - ''' - Reset to original value - ''' - if self.SLEEP_ON_BUTTON is not None: - self.run_function( - 'power.set_sleep_on_power_button', [self.SLEEP_ON_BUTTON]) - - def test_sleep_on_power_button(self): - ''' - Test power.get_sleep_on_power_button - Test power.set_sleep_on_power_button - ''' - # If available on this system, test it - if self.SLEEP_ON_BUTTON is None: - # Check for not available - ret = self.run_function('power.get_sleep_on_power_button') - self.assertIn('Error', ret) - else: - self.assertTrue( - self.run_function('power.set_sleep_on_power_button', ['on'])) - self.assertTrue( - self.run_function('power.get_sleep_on_power_button')) - self.assertTrue( - self.run_function('power.set_sleep_on_power_button', ['off'])) - self.assertFalse( - self.run_function('power.get_sleep_on_power_button')) - - -if __name__ == '__main__': - from integration import run_tests - run_tests(MacPowerModuleTestSleepOnPowerButton) From 9e0f45785a4003dc4eaf76894f61e4a3514d5aba Mon Sep 17 00:00:00 2001 From: twangboy Date: Fri, 2 Sep 2016 11:37:04 -0600 Subject: [PATCH 049/100] Fix some lint --- tests/integration/modules/mac_power.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/modules/mac_power.py b/tests/integration/modules/mac_power.py index 96e26c9993..026cccebc9 100644 --- a/tests/integration/modules/mac_power.py +++ b/tests/integration/modules/mac_power.py @@ -5,17 +5,17 @@ integration tests for mac_power # Import python libs from __future__ import absolute_import, print_function -from six import string_types # Import Salt Testing libs from salttesting import skipIf from salttesting.helpers import ensure_in_syspath, destructiveTest -ensure_in_syspath('../../') # Import salt libs import integration import salt.utils +ensure_in_syspath('../../') + @skipIf(not salt.utils.is_darwin() or not salt.utils.which('systemsetup') From 4afc82ac63dadc33fe2fc9ea602174a762a51f2f Mon Sep 17 00:00:00 2001 From: Ch3LL Date: Fri, 2 Sep 2016 16:52:39 -0600 Subject: [PATCH 050/100] add salt-run state.event test --- tests/integration/runners/state.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/integration/runners/state.py b/tests/integration/runners/state.py index e463e38e08..d00ebc7446 100644 --- a/tests/integration/runners/state.py +++ b/tests/integration/runners/state.py @@ -12,6 +12,8 @@ import signal import tempfile import textwrap import yaml +import threading +from salt.ext.six.moves import queue # Import Salt Testing Libs from salttesting import skipIf @@ -29,6 +31,14 @@ class StateRunnerTest(integration.ShellCase): ''' Test the state runner. ''' + def add_to_queue(self, q, cmd): + ''' + helper method to add salt-run + return data to a queue + ''' + ret = self.run_run(cmd) + q.put(ret) + q.task_done() def test_orchestrate_output(self): ''' @@ -58,6 +68,25 @@ class StateRunnerTest(integration.ShellCase): for item in good_out: self.assertIn(item, ret_output) + def test_state_event(self): + ''' + test to ensure state.event + runner returns correct data + ''' + q = queue.Queue(maxsize=0) + + cmd = 'state.event salt/job/*/new count=1' + expect = '"minions": ["minion"]' + server_thread = threading.Thread(target=self.add_to_queue, args=(q, cmd)) + server_thread.setDaemon(True) + server_thread.start() + + while q.empty(): + self.run_salt('minion test.ping --static') + out = q.get() + self.assertIn(expect, str(out)) + + server_thread.join() @skipIf(salt.utils.is_windows(), '*NIX-only test') class OrchEventTest(integration.ShellCase): From dde85e114459fc6b8dcab111f0d7fe6af573dfe8 Mon Sep 17 00:00:00 2001 From: plastikos Date: Tue, 6 Sep 2016 16:30:08 -0600 Subject: [PATCH 051/100] Various fixes for MacOS X (#36085) * Change "unknown" user to "some_unknown_user_xyz" since "unknown" *is* a valid user * Add timeout to proxy test_exit_status_no_proxyid() since a known failure case is to infinitely loop with an error. * Always force the source code tree under test to be the first entry in PYTHONPATH (excluding verbatim_env) --- tests/integration/shell/master.py | 2 +- tests/integration/shell/minion.py | 2 +- tests/integration/shell/proxy.py | 5 ++++- tests/integration/shell/syndic.py | 2 +- tests/integration/utils/testprogram.py | 5 +++-- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/integration/shell/master.py b/tests/integration/shell/master.py index 671b573768..799374917b 100644 --- a/tests/integration/shell/master.py +++ b/tests/integration/shell/master.py @@ -87,7 +87,7 @@ class MasterTest(integration.ShellCase, testprogram.TestProgramCase, integration master = testprogram.TestDaemonSaltMaster( name='unknown_user', - configs={'master': {'map': {'user': 'unknown'}}}, + configs={'master': {'map': {'user': 'some_unknown_user_xyz'}}}, parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist diff --git a/tests/integration/shell/minion.py b/tests/integration/shell/minion.py index 3011e05819..82fe0503c7 100644 --- a/tests/integration/shell/minion.py +++ b/tests/integration/shell/minion.py @@ -283,7 +283,7 @@ class MinionTest(integration.ShellCase, testprogram.TestProgramCase, integration minion = testprogram.TestDaemonSaltMinion( name='unknown_user', - configs={'minion': {'map': {'user': 'unknown'}}}, + configs={'minion': {'map': {'user': 'some_unknown_user_xyz'}}}, parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist diff --git a/tests/integration/shell/proxy.py b/tests/integration/shell/proxy.py index eb37ed027c..a7bc047315 100644 --- a/tests/integration/shell/proxy.py +++ b/tests/integration/shell/proxy.py @@ -46,6 +46,9 @@ class ProxyTest(testprogram.TestProgramCase): verbatim_args=True, # prevents --proxyid from being added automatically catch_stderr=True, with_retcode=True, + # The proxy minion had a bug where it would loop forever + # without daemonizing - protect that with a timeout. + timeout=60, ) self.assert_exit_status( status, 'EX_USAGE', @@ -62,7 +65,7 @@ class ProxyTest(testprogram.TestProgramCase): proxy = testprogram.TestDaemonSaltProxy( name='proxy-unknown_user', - config_base={'user': 'unknown'}, + config_base={'user': 'some_unknown_user_xyz'}, parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist diff --git a/tests/integration/shell/syndic.py b/tests/integration/shell/syndic.py index 8ef98f728f..ce4b4a9e30 100644 --- a/tests/integration/shell/syndic.py +++ b/tests/integration/shell/syndic.py @@ -94,7 +94,7 @@ class SyndicTest(integration.ShellCase, testprogram.TestProgramCase, integration syndic = testprogram.TestDaemonSaltSyndic( name='unknown_user', - config_base={'user': 'unknown'}, + config_base={'user': 'some_unknown_user_xyz'}, parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist diff --git a/tests/integration/utils/testprogram.py b/tests/integration/utils/testprogram.py index 661e9fc295..4fb8799a6d 100644 --- a/tests/integration/utils/testprogram.py +++ b/tests/integration/utils/testprogram.py @@ -384,8 +384,9 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): for path in sys.path: if path not in env_pypath: env_pypath.append(path) - if integration.CODE_DIR not in env_pypath: - env_pypath.append(integration.CODE_DIR) + # Always ensure that the test tree is searched first for python modules + if integration.CODE_DIR != env_pypath[0]: + env_pypath.insert(0, integration.CODE_DIR) env_delta['PYTHONPATH'] = ':'.join(env_pypath) cmd_env = dict(os.environ) From 31ed4039b6a3268c346fa166b7e7c87c63a2271e Mon Sep 17 00:00:00 2001 From: David Boucha Date: Tue, 6 Sep 2016 15:43:05 -0600 Subject: [PATCH 052/100] add missing imports and namespace them Fixes #36087 --- salt/states/pkg.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/salt/states/pkg.py b/salt/states/pkg.py index 7021e074ed..52a7fbe589 100644 --- a/salt/states/pkg.py +++ b/salt/states/pkg.py @@ -96,12 +96,15 @@ import salt.ext.six as six _repack_pkgs = _namespaced_function(_repack_pkgs, globals()) if salt.utils.is_windows(): + from salt.ext.six.moves.urllib.parse import urlparse as _urlparse from salt.modules.win_pkg import _get_package_info from salt.modules.win_pkg import get_repo_data + from salt.modules.win_pkg import _get_repo_src_dest from salt.modules.win_pkg import _get_latest_pkg_version from salt.modules.win_pkg import _reverse_cmp_pkg_versions _get_package_info = _namespaced_function(_get_package_info, globals()) get_repo_data = _namespaced_function(get_repo_data, globals()) + _get_repo_src_dest = _namespaced_function(_get_repo_src_dest, globals()) _get_latest_pkg_version = \ _namespaced_function(_get_latest_pkg_version, globals()) _reverse_cmp_pkg_versions = \ From 75933869cba38806789190394def278fa1985c3f Mon Sep 17 00:00:00 2001 From: David Boucha Date: Tue, 6 Sep 2016 17:32:22 -0600 Subject: [PATCH 053/100] disable some pylint checks --- salt/states/pkg.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/salt/states/pkg.py b/salt/states/pkg.py index 52a7fbe589..73f69f5af8 100644 --- a/salt/states/pkg.py +++ b/salt/states/pkg.py @@ -96,7 +96,11 @@ import salt.ext.six as six _repack_pkgs = _namespaced_function(_repack_pkgs, globals()) if salt.utils.is_windows(): + # pylint: disable=W0611 + # pylint: disable=import-error,no-name-in-module from salt.ext.six.moves.urllib.parse import urlparse as _urlparse + # pylint: disable=import-error + # pylint: enable=W0611 from salt.modules.win_pkg import _get_package_info from salt.modules.win_pkg import get_repo_data from salt.modules.win_pkg import _get_repo_src_dest From 5c7b9f0341e95d2724333fefabb9d250d38cb88a Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 6 Sep 2016 11:11:22 -0500 Subject: [PATCH 054/100] Make "password" an explicit argument, not a kwarg --- salt/modules/cmdmod.py | 82 ++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 30 deletions(-) diff --git a/salt/modules/cmdmod.py b/salt/modules/cmdmod.py index b5d36383f3..ee590bde68 100644 --- a/salt/modules/cmdmod.py +++ b/salt/modules/cmdmod.py @@ -680,6 +680,7 @@ def run(cmd, saltenv='base', use_vt=False, bg=False, + password=None, **kwargs): r''' Execute the passed command and return the output as a string @@ -699,8 +700,8 @@ def run(cmd, :param str runas: User to run script as. If running on a Windows minion you must also pass a password - :param str password: Windows only. Pass a password if you specify runas. - This parameter will be ignored for other OS's + :param str password: Windows only. Required when specifying ``runas``. This + parameter will be ignored on non-Windows platforms. .. versionadded:: 2016.3.0 @@ -848,7 +849,7 @@ def run(cmd, pillarenv=kwargs.get('pillarenv'), pillar_override=kwargs.get('pillar'), use_vt=use_vt, - password=kwargs.get('password', None), + password=password, bg=bg) log_callback = _check_cb(log_callback) @@ -888,6 +889,7 @@ def shell(cmd, saltenv='base', use_vt=False, bg=False, + password=None, **kwargs): ''' Execute the passed command and return the output as a string. @@ -906,15 +908,16 @@ def shell(cmd, :param str runas: User to run script as. If running on a Windows minion you must also pass a password - :param str password: Windows only. Pass a password if you specify runas. - This parameter will be ignored for other OS's + :param str password: Windows only. Required when specifying ``runas``. This + parameter will be ignored on non-Windows platforms. .. versionadded:: 2016.3.0 :param int shell: Shell to execute under. Defaults to the system default shell. - :param bool bg: If True, run command in background and do not await or deliver it's results + :param bool bg: If True, run command in background and do not await or + deliver its results :param list env: A list of environment variables to be set prior to execution. @@ -1053,6 +1056,7 @@ def shell(cmd, use_vt=use_vt, python_shell=python_shell, bg=bg, + password=password, **kwargs) @@ -1074,6 +1078,7 @@ def run_stdout(cmd, ignore_retcode=False, saltenv='base', use_vt=False, + password=None, **kwargs): ''' Execute a command, and only return the standard out @@ -1090,8 +1095,8 @@ def run_stdout(cmd, :param str runas: User to run script as. If running on a Windows minion you must also pass a password - :param str password: Windows only. Pass a password if you specify runas. - This parameter will be ignored for other OS's + :param str password: Windows only. Required when specifying ``runas``. This + parameter will be ignored on non-Windows platforms. .. versionadded:: 2016.3.0 @@ -1212,6 +1217,7 @@ def run_stdout(cmd, pillarenv=kwargs.get('pillarenv'), pillar_override=kwargs.get('pillar'), use_vt=use_vt, + password=password, **kwargs) log_callback = _check_cb(log_callback) @@ -1255,6 +1261,7 @@ def run_stderr(cmd, ignore_retcode=False, saltenv='base', use_vt=False, + password=None, **kwargs): ''' Execute a command and only return the standard error @@ -1271,8 +1278,8 @@ def run_stderr(cmd, :param str runas: User to run script as. If running on a Windows minion you must also pass a password - :param str password: Windows only. Pass a password if you specify runas. - This parameter will be ignored for other OS's + :param str password: Windows only. Required when specifying ``runas``. This + parameter will be ignored on non-Windows platforms. .. versionadded:: 2016.3.0 @@ -1394,7 +1401,7 @@ def run_stderr(cmd, saltenv=saltenv, pillarenv=kwargs.get('pillarenv'), pillar_override=kwargs.get('pillar'), - password=kwargs.get('password', None)) + password=password) log_callback = _check_cb(log_callback) @@ -1438,6 +1445,7 @@ def run_all(cmd, saltenv='base', use_vt=False, redirect_stderr=False, + password=None, **kwargs): ''' Execute the passed command and return a dict of return data @@ -1454,8 +1462,8 @@ def run_all(cmd, :param str runas: User to run script as. If running on a Windows minion you must also pass a password - :param str password: Windows only. Pass a password if you specify runas. - This parameter will be ignored for other OS's + :param str password: Windows only. Required when specifying ``runas``. This + parameter will be ignored on non-Windows platforms. .. versionadded:: 2016.3.0 @@ -1587,7 +1595,7 @@ def run_all(cmd, pillarenv=kwargs.get('pillarenv'), pillar_override=kwargs.get('pillar'), use_vt=use_vt, - password=kwargs.get('password', None)) + password=password) log_callback = _check_cb(log_callback) @@ -1629,6 +1637,7 @@ def retcode(cmd, ignore_retcode=False, saltenv='base', use_vt=False, + password=None, **kwargs): ''' Execute a shell command and return the command's return code. @@ -1645,8 +1654,8 @@ def retcode(cmd, :param str runas: User to run script as. If running on a Windows minion you must also pass a password - :param str password: Windows only. Pass a password if you specify runas. - This parameter will be ignored for other OS's + :param str password: Windows only. Required when specifying ``runas``. This + parameter will be ignored on non-Windows platforms. .. versionadded:: 2016.3.0 @@ -1770,7 +1779,7 @@ def retcode(cmd, pillarenv=kwargs.get('pillarenv'), pillar_override=kwargs.get('pillar'), use_vt=use_vt, - password=kwargs.get('password', None)) + password=password) log_callback = _check_cb(log_callback) @@ -1807,6 +1816,7 @@ def _retcode_quiet(cmd, ignore_retcode=False, saltenv='base', use_vt=False, + password=None, **kwargs): ''' Helper for running commands quietly for minion startup. @@ -1829,6 +1839,7 @@ def _retcode_quiet(cmd, ignore_retcode=ignore_retcode, saltenv=saltenv, use_vt=use_vt, + password=password, **kwargs) @@ -1851,6 +1862,7 @@ def script(source, saltenv='base', use_vt=False, bg=False, + password=None, **kwargs): ''' Download a script from a remote location and execute the script locally. @@ -1879,8 +1891,8 @@ def script(source, :param str runas: User to run script as. If running on a Windows minion you must also pass a password - :param str password: Windows only. Pass a password if you specify runas. - This parameter will be ignored for other OS's + :param str password: Windows only. Required when specifying ``runas``. This + parameter will be ignored on non-Windows platforms. .. versionadded:: 2016.3.0 @@ -2038,8 +2050,8 @@ def script(source, pillarenv=kwargs.get('pillarenv'), pillar_override=kwargs.get('pillar'), use_vt=use_vt, - password=kwargs.get('password', None), - bg=bg) + bg=bg, + password=password) _cleanup_tempfile(path) return ret @@ -2061,6 +2073,7 @@ def script_retcode(source, output_loglevel='debug', log_callback=None, use_vt=False, + password=None, **kwargs): ''' Download a script from a remote location and execute the script locally. @@ -2093,8 +2106,8 @@ def script_retcode(source, :param str runas: User to run script as. If running on a Windows minion you must also pass a password - :param str password: Windows only. Pass a password if you specify runas. - This parameter will be ignored for other OS's + :param str password: Windows only. Required when specifying ``runas``. This + parameter will be ignored on non-Windows platforms. .. versionadded:: 2016.3.0 @@ -2201,6 +2214,7 @@ def script_retcode(source, output_loglevel=output_loglevel, log_callback=log_callback, use_vt=use_vt, + password=password, **kwargs)['retcode'] @@ -2348,10 +2362,10 @@ def run_chroot(root, This function runs :mod:`cmd.run_all ` wrapped within a chroot, with dev and proc mounted in the chroot - root: + root Path to the root of the jail to use. - cmd: + cmd The command to run. ex: 'ls -lart /home' cwd @@ -2586,6 +2600,7 @@ def powershell(cmd, ignore_retcode=False, saltenv='base', use_vt=False, + password=None, **kwargs): ''' Execute the passed PowerShell command and return the output as a string. @@ -2613,8 +2628,8 @@ def powershell(cmd, :param str runas: User to run script as. If running on a Windows minion you must also pass a password - :param str password: Windows only. Pass a password if you specify runas. - This parameter will be ignored for other OS's + :param str password: Windows only. Required when specifying ``runas``. This + parameter will be ignored on non-Windows platforms. .. versionadded:: 2016.3.0 @@ -2727,6 +2742,7 @@ def powershell(cmd, saltenv=saltenv, use_vt=use_vt, python_shell=python_shell, + password=password, **kwargs) try: @@ -2749,7 +2765,9 @@ def run_bg(cmd, output_loglevel='debug', log_callback=None, reset_system_locale=True, + ignore_retcode=False, saltenv='base', + password=None, **kwargs): r''' .. versionadded: 2016.3.0 @@ -2771,6 +2789,11 @@ def run_bg(cmd, :param str runas: User to run script as. If running on a Windows minion you must also pass a password + :param str password: Windows only. Required when specifying ``runas``. This + parameter will be ignored on non-Windows platforms. + + .. versionadded:: 2016.3.0 + :param str shell: Shell to execute under. Defaults to the system default shell. @@ -2896,12 +2919,11 @@ def run_bg(cmd, log_callback=log_callback, timeout=timeout, reset_system_locale=reset_system_locale, - # ignore_retcode=ignore_retcode, + ignore_retcode=ignore_retcode, saltenv=saltenv, pillarenv=kwargs.get('pillarenv'), pillar_override=kwargs.get('pillar'), - # password=kwargs.get('password', None), - ) + password=password) return { 'pid': res['pid'] From 5943b4662cb75bb0d4ea5af89816571455a88998 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 6 Sep 2016 15:32:15 -0500 Subject: [PATCH 055/100] Add "password" param to funcs which support the user parameter This allows Windows users to run commands as a different user --- salt/modules/git.py | 506 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 461 insertions(+), 45 deletions(-) diff --git a/salt/modules/git.py b/salt/modules/git.py index d8eafa9cf2..97a6210a53 100644 --- a/salt/modules/git.py +++ b/salt/modules/git.py @@ -57,6 +57,7 @@ def _config_getter(get_opt, value_regex=None, cwd=None, user=None, + password=None, ignore_retcode=False, **kwargs): ''' @@ -85,7 +86,7 @@ def _config_getter(get_opt, value_regex = None command = ['git', 'config'] - command.extend(_which_git_config(global_, cwd, user)) + command.extend(_which_git_config(global_, cwd, user, password)) command.append(get_opt) command.append(key) if value_regex is not None: @@ -93,6 +94,7 @@ def _config_getter(get_opt, return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode, failhard=False) @@ -144,7 +146,7 @@ def _format_opts(opts): return opts -def _git_run(command, cwd=None, runas=None, identity=None, +def _git_run(command, cwd=None, runas=None, password=None, identity=None, ignore_retcode=False, failhard=True, redirect_stderr=False, saltenv='base', **kwargs): ''' @@ -234,6 +236,7 @@ def _git_run(command, cwd=None, runas=None, identity=None, command, cwd=cwd, runas=runas, + password=password, env=env, python_shell=False, log_callback=salt.utils.url.redact_http_basic_auth, @@ -274,6 +277,7 @@ def _git_run(command, cwd=None, runas=None, identity=None, command, cwd=cwd, runas=runas, + password=password, env=env, python_shell=False, log_callback=salt.utils.url.redact_http_basic_auth, @@ -300,18 +304,18 @@ def _git_run(command, cwd=None, runas=None, identity=None, return result -def _get_toplevel(path, user=None): +def _get_toplevel(path, user=None, password=None): ''' Use git rev-parse to return the top level of a repo ''' return _git_run( ['git', 'rev-parse', '--show-toplevel'], cwd=path, - runas=user - )['stdout'] + runas=user, + password=password)['stdout'] -def _git_config(cwd, user): +def _git_config(cwd, user, password): ''' Helper to retrieve git config options ''' @@ -320,6 +324,7 @@ def _git_config(cwd, user): git_dir = rev_parse(cwd, opts=['--git-dir'], user=user, + password=password, ignore_retcode=True) if not os.path.isabs(git_dir): paths = (cwd, git_dir, 'config') @@ -329,7 +334,7 @@ def _git_config(cwd, user): return __context__[contextkey] -def _which_git_config(global_, cwd, user): +def _which_git_config(global_, cwd, user, password): ''' Based on whether global or local config is desired, return a list of CLI args to include in the git config command. @@ -342,10 +347,15 @@ def _which_git_config(global_, cwd, user): return ['--local'] else: # For earlier versions, need to specify the path to the git config file - return ['--file', _git_config(cwd, user)] + return ['--file', _git_config(cwd, user, password)] -def add(cwd, filename, opts='', user=None, ignore_retcode=False): +def add(cwd, + filename, + opts='', + user=None, + password=None, + ignore_retcode=False): ''' .. versionchanged:: 2015.8.0 The ``--verbose`` command line argument is now implied @@ -370,6 +380,12 @@ def add(cwd, filename, opts='', user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -397,6 +413,7 @@ def add(cwd, filename, opts='', user=None, ignore_retcode=False): return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -406,6 +423,7 @@ def archive(cwd, fmt=None, prefix=None, user=None, + password=None, ignore_retcode=False, **kwargs): ''' @@ -481,6 +499,12 @@ def archive(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -530,13 +554,24 @@ def archive(cwd, format_ = str(format_) command.extend(['--format', format_]) command.extend(['--output', output, rev]) - _git_run(command, cwd=cwd, runas=user, ignore_retcode=ignore_retcode) + _git_run(command, + cwd=cwd, + runas=user, + password=password, + ignore_retcode=ignore_retcode) # No output (unless --verbose is used, and we don't want all files listed - # in the output in case there are thousands), so just return True + # in the output in case there are thousands), so just return True. If there + # was an error in the git command, it will have already raised an exception + # and we will never get to this return statement. return True -def branch(cwd, name=None, opts='', user=None, ignore_retcode=False): +def branch(cwd, + name=None, + opts='', + user=None, + password=None, + ignore_retcode=False): ''' Interface to `git-branch(1)`_ @@ -565,6 +600,12 @@ def branch(cwd, name=None, opts='', user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -592,7 +633,11 @@ def branch(cwd, name=None, opts='', user=None, ignore_retcode=False): command.extend(_format_opts(opts)) if name is not None: command.append(name) - _git_run(command, cwd=cwd, runas=user, ignore_retcode=ignore_retcode) + _git_run(command, + cwd=cwd, + runas=user, + password=password, + ignore_retcode=ignore_retcode) return True @@ -601,6 +646,7 @@ def checkout(cwd, force=False, opts='', user=None, + password=None, ignore_retcode=False): ''' Interface to `git-checkout(1)`_ @@ -629,6 +675,12 @@ def checkout(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -671,6 +723,7 @@ def checkout(cwd, return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode, redirect_stderr=True)['stdout'] @@ -680,6 +733,7 @@ def clone(cwd, name=None, opts='', user=None, + password=None, identity=None, https_user=None, https_pass=None, @@ -715,7 +769,11 @@ def clone(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. - Run git as a user other than what the minion runs as + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 identity Path to a private key to use for ssh URLs @@ -811,6 +869,7 @@ def clone(cwd, _git_run(command, cwd=clone_cwd, runas=user, + password=password, identity=identity, ignore_retcode=ignore_retcode, saltenv=saltenv) @@ -821,6 +880,7 @@ def commit(cwd, message, opts='', user=None, + password=None, filename=None, ignore_retcode=False): ''' @@ -847,6 +907,12 @@ def commit(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + filename The location of the file/directory to commit, relative to ``cwd``. This argument is optional, and can be used to commit a file without @@ -886,12 +952,14 @@ def commit(cwd, return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] def config_get(key, cwd=None, user=None, + password=None, ignore_retcode=False, **kwargs): ''' @@ -925,6 +993,12 @@ def config_get(key, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -949,6 +1023,7 @@ def config_get(key, key, cwd=cwd, user=user, + password=password, ignore_retcode=ignore_retcode, **kwargs) @@ -970,6 +1045,7 @@ def config_get_regexp(key, value_regex=None, cwd=None, user=None, + password=None, ignore_retcode=False, **kwargs): r''' @@ -1005,6 +1081,12 @@ def config_get_regexp(key, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -1026,6 +1108,7 @@ def config_get_regexp(key, value_regex=value_regex, cwd=cwd, user=user, + password=password, ignore_retcode=ignore_retcode, **kwargs) @@ -1049,6 +1132,7 @@ def config_set(key, multivar=None, cwd=None, user=None, + password=None, ignore_retcode=False, **kwargs): ''' @@ -1092,6 +1176,12 @@ def config_set(key, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -1176,6 +1266,7 @@ def config_set(key, _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode) else: for idx, target in enumerate(multivar): @@ -1188,9 +1279,11 @@ def config_set(key, _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode) return config_get(key, user=user, + password=password, cwd=cwd, ignore_retcode=ignore_retcode, **{'all': True, 'global': global_}) @@ -1200,6 +1293,7 @@ def config_unset(key, value_regex=None, cwd=None, user=None, + password=None, ignore_retcode=False, **kwargs): ''' @@ -1230,6 +1324,12 @@ def config_unset(key, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -1261,7 +1361,7 @@ def config_unset(key, command.append('--unset-all') else: command.append('--unset') - command.extend(_which_git_config(global_, cwd, user)) + command.extend(_which_git_config(global_, cwd, user, password)) if not isinstance(key, six.string_types): key = str(key) @@ -1273,6 +1373,7 @@ def config_unset(key, ret = _git_run(command, cwd=cwd if cwd != 'global' else None, runas=user, + password=password, ignore_retcode=ignore_retcode, failhard=False) retcode = ret['retcode'] @@ -1284,6 +1385,7 @@ def config_unset(key, if config_get(cwd, key, user=user, + password=password, ignore_retcode=ignore_retcode) is None: raise CommandExecutionError( 'Key \'{0}\' does not exist'.format(key) @@ -1305,7 +1407,10 @@ def config_unset(key, raise CommandExecutionError(msg) -def current_branch(cwd, user=None, ignore_retcode=False): +def current_branch(cwd, + user=None, + password=None, + ignore_retcode=False): ''' Returns the current branch name of a local checkout. If HEAD is detached, return the SHA1 of the revision which is currently checked out. @@ -1317,6 +1422,12 @@ def current_branch(cwd, user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -1335,10 +1446,15 @@ def current_branch(cwd, user=None, ignore_retcode=False): return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] -def describe(cwd, rev='HEAD', user=None, ignore_retcode=False): +def describe(cwd, + rev='HEAD', + user=None, + password=None, + ignore_retcode=False): ''' Returns the `git-describe(1)`_ string (or the SHA1 hash if there are no tags) for the given revision. @@ -1353,6 +1469,12 @@ def describe(cwd, rev='HEAD', user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -1379,6 +1501,7 @@ def describe(cwd, rev='HEAD', user=None, ignore_retcode=False): return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -1387,6 +1510,7 @@ def diff(cwd, item2=None, opts='', user=None, + password=None, no_index=False, cached=False, paths=None): @@ -1418,6 +1542,12 @@ def diff(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + no_index : False When it is necessary to diff two files in the same repo against each other, and not diff two different revisions, set this option to @@ -1516,6 +1646,7 @@ def diff(cwd, return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode, failhard=failhard, redirect_stderr=True)['stdout'] @@ -1527,6 +1658,7 @@ def fetch(cwd, refspecs=None, opts='', user=None, + password=None, identity=None, ignore_retcode=False, saltenv='base'): @@ -1569,6 +1701,12 @@ def fetch(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + identity Path to a private key to use for ssh URLs @@ -1640,6 +1778,7 @@ def fetch(cwd, output = _git_run(command, cwd=cwd, runas=user, + password=password, identity=identity, ignore_retcode=ignore_retcode, redirect_stderr=True, @@ -1679,6 +1818,7 @@ def init(cwd, shared=None, opts='', user=None, + password=None, ignore_retcode=False): ''' Interface to `git-init(1)`_ @@ -1719,6 +1859,12 @@ def init(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -1763,10 +1909,13 @@ def init(cwd, command.append(cwd) return _git_run(command, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] -def is_worktree(cwd, user=None): +def is_worktree(cwd, + user=None, + password=None): ''' .. versionadded:: 2015.8.0 @@ -1781,6 +1930,12 @@ def is_worktree(cwd, user=None): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + CLI Example: @@ -1790,7 +1945,7 @@ def is_worktree(cwd, user=None): ''' cwd = _expand_path(cwd, user) try: - toplevel = _get_toplevel(cwd) + toplevel = _get_toplevel(cwd, user=user, password=password) except CommandExecutionError: return False gitdir = os.path.join(toplevel, '.git') @@ -1816,7 +1971,11 @@ def is_worktree(cwd, user=None): return False -def list_branches(cwd, remote=False, user=None, ignore_retcode=False): +def list_branches(cwd, + remote=False, + user=None, + password=None, + ignore_retcode=False): ''' .. versionadded:: 2015.8.0 @@ -1839,6 +1998,12 @@ def list_branches(cwd, remote=False, user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -1859,10 +2024,14 @@ def list_branches(cwd, remote=False, user=None, ignore_retcode=False): return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'].splitlines() -def list_tags(cwd, user=None, ignore_retcode=False): +def list_tags(cwd, + user=None, + password=None, + ignore_retcode=False): ''' .. versionadded:: 2015.8.0 @@ -1875,6 +2044,12 @@ def list_tags(cwd, user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -1894,10 +2069,15 @@ def list_tags(cwd, user=None, ignore_retcode=False): return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'].splitlines() -def list_worktrees(cwd, stale=False, user=None, **kwargs): +def list_worktrees(cwd, + stale=False, + user=None, + password=None, + **kwargs): ''' .. versionadded:: 2015.8.0 @@ -1922,6 +2102,12 @@ def list_worktrees(cwd, stale=False, user=None, **kwargs): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + all : False If ``True``, then return all worktrees tracked under $GIT_DIR/worktrees, including ones for which the gitdir is no longer @@ -1957,13 +2143,14 @@ def list_worktrees(cwd, stale=False, user=None, **kwargs): '\'all\' and \'stale\' cannot both be set to True' ) - def _git_tag_points_at(cwd, rev, user=None): + def _git_tag_points_at(cwd, rev, user=None, password=None): ''' Get any tags that point at a ''' return _git_run(['git', 'tag', '--points-at', rev], cwd=cwd, - runas=user)['stdout'].splitlines() + runas=user, + password=password)['stdout'].splitlines() def _desired(is_stale, all_, stale): ''' @@ -1999,7 +2186,8 @@ def list_worktrees(cwd, stale=False, user=None, **kwargs): if has_native_list_subcommand: out = _git_run(['git', 'worktree', 'list', '--porcelain'], cwd=cwd, - runas=user) + runas=user, + password=password) if out['retcode'] != 0: msg = 'Failed to list worktrees' if out['stderr']: @@ -2068,7 +2256,10 @@ def list_worktrees(cwd, stale=False, user=None, **kwargs): if wt_ptr['detached']: wt_ptr['branch'] = None # Check to see if HEAD points at a tag - tags_found = _git_tag_points_at(cwd, wt_ptr['HEAD'], user) + tags_found = _git_tag_points_at(cwd, + wt_ptr['HEAD'], + user=user, + password=password) if tags_found: wt_ptr['tags'] = tags_found else: @@ -2078,11 +2269,12 @@ def list_worktrees(cwd, stale=False, user=None, **kwargs): return ret else: - toplevel = _get_toplevel(cwd, user) + toplevel = _get_toplevel(cwd, user=user, password=password) try: worktree_root = rev_parse(cwd, opts=['--git-path', 'worktrees'], - user=user) + user=user, + password=password) except CommandExecutionError as exc: msg = 'Failed to find worktree location for ' + cwd log.error(msg, exc_info_on_loglevel=logging.DEBUG) @@ -2146,7 +2338,10 @@ def list_worktrees(cwd, stale=False, user=None, **kwargs): if head_ref.startswith('ref: '): head_ref = head_ref.split(None, 1)[-1] wt_branch = head_ref.replace('refs/heads/', '', 1) - wt_head = rev_parse(cwd, rev=head_ref, user=user) + wt_head = rev_parse(cwd, + rev=head_ref, + user=user, + password=password) wt_detached = False else: wt_branch = None @@ -2161,7 +2356,10 @@ def list_worktrees(cwd, stale=False, user=None, **kwargs): # Check to see if HEAD points at a tag if wt_detached: - tags_found = _git_tag_points_at(cwd, wt_head, user) + tags_found = _git_tag_points_at(cwd, + wt_head, + user=user, + password=password) if tags_found: wt_ptr['tags'] = tags_found @@ -2173,6 +2371,7 @@ def ls_remote(cwd=None, ref=None, opts='', user=None, + password=None, identity=None, https_user=None, https_pass=None, @@ -2215,6 +2414,12 @@ def ls_remote(cwd=None, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + identity Path to a private key to use for ssh URLs @@ -2290,6 +2495,7 @@ def ls_remote(cwd=None, output = _git_run(command, cwd=cwd, runas=user, + password=password, identity=identity, ignore_retcode=ignore_retcode, saltenv=saltenv)['stdout'] @@ -2307,6 +2513,7 @@ def merge(cwd, rev=None, opts='', user=None, + password=None, ignore_retcode=False, **kwargs): ''' @@ -2340,6 +2547,12 @@ def merge(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -2382,6 +2595,7 @@ def merge(cwd, return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -2393,6 +2607,7 @@ def merge_base(cwd, fork_point=None, opts='', user=None, + password=None, ignore_retcode=False, **kwargs): ''' @@ -2459,6 +2674,12 @@ def merge_base(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False if ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -2529,11 +2750,13 @@ def merge_base(cwd, rev=refs[0], opts=['--verify'], user=user, + password=password, ignore_retcode=ignore_retcode) return merge_base(cwd, refs=refs, is_ancestor=False, user=user, + password=password, ignore_retcode=ignore_retcode) == first_commit command = ['git', 'merge-base'] @@ -2556,6 +2779,7 @@ def merge_base(cwd, result = _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode, failhard=False if is_ancestor else True) if is_ancestor: @@ -2571,6 +2795,7 @@ def merge_tree(cwd, ref2, base=None, user=None, + password=None, ignore_retcode=False): ''' .. versionadded:: 2015.8.0 @@ -2596,6 +2821,12 @@ def merge_tree(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False if ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -2628,10 +2859,17 @@ def merge_tree(cwd, return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] -def pull(cwd, opts='', user=None, identity=None, ignore_retcode=False, saltenv='base'): +def pull(cwd, + opts='', + user=None, + password=None, + identity=None, + ignore_retcode=False, + saltenv='base'): ''' Interface to `git-pull(1)`_ @@ -2650,6 +2888,12 @@ def pull(cwd, opts='', user=None, identity=None, ignore_retcode=False, saltenv=' User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + identity Path to a private key to use for ssh URLs @@ -2698,6 +2942,7 @@ def pull(cwd, opts='', user=None, identity=None, ignore_retcode=False, saltenv=' return _git_run(command, cwd=cwd, runas=user, + password=password, identity=identity, ignore_retcode=ignore_retcode, saltenv=saltenv)['stdout'] @@ -2708,6 +2953,7 @@ def push(cwd, ref=None, opts='', user=None, + password=None, identity=None, ignore_retcode=False, saltenv='base', @@ -2748,6 +2994,12 @@ def push(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + identity Path to a private key to use for ssh URLs @@ -2819,12 +3071,18 @@ def push(cwd, return _git_run(command, cwd=cwd, runas=user, + password=password, identity=identity, ignore_retcode=ignore_retcode, saltenv=saltenv)['stdout'] -def rebase(cwd, rev='master', opts='', user=None, ignore_retcode=False): +def rebase(cwd, + rev='master', + opts='', + user=None, + password=None, + ignore_retcode=False): ''' Interface to `git-rebase(1)`_ @@ -2846,6 +3104,12 @@ def rebase(cwd, rev='master', opts='', user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -2875,12 +3139,14 @@ def rebase(cwd, rev='master', opts='', user=None, ignore_retcode=False): return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] def remote_get(cwd, remote='origin', user=None, + password=None, redact_auth=True, ignore_retcode=False): ''' @@ -2896,6 +3162,12 @@ def remote_get(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + redact_auth : True Set to ``False`` to include the username/password if the remote uses HTTPS Basic Auth. Otherwise, this information will be redacted. @@ -2925,6 +3197,7 @@ def remote_get(cwd, cwd = _expand_path(cwd, user) all_remotes = remotes(cwd, user=user, + password=password, redact_auth=redact_auth, ignore_retcode=ignore_retcode) if remote not in all_remotes: @@ -2939,6 +3212,7 @@ def remote_refs(url, heads=False, tags=False, user=None, + password=None, identity=None, https_user=None, https_pass=None, @@ -2962,6 +3236,12 @@ def remote_refs(url, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + identity Path to a private key to use for ssh URLs @@ -3020,6 +3300,7 @@ def remote_refs(url, raise SaltInvocationError(exc.__str__()) output = _git_run(command, runas=user, + password=password, identity=identity, ignore_retcode=ignore_retcode, saltenv=saltenv)['stdout'] @@ -3037,6 +3318,7 @@ def remote_set(cwd, url, remote='origin', user=None, + password=None, https_user=None, https_pass=None, push_url=None, @@ -3062,6 +3344,12 @@ def remote_set(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + https_user Set HTTP Basic Auth username. Only accepted for HTTPS URLs. @@ -3100,13 +3388,17 @@ def remote_set(cwd, salt myminion git.remote_set /path/to/repo https://github.com/user/repo.git remote=upstream push_url=git@github.com:user/repo.git ''' # Check if remote exists - if remote in remotes(cwd, user=user): + if remote in remotes(cwd, user=user, password=password): log.debug( 'Remote \'{0}\' already exists in git checkout located at {1}, ' 'removing so it can be re-added'.format(remote, cwd) ) command = ['git', 'remote', 'rm', remote] - _git_run(command, cwd=cwd, runas=user, ignore_retcode=ignore_retcode) + _git_run(command, + cwd=cwd, + runas=user, + password=password, + ignore_retcode=ignore_retcode) # Add remote try: url = salt.utils.url.add_http_basic_auth(url, @@ -3120,7 +3412,11 @@ def remote_set(cwd, if not isinstance(url, six.string_types): url = str(url) command = ['git', 'remote', 'add', remote, url] - _git_run(command, cwd=cwd, runas=user, ignore_retcode=ignore_retcode) + _git_run(command, + cwd=cwd, + runas=user, + password=password, + ignore_retcode=ignore_retcode) if push_url: if not isinstance(push_url, six.string_types): push_url = str(push_url) @@ -3132,14 +3428,23 @@ def remote_set(cwd, except ValueError as exc: raise SaltInvocationError(exc.__str__()) command = ['git', 'remote', 'set-url', '--push', remote, push_url] - _git_run(command, cwd=cwd, runas=user, ignore_retcode=ignore_retcode) + _git_run(command, + cwd=cwd, + runas=user, + password=password, + ignore_retcode=ignore_retcode) return remote_get(cwd=cwd, remote=remote, user=user, + password=password, ignore_retcode=ignore_retcode) -def remotes(cwd, user=None, redact_auth=True, ignore_retcode=False): +def remotes(cwd, + user=None, + password=None, + redact_auth=True, + ignore_retcode=False): ''' Get fetch and push URLs for each remote in a git checkout @@ -3150,6 +3455,12 @@ def remotes(cwd, user=None, redact_auth=True, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + redact_auth : True Set to ``False`` to include the username/password for authenticated remotes in the return data. Otherwise, this information will be @@ -3182,6 +3493,7 @@ def remotes(cwd, user=None, redact_auth=True, ignore_retcode=False): output = _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] for remote_line in salt.utils.itertools.split(output, '\n'): try: @@ -3206,7 +3518,11 @@ def remotes(cwd, user=None, redact_auth=True, ignore_retcode=False): return ret -def reset(cwd, opts='', user=None, ignore_retcode=False): +def reset(cwd, + opts='', + user=None, + password=None, + ignore_retcode=False): ''' Interface to `git-reset(1)`_, returns the stdout from the git command @@ -3225,6 +3541,12 @@ def reset(cwd, opts='', user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -3249,10 +3571,16 @@ def reset(cwd, opts='', user=None, ignore_retcode=False): return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] -def rev_parse(cwd, rev=None, opts='', user=None, ignore_retcode=False): +def rev_parse(cwd, + rev=None, + opts='', + user=None, + password=None, + ignore_retcode=False): ''' .. versionadded:: 2015.8.0 @@ -3275,6 +3603,12 @@ def rev_parse(cwd, rev=None, opts='', user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -3309,10 +3643,16 @@ def rev_parse(cwd, rev=None, opts='', user=None, ignore_retcode=False): return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] -def revision(cwd, rev='HEAD', short=False, user=None, ignore_retcode=False): +def revision(cwd, + rev='HEAD', + short=False, + user=None, + password=None, + ignore_retcode=False): ''' Returns the SHA1 hash of a given identifier (hash, branch, tag, HEAD, etc.) @@ -3329,6 +3669,12 @@ def revision(cwd, rev='HEAD', short=False, user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -3351,10 +3697,16 @@ def revision(cwd, rev='HEAD', short=False, user=None, ignore_retcode=False): return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] -def rm_(cwd, filename, opts='', user=None, ignore_retcode=False): +def rm_(cwd, + filename, + opts='', + user=None, + password=None, + ignore_retcode=False): ''' Interface to `git-rm(1)`_ @@ -3380,6 +3732,12 @@ def rm_(cwd, filename, opts='', user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -3404,10 +3762,16 @@ def rm_(cwd, filename, opts='', user=None, ignore_retcode=False): return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] -def stash(cwd, action='save', opts='', user=None, ignore_retcode=False): +def stash(cwd, + action='save', + opts='', + user=None, + password=None, + ignore_retcode=False): ''' Interface to `git-stash(1)`_, returns the stdout from the git command @@ -3425,6 +3789,12 @@ def stash(cwd, action='save', opts='', user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -3453,10 +3823,14 @@ def stash(cwd, action='save', opts='', user=None, ignore_retcode=False): return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] -def status(cwd, user=None, ignore_retcode=False): +def status(cwd, + user=None, + password=None, + ignore_retcode=False): ''' .. versionchanged:: 2015.8.0 Return data has changed from a list of lists to a dictionary @@ -3470,6 +3844,12 @@ def status(cwd, user=None, ignore_retcode=False): User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -3495,6 +3875,7 @@ def status(cwd, user=None, ignore_retcode=False): output = _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] for line in output.split('\0'): try: @@ -3509,6 +3890,7 @@ def submodule(cwd, command, opts='', user=None, + password=None, identity=None, ignore_retcode=False, saltenv='base', @@ -3552,6 +3934,12 @@ def submodule(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + identity Path to a private key to use for ssh URLs @@ -3625,6 +4013,7 @@ def submodule(cwd, return _git_run(cmd, cwd=cwd, runas=user, + password=password, identity=identity, ignore_retcode=ignore_retcode, saltenv=saltenv)['stdout'] @@ -3635,6 +4024,7 @@ def symbolic_ref(cwd, value=None, opts='', user=None, + password=None, ignore_retcode=False): ''' .. versionadded:: 2015.8.0 @@ -3662,6 +4052,12 @@ def symbolic_ref(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -3697,6 +4093,7 @@ def symbolic_ref(cwd, return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -3758,6 +4155,7 @@ def worktree_add(cwd, detach=False, opts='', user=None, + password=None, ignore_retcode=False, **kwargs): ''' @@ -3812,6 +4210,12 @@ def worktree_add(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -3866,6 +4270,7 @@ def worktree_add(cwd, return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode, redirect_stderr=True)['stdout'] @@ -3876,6 +4281,7 @@ def worktree_prune(cwd, expire=None, opts='', user=None, + password=None, ignore_retcode=False): ''' .. versionadded:: 2015.8.0 @@ -3916,6 +4322,12 @@ def worktree_prune(cwd, User under which to run the git command. By default, the command is run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + ignore_retcode : False If ``True``, do not log an error to the minion log if the git command returns a nonzero exit status. @@ -3949,6 +4361,7 @@ def worktree_prune(cwd, return _git_run(command, cwd=cwd, runas=user, + password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -3971,8 +4384,11 @@ def worktree_rm(cwd, user=None): Path to the worktree to be removed user - User under which to run the git command. By default, the command is run - by the user under which the minion is running. + Used for path expansion when ``cwd`` is not an absolute path. By + default, when ``cwd`` is not absolute, the path will be assumed to be + relative to the home directory of the user under which the minion is + running. Setting this option will change the home directory from which + path expansion is performed. CLI Examples: From 7871065d32181f98c16837987d15e9450e57a07d Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 6 Sep 2016 15:36:20 -0500 Subject: [PATCH 056/100] Use "user" instead of "runas" in _git_run() helper This makes the calls to this function more uniform in the execution module. --- salt/modules/git.py | 92 ++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/salt/modules/git.py b/salt/modules/git.py index 97a6210a53..0f081f2e0e 100644 --- a/salt/modules/git.py +++ b/salt/modules/git.py @@ -93,7 +93,7 @@ def _config_getter(get_opt, command.append(value_regex) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode, failhard=False) @@ -146,7 +146,7 @@ def _format_opts(opts): return opts -def _git_run(command, cwd=None, runas=None, password=None, identity=None, +def _git_run(command, cwd=None, user=None, password=None, identity=None, ignore_retcode=False, failhard=True, redirect_stderr=False, saltenv='base', **kwargs): ''' @@ -213,7 +213,7 @@ def _git_run(command, cwd=None, runas=None, password=None, identity=None, tmp_file = salt.utils.mkstemp() salt.utils.files.copyfile(ssh_id_wrapper, tmp_file) os.chmod(tmp_file, 0o500) - os.chown(tmp_file, __salt__['file.user_to_uid'](runas), -1) + os.chown(tmp_file, __salt__['file.user_to_uid'](user), -1) env['GIT_SSH'] = tmp_file if 'salt-call' not in _salt_cli \ @@ -235,7 +235,7 @@ def _git_run(command, cwd=None, runas=None, password=None, identity=None, result = __salt__['cmd.run_all']( command, cwd=cwd, - runas=runas, + runas=user, password=password, env=env, python_shell=False, @@ -276,7 +276,7 @@ def _git_run(command, cwd=None, runas=None, password=None, identity=None, result = __salt__['cmd.run_all']( command, cwd=cwd, - runas=runas, + runas=user, password=password, env=env, python_shell=False, @@ -311,7 +311,7 @@ def _get_toplevel(path, user=None, password=None): return _git_run( ['git', 'rev-parse', '--show-toplevel'], cwd=path, - runas=user, + user=user, password=password)['stdout'] @@ -412,7 +412,7 @@ def add(cwd, command.extend(['--', filename]) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -556,7 +556,7 @@ def archive(cwd, command.extend(['--output', output, rev]) _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode) # No output (unless --verbose is used, and we don't want all files listed @@ -635,7 +635,7 @@ def branch(cwd, command.append(name) _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode) return True @@ -722,7 +722,7 @@ def checkout(cwd, # Checkout message goes to stderr return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode, redirect_stderr=True)['stdout'] @@ -868,7 +868,7 @@ def clone(cwd, clone_cwd = '/tmp' if not salt.utils.is_windows() else None _git_run(command, cwd=clone_cwd, - runas=user, + user=user, password=password, identity=identity, ignore_retcode=ignore_retcode, @@ -951,7 +951,7 @@ def commit(cwd, command.extend(['--', filename]) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -1265,7 +1265,7 @@ def config_set(key, command.extend([key, value]) _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode) else: @@ -1278,7 +1278,7 @@ def config_set(key, command.extend([key, target]) _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode) return config_get(key, @@ -1372,7 +1372,7 @@ def config_unset(key, command.append(value_regex) ret = _git_run(command, cwd=cwd if cwd != 'global' else None, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode, failhard=False) @@ -1445,7 +1445,7 @@ def current_branch(cwd, command = ['git', 'rev-parse', '--abbrev-ref', 'HEAD'] return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -1500,7 +1500,7 @@ def describe(cwd, command.append(rev) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -1645,7 +1645,7 @@ def diff(cwd, return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode, failhard=failhard, @@ -1777,7 +1777,7 @@ def fetch(cwd, command.extend(refspec_list) output = _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, identity=identity, ignore_retcode=ignore_retcode, @@ -1908,7 +1908,7 @@ def init(cwd, command.extend(_format_opts(opts)) command.append(cwd) return _git_run(command, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -2023,7 +2023,7 @@ def list_branches(cwd, 'refs/{0}/'.format('heads' if not remote else 'remotes')] return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'].splitlines() @@ -2068,7 +2068,7 @@ def list_tags(cwd, 'refs/tags/'] return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'].splitlines() @@ -2149,7 +2149,7 @@ def list_worktrees(cwd, ''' return _git_run(['git', 'tag', '--points-at', rev], cwd=cwd, - runas=user, + user=user, password=password)['stdout'].splitlines() def _desired(is_stale, all_, stale): @@ -2186,7 +2186,7 @@ def list_worktrees(cwd, if has_native_list_subcommand: out = _git_run(['git', 'worktree', 'list', '--porcelain'], cwd=cwd, - runas=user, + user=user, password=password) if out['retcode'] != 0: msg = 'Failed to list worktrees' @@ -2494,7 +2494,7 @@ def ls_remote(cwd=None, command.extend([ref]) output = _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, identity=identity, ignore_retcode=ignore_retcode, @@ -2594,7 +2594,7 @@ def merge(cwd, command.append(rev) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -2778,7 +2778,7 @@ def merge_base(cwd, command.append(str(ref)) result = _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode, failhard=False if is_ancestor else True) @@ -2858,7 +2858,7 @@ def merge_tree(cwd, command.extend([base, ref1, ref2]) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -2941,7 +2941,7 @@ def pull(cwd, command.extend(_format_opts(opts)) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, identity=identity, ignore_retcode=ignore_retcode, @@ -3070,7 +3070,7 @@ def push(cwd, command.extend([remote, ref]) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, identity=identity, ignore_retcode=ignore_retcode, @@ -3138,7 +3138,7 @@ def rebase(cwd, command.extend(salt.utils.shlex_split(rev)) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -3299,7 +3299,7 @@ def remote_refs(url, except ValueError as exc: raise SaltInvocationError(exc.__str__()) output = _git_run(command, - runas=user, + user=user, password=password, identity=identity, ignore_retcode=ignore_retcode, @@ -3396,7 +3396,7 @@ def remote_set(cwd, command = ['git', 'remote', 'rm', remote] _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode) # Add remote @@ -3414,7 +3414,7 @@ def remote_set(cwd, command = ['git', 'remote', 'add', remote, url] _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode) if push_url: @@ -3430,7 +3430,7 @@ def remote_set(cwd, command = ['git', 'remote', 'set-url', '--push', remote, push_url] _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode) return remote_get(cwd=cwd, @@ -3492,7 +3492,7 @@ def remotes(cwd, ret = {} output = _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] for remote_line in salt.utils.itertools.split(output, '\n'): @@ -3570,7 +3570,7 @@ def reset(cwd, command.extend(_format_opts(opts)) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -3642,7 +3642,7 @@ def rev_parse(cwd, command.append(rev) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -3696,7 +3696,7 @@ def revision(cwd, command.append(rev) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -3761,7 +3761,7 @@ def rm_(cwd, command.extend(['--', filename]) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -3822,7 +3822,7 @@ def stash(cwd, command.extend(_format_opts(opts)) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -3874,7 +3874,7 @@ def status(cwd, command = ['git', 'status', '-z', '--porcelain'] output = _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] for line in output.split('\0'): @@ -4012,7 +4012,7 @@ def submodule(cwd, cmd.extend(_format_opts(opts)) return _git_run(cmd, cwd=cwd, - runas=user, + user=user, password=password, identity=identity, ignore_retcode=ignore_retcode, @@ -4092,7 +4092,7 @@ def symbolic_ref(cwd, command.extend(value) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] @@ -4269,7 +4269,7 @@ def worktree_add(cwd, # Checkout message goes to stderr return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode, redirect_stderr=True)['stdout'] @@ -4360,7 +4360,7 @@ def worktree_prune(cwd, command.extend(_format_opts(opts)) return _git_run(command, cwd=cwd, - runas=user, + user=user, password=password, ignore_retcode=ignore_retcode)['stdout'] From 91eafddda600244c4e5861a587ea96637a33af40 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Tue, 6 Sep 2016 16:21:29 -0500 Subject: [PATCH 057/100] Pass the "password" param to git module functions --- salt/states/git.py | 187 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 149 insertions(+), 38 deletions(-) diff --git a/salt/states/git.py b/salt/states/git.py index 4683fdc380..695dd5f751 100644 --- a/salt/states/git.py +++ b/salt/states/git.py @@ -105,7 +105,7 @@ def _get_branch_opts(branch, local_branch, all_local_branches, return ret -def _get_local_rev_and_branch(target, user): +def _get_local_rev_and_branch(target, user, password): ''' Return the local revision for before/after comparisons ''' @@ -113,6 +113,7 @@ def _get_local_rev_and_branch(target, user): try: local_rev = __salt__['git.revision'](target, user=user, + password=password, ignore_retcode=True) except CommandExecutionError: log.info('No local revision for {0}'.format(target)) @@ -122,6 +123,7 @@ def _get_local_rev_and_branch(target, user): try: local_branch = __salt__['git.current_branch'](target, user=user, + password=password, ignore_retcode=True) except CommandExecutionError: log.info('No local branch for {0}'.format(target)) @@ -205,6 +207,7 @@ def latest(name, target=None, branch=None, user=None, + password=None, update_head=True, force_checkout=False, force_clone=False, @@ -262,6 +265,12 @@ def latest(name, .. versionadded:: 0.17.0 + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + update_head : True If set to ``False``, then the remote repository will be fetched (if necessary) to ensure that the commit to which ``rev`` points exists in @@ -503,6 +512,8 @@ def latest(name, branch = str(branch) if user is not None and not isinstance(user, six.string_types): user = str(user) + if password is not None and not isinstance(password, six.string_types): + password = str(password) if remote is not None and not isinstance(remote, six.string_types): remote = str(remote) if identity is not None: @@ -564,7 +575,7 @@ def latest(name, return _fail(ret, ('\'rev\' is not compatible with the \'mirror\' and ' '\'bare\' arguments')) - run_check_cmd_kwargs = {'runas': user} + run_check_cmd_kwargs = {'runas': user, 'password': password} if 'shell' in __grains__: run_check_cmd_kwargs['shell'] = __grains__['shell'] @@ -588,6 +599,7 @@ def latest(name, heads=False, tags=False, user=user, + password=password, identity=identity, https_user=https_user, https_pass=https_pass, @@ -682,13 +694,18 @@ def latest(name, check = 'refs' if bare else '.git' gitdir = os.path.join(target, check) comments = [] - if os.path.isdir(gitdir) or __salt__['git.is_worktree'](target): + if os.path.isdir(gitdir) or __salt__['git.is_worktree'](target, + user=user, + password=password): # Target directory is a git repository or git worktree try: all_local_branches = __salt__['git.list_branches']( - target, user=user) - all_local_tags = __salt__['git.list_tags'](target, user=user) - local_rev, local_branch = _get_local_rev_and_branch(target, user) + target, user=user, password=password) + all_local_tags = __salt__['git.list_tags'](target, + user=user, + password=password) + local_rev, local_branch = \ + _get_local_rev_and_branch(target, user, password) if not bare and remote_rev is None and local_rev is not None: return _fail( @@ -723,6 +740,7 @@ def latest(name, target, branch + '^{commit}', user=user, + password=password, ignore_retcode=True) except CommandExecutionError as exc: return _fail( @@ -734,12 +752,16 @@ def latest(name, remotes = __salt__['git.remotes'](target, user=user, + password=password, redact_auth=False) revs_match = _revs_equal(local_rev, remote_rev, remote_rev_type) try: local_changes = bool( - __salt__['git.diff'](target, 'HEAD', user=user) + __salt__['git.diff'](target, + 'HEAD', + user=user, + password=password) ) except CommandExecutionError: # No need to capture the error and log it, the _git_run() @@ -767,6 +789,8 @@ def latest(name, __salt__['git.rev_parse']( target, remote_rev + '^{commit}', + user=user, + password=password, ignore_retcode=True) except CommandExecutionError: # Local checkout doesn't have the remote_rev @@ -787,6 +811,7 @@ def latest(name, target, desired_upstream, user=user, + password=password, ignore_retcode=True) except CommandExecutionError: pass @@ -806,6 +831,7 @@ def latest(name, target, rev + '^{commit}', user=user, + password=password, ignore_retcode=True) except CommandExecutionError: # Shouldn't happen if the tag exists @@ -875,6 +901,7 @@ def latest(name, refs=[base_rev, remote_rev], is_ancestor=True, user=user, + password=password, ignore_retcode=True) if fast_forward is False: @@ -903,6 +930,7 @@ def latest(name, base_branch + '@{upstream}', opts=['--abbrev-ref'], user=user, + password=password, ignore_retcode=True) except CommandExecutionError: # There is a local branch but the rev-parse command @@ -970,6 +998,7 @@ def latest(name, url=name, remote=remote, user=user, + password=password, https_user=https_user, https_pass=https_pass) comments.append( @@ -1121,6 +1150,7 @@ def latest(name, force=force_fetch, refspecs=refspecs, user=user, + password=password, identity=identity, saltenv=__env__) except CommandExecutionError as exc: @@ -1136,6 +1166,8 @@ def latest(name, __salt__['git.rev_parse']( target, remote_rev + '^{commit}', + user=user, + password=password, ignore_retcode=True) except CommandExecutionError as exc: return _fail( @@ -1167,7 +1199,8 @@ def latest(name, target, refs=[base_rev, remote_rev], is_ancestor=True, - user=user) + user=user, + password=password) if fast_forward is False and not force_reset: return _not_fast_forward( @@ -1207,7 +1240,8 @@ def latest(name, checkout_rev, force=force_checkout, opts=checkout_opts, - user=user) + user=user, + password=password) if '-b' in checkout_opts: comments.append( 'New branch \'{0}\' was checked out, with {1} ' @@ -1228,7 +1262,8 @@ def latest(name, __salt__['git.reset']( target, opts=['--hard', remote_rev], - user=user + user=user, + password=password, ) ret['changes']['forced update'] = True comments.append( @@ -1239,7 +1274,8 @@ def latest(name, __salt__['git.branch']( target, opts=branch_opts, - user=user) + user=user, + password=password) comments.append(upstream_action) # Fast-forward to the desired revision @@ -1255,6 +1291,8 @@ def latest(name, if __salt__['git.symbolic_ref'](target, 'HEAD', opts=['--quiet'], + user=user, + password=password, ignore_retcode=True): merge_rev = remote_rev if rev == 'HEAD' \ else desired_upstream @@ -1276,8 +1314,8 @@ def latest(name, target, rev=merge_rev, opts=merge_opts, - user=user - ) + user=user, + password=password) comments.append( 'Repository was fast-forwarded to {0}' .format(remote_loc) @@ -1295,8 +1333,8 @@ def latest(name, target, opts=['--hard', remote_rev if rev == 'HEAD' else rev], - user=user - ) + user=user, + password=password) comments.append( 'Repository was reset to {0} (fast-forward)' .format(rev) @@ -1311,6 +1349,7 @@ def latest(name, 'update', opts=['--init', '--recursive'], user=user, + password=password, identity=identity, saltenv=__env__) except CommandExecutionError as exc: @@ -1332,6 +1371,7 @@ def latest(name, force=force_fetch, refspecs=refspecs, user=user, + password=password, identity=identity, saltenv=__env__) except CommandExecutionError as exc: @@ -1349,6 +1389,7 @@ def latest(name, new_rev = __salt__['git.revision']( cwd=target, user=user, + password=password, ignore_retcode=True) except CommandExecutionError: new_rev = None @@ -1447,6 +1488,7 @@ def latest(name, __salt__['git.clone'](target, name, user=user, + password=password, opts=clone_opts, identity=identity, https_user=https_user, @@ -1483,7 +1525,7 @@ def latest(name, else: if remote_rev_type == 'tag' \ and rev not in __salt__['git.list_tags']( - target, user=user): + target, user=user, password=password): return _fail( ret, 'Revision \'{0}\' does not exist in clone' @@ -1493,8 +1535,10 @@ def latest(name, if branch is not None: if branch not in \ - __salt__['git.list_branches'](target, - user=user): + __salt__['git.list_branches']( + target, + user=user, + password=password): if rev == 'HEAD': checkout_rev = remote_rev else: @@ -1504,7 +1548,8 @@ def latest(name, __salt__['git.checkout'](target, checkout_rev, opts=['-b', branch], - user=user) + user=user, + password=password) comments.append( 'Branch \'{0}\' checked out, with {1} ' 'as a starting point'.format( @@ -1514,14 +1559,14 @@ def latest(name, ) local_rev, local_branch = \ - _get_local_rev_and_branch(target, user) + _get_local_rev_and_branch(target, user, password) if not _revs_equal(local_rev, remote_rev, remote_rev_type): __salt__['git.reset']( target, opts=['--hard', remote_rev], - user=user - ) + user=user, + password=password) comments.append( 'Repository was reset to {0}'.format(remote_loc) ) @@ -1532,6 +1577,7 @@ def latest(name, local_branch + '@{upstream}', opts=['--abbrev-ref'], user=user, + password=password, ignore_retcode=True) except CommandExecutionError: upstream = False @@ -1545,7 +1591,9 @@ def latest(name, branch_opts = _get_branch_opts( branch, local_branch, - __salt__['git.list_branches'](target, user=user), + __salt__['git.list_branches'](target, + user=user, + password=password), desired_upstream, git_ver) elif upstream and desired_upstream is False: @@ -1568,7 +1616,9 @@ def latest(name, branch_opts = _get_branch_opts( branch, local_branch, - __salt__['git.list_branches'](target, user=user), + __salt__['git.list_branches'](target, + user=user, + password=password), desired_upstream, git_ver) else: @@ -1578,7 +1628,8 @@ def latest(name, __salt__['git.branch']( target, opts=branch_opts, - user=user) + user=user, + password=password) comments.append(upstream_action) if submodules and remote_rev: @@ -1587,6 +1638,7 @@ def latest(name, 'update', opts=['--init', '--recursive'], user=user, + password=password, identity=identity) except CommandExecutionError as exc: return _failed_submodule_update(ret, exc, comments) @@ -1595,6 +1647,7 @@ def latest(name, new_rev = __salt__['git.revision']( cwd=target, user=user, + password=password, ignore_retcode=True) except CommandExecutionError: new_rev = None @@ -1624,7 +1677,8 @@ def present(name, template=None, separate_git_dir=None, shared=None, - user=None): + user=None, + password=None): ''' Ensure that a repository exists in the given directory @@ -1678,6 +1732,12 @@ def present(name, .. versionadded:: 0.17.0 + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + .. _`git-init(1)`: http://git-scm.com/docs/git-init .. _`worktree`: http://git-scm.com/docs/git-worktree ''' @@ -1689,7 +1749,7 @@ def present(name, return ret elif not bare and \ (os.path.isdir(os.path.join(name, '.git')) or - __salt__['git.is_worktree'](name)): + __salt__['git.is_worktree'](name, user=user, password=password)): return ret # Directory exists and is not a git repo, if force is set destroy the # directory and recreate, otherwise throw an error @@ -1747,7 +1807,8 @@ def present(name, template=template, separate_git_dir=separate_git_dir, shared=shared, - user=user) + user=user, + password=password) actions = [ 'Initialized {0}repository in {1}'.format( @@ -1773,6 +1834,7 @@ def detached(name, target=None, remote='origin', user=None, + password=None, force_clone=False, force_checkout=False, fetch_remote=True, @@ -1811,6 +1873,12 @@ def detached(name, User under which to run git commands. By default, commands are run by the user under which the minion is running. + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 + force_clone : False If the ``target`` directory exists and is not a git repository, then this state will fail. Set this argument to ``True`` to remove the @@ -1966,18 +2034,24 @@ def detached(name, local_commit_id = None gitdir = os.path.join(target, '.git') - if os.path.isdir(gitdir) or __salt__['git.is_worktree'](target): + if os.path.isdir(gitdir) \ + or __salt__['git.is_worktree'](target, user=user, password=password): # Target directory is a git repository or git worktree - local_commit_id = _get_local_rev_and_branch(target, user)[0] + local_commit_id = _get_local_rev_and_branch(target, user, password)[0] - if remote_ref_type is 'hash' and __salt__['git.describe'](target, ref): + if remote_ref_type is 'hash' \ + and __salt__['git.describe'](target, + ref, + user=user, + password=password): # The ref is a hash and it exists locally so skip to checkout hash_exists_locally = True else: # Check that remote is present and set to correct url remotes = __salt__['git.remotes'](target, user=user, + password=password, redact_auth=False) if remote in remotes and name in remotes[remote]['fetch']: @@ -2002,6 +2076,7 @@ def detached(name, url=name, remote=remote, user=user, + password=password, https_user=https_user, https_pass=https_pass) comments.append( @@ -2073,6 +2148,7 @@ def detached(name, __salt__['git.clone'](target, name, user=user, + password=password, opts=clone_opts, identity=identity, https_user=https_user, @@ -2119,6 +2195,7 @@ def detached(name, force=True, refspecs=refspecs, user=user, + password=password, identity=identity, saltenv=__env__) except CommandExecutionError as exc: @@ -2135,7 +2212,7 @@ def detached(name, #get refs and checkout checkout_commit_id = '' if remote_ref_type is 'hash': - if __salt__['git.describe'](target, ref): + if __salt__['git.describe'](target, ref, user=user, password=password): checkout_commit_id = ref else: return _fail( @@ -2147,6 +2224,7 @@ def detached(name, all_remote_refs = __salt__['git.remote_refs']( target, user=user, + password=password, identity=identity, https_user=https_user, https_pass=https_pass, @@ -2179,8 +2257,8 @@ def detached(name, __salt__['git.reset']( target, opts=['--hard', 'HEAD'], - user=user - ) + user=user, + password=password) comments.append( 'Repository was reset to HEAD before checking out ref' ) @@ -2202,7 +2280,8 @@ def detached(name, __salt__['git.checkout'](target, checkout_commit_id, force=force_checkout, - user=user) + user=user, + password=password) comments.append( 'Commit ID {0} was checked out at {1}'.format( checkout_commit_id, @@ -2214,6 +2293,7 @@ def detached(name, new_rev = __salt__['git.revision']( cwd=target, user=user, + password=password, ignore_retcode=True) except CommandExecutionError: new_rev = None @@ -2223,6 +2303,7 @@ def detached(name, 'update', opts=['--init', '--recursive'], user=user, + password=password, identity=identity) comments.append( 'Submodules were updated' @@ -2244,6 +2325,7 @@ def config_unset(name, value_regex=None, repo=None, user=None, + password=None, **kwargs): r''' .. versionadded:: 2015.8.0 @@ -2274,7 +2356,14 @@ def config_unset(name, set. Required unless ``global`` is set to ``True``. user - Optional name of a user as whom `git config` will be run + User under which to run git commands. By default, commands are run by + the user under which the minion is running. + + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 global : False If ``True``, this will set a global git config option @@ -2349,6 +2438,7 @@ def config_unset(name, key=key, value_regex=value_regex, user=user, + password=password, ignore_retcode=True, **{'global': global_} ) @@ -2397,6 +2487,7 @@ def config_unset(name, key=key, value_regex=None, user=user, + password=password, ignore_retcode=True, **{'global': global_} ) @@ -2412,6 +2503,7 @@ def config_unset(name, value_regex=value_regex, all=all_, user=user, + password=password, **{'global': global_} ) except CommandExecutionError as exc: @@ -2434,6 +2526,7 @@ def config_unset(name, key=key, value_regex=None, user=user, + password=password, ignore_retcode=True, **{'global': global_} ) @@ -2453,6 +2546,7 @@ def config_unset(name, key=key, value_regex=value_regex, user=user, + password=password, ignore_retcode=True, **{'global': global_} ) @@ -2474,6 +2568,7 @@ def config_set(name, multivar=None, repo=None, user=None, + password=None, **kwargs): ''' .. versionadded:: 2014.7.0 @@ -2504,7 +2599,14 @@ def config_set(name, set. Required unless ``global`` is set to ``True``. user - Optional name of a user as whom `git config` will be run + User under which to run git commands. By default, the commands are run + by the user under which the minion is running. + + password + Windows only. Required when specifying ``user``. This parameter will be + ignored on non-Windows platforms. + + .. versionadded:: 2016.3.4 global : False If ``True``, this will set a global git config option @@ -2614,6 +2716,7 @@ def config_set(name, cwd=repo, key=name, user=user, + password=password, ignore_retcode=True, **{'all': True, 'global': global_} ) @@ -2644,6 +2747,7 @@ def config_set(name, value=value, multivar=multivar, user=user, + password=password, **{'global': global_} ) except CommandExecutionError as exc: @@ -2679,7 +2783,13 @@ def config_set(name, return ret -def config(name, value=None, multivar=None, repo=None, user=None, **kwargs): +def config(name, + value=None, + multivar=None, + repo=None, + user=None, + password=None, + **kwargs): ''' Pass through to git.config_set and display a deprecation warning ''' @@ -2693,6 +2803,7 @@ def config(name, value=None, multivar=None, repo=None, user=None, **kwargs): multivar=multivar, repo=repo, user=user, + password=password, **kwargs) From de4f77cb687c11bd1dff99f013b24b3bcb50a51f Mon Sep 17 00:00:00 2001 From: rallytime Date: Wed, 7 Sep 2016 08:46:47 -0600 Subject: [PATCH 058/100] Fixup failing test: need to mock __utils__ instead of salt.utils.cloud call --- tests/unit/cloud/clouds/opennebula_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/cloud/clouds/opennebula_test.py b/tests/unit/cloud/clouds/opennebula_test.py index 0a0788912b..8c9473a4fe 100644 --- a/tests/unit/cloud/clouds/opennebula_test.py +++ b/tests/unit/cloud/clouds/opennebula_test.py @@ -20,6 +20,8 @@ from salt.exceptions import SaltCloudSystemExit, SaltCloudNotFound # Global Variables opennebula.__active_provider_name__ = '' opennebula.__opts__ = {} +opennebula.__utils__ = {} +opennebula.__utils__['cloud.cache_node'] = MagicMock() VM_NAME = 'my-vm' @@ -761,7 +763,6 @@ class OpenNebulaTestCase(TestCase): @patch('salt.cloud.clouds.opennebula._get_node', MagicMock(return_value={'my-vm': {'name': 'my-vm', 'id': 0}})) - @patch('salt.utils.cloud.cache_node', MagicMock()) def test_show_instance_success(self): ''' Tests that the node was found successfully. From 2e56527eada169d5b2ae2b9be8c6469a04a0d638 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Wed, 7 Sep 2016 10:36:13 -0500 Subject: [PATCH 059/100] Move command logging to before win_runas With the addition of runas for Windows in 2016.3.0, commands run using Windows runas would not be logged at all, as we would return from the _run() function before we got to the line where this information was logged. This commit moves the command logging up to before we enter the if block where the win_runas code would be executed, restoring the prior logging behavior. --- salt/modules/cmdmod.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/salt/modules/cmdmod.py b/salt/modules/cmdmod.py index ee590bde68..08eb4f941a 100644 --- a/salt/modules/cmdmod.py +++ b/salt/modules/cmdmod.py @@ -303,6 +303,22 @@ def _run(cmd, 'Setting value to an empty string'.format(bad_env_key)) env[bad_env_key] = '' + if _check_loglevel(output_loglevel) is not None: + # Always log the shell commands at INFO unless quiet logging is + # requested. The command output is what will be controlled by the + # 'loglevel' parameter. + msg = ( + 'Executing command {0}{1}{0} {2}in directory \'{3}\'{4}'.format( + '\'' if not isinstance(cmd, list) else '', + cmd, + 'as user \'{0}\' '.format(runas) if runas else '', + cwd, + '. Executing command in the background, no output will be ' + 'logged.' if bg else '' + ) + ) + log.info(log_callback(msg)) + if runas and salt.utils.is_windows(): if not password: msg = 'password is a required argument for runas on Windows' @@ -368,21 +384,6 @@ def _run(cmd, ) ) - if _check_loglevel(output_loglevel) is not None: - # Always log the shell commands at INFO unless quiet logging is - # requested. The command output is what will be controlled by the - # 'loglevel' parameter. - msg = ( - 'Executing command {0}{1}{0} {2}in directory \'{3}\'{4}'.format( - '\'' if not isinstance(cmd, list) else '', - cmd, - 'as user \'{0}\' '.format(runas) if runas else '', - cwd, - ' in the background, no output will be logged' if bg else '' - ) - ) - log.info(log_callback(msg)) - if reset_system_locale is True: if not salt.utils.is_windows(): # Default to C! From ee398a94b6046e7f33397c71aee0cdb10ce119ae Mon Sep 17 00:00:00 2001 From: Ch3LL Date: Wed, 7 Sep 2016 10:05:44 -0600 Subject: [PATCH 060/100] fix pylint --- tests/integration/runners/state.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/runners/state.py b/tests/integration/runners/state.py index d00ebc7446..1ab925ece9 100644 --- a/tests/integration/runners/state.py +++ b/tests/integration/runners/state.py @@ -88,6 +88,7 @@ class StateRunnerTest(integration.ShellCase): server_thread.join() + @skipIf(salt.utils.is_windows(), '*NIX-only test') class OrchEventTest(integration.ShellCase): ''' From 796156c5f5181876e90606651e21fe3def0e8318 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Wed, 7 Sep 2016 11:17:56 -0500 Subject: [PATCH 061/100] Add attribution --- salt/utils/win_runas.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/salt/utils/win_runas.py b/salt/utils/win_runas.py index ced3dbcc95..285cd4533a 100644 --- a/salt/utils/win_runas.py +++ b/salt/utils/win_runas.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- ''' -Implements the ability to run processes as another user in Windows for salt +Run processes as a different user in Windows + +Based on a solution from http://stackoverflow.com/questions/29566330 ''' from __future__ import absolute_import From b4112247a45b4fa5e8dbffd8d0f2ba706f8dc903 Mon Sep 17 00:00:00 2001 From: twangboy Date: Wed, 7 Sep 2016 12:01:38 -0600 Subject: [PATCH 062/100] Fix test not starting, skip broken functions --- tests/integration/modules/mac_system.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/integration/modules/mac_system.py b/tests/integration/modules/mac_system.py index 3d85cd248b..add68e6702 100644 --- a/tests/integration/modules/mac_system.py +++ b/tests/integration/modules/mac_system.py @@ -35,7 +35,7 @@ SET_SUBNET_NAME = __random_string() @skipIf(not salt.utils.is_darwin() or not salt.utils.which('systemsetup') - or salt.utils.get_uid(salt.utils.get_user() != 0), 'Test requirements not met') + or salt.utils.get_uid(salt.utils.get_user()) != 0, 'Test requirements not met') class MacSystemModuleTest(integration.ModuleCase): ''' Validate the mac_system module @@ -168,6 +168,7 @@ class MacSystemModuleTest(integration.ModuleCase): 'Invalid value passed for path.', self.run_function('system.set_startup_disk', ['spongebob'])) + @skipIf(True, 'Skip this test until mac fixes it.') def test_get_set_restart_delay(self): ''' Test system.get_restart_delay @@ -184,7 +185,7 @@ class MacSystemModuleTest(integration.ModuleCase): # Pass set bad value for seconds self.assertIn( 'Invalid value passed for seconds.', - self.run_funcdtion('system.set_restart_delay', [70])) + self.run_function('system.set_restart_delay', [70])) def test_get_set_disable_keyboard_on_lock(self): ''' @@ -226,6 +227,7 @@ class MacSystemModuleTest(integration.ModuleCase): self.run_function('system.set_disable_keyboard_on_lock', ['spongebob'])) + @skipIf(True, 'Skip this test until mac fixes it.') def test_get_set_boot_arch(self): ''' Test system.get_boot_arch From 07172cf371e1f53f3d478b9be4baa43850b74949 Mon Sep 17 00:00:00 2001 From: rallytime Date: Wed, 7 Sep 2016 14:13:59 -0600 Subject: [PATCH 063/100] Add some unit tests for the jid_queue functionality in minion.py Refs #35172 and #28785 --- tests/unit/minion_test.py | 79 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/tests/unit/minion_test.py b/tests/unit/minion_test.py index 4eed48a103..2b4af1b406 100644 --- a/tests/unit/minion_test.py +++ b/tests/unit/minion_test.py @@ -5,12 +5,13 @@ # Import python libs from __future__ import absolute_import +import copy import os # Import Salt Testing libs from salttesting import TestCase, skipIf from salttesting.helpers import ensure_in_syspath -from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch +from salttesting.mock import NO_MOCK, NO_MOCK_REASON, patch, MagicMock # Import salt libs from salt import minion @@ -52,6 +53,82 @@ class MinionTestCase(TestCase): result = False self.assertTrue(result) + # Tests for _handle_decoded_payload in the salt.minion.Minion() class: 3 + + def test_handle_decoded_payload_jid_match_in_jid_queue(self): + ''' + Tests that the _handle_decoded_payload function returns when a jid is given that is already present + in the jid_queue. + + Note: This test doesn't contain all of the patch decorators above the function like the other tests + for _handle_decoded_payload below. This is essential to this test as the call to the function must + return None BEFORE any of the processes are spun up because we should be avoiding firing duplicate + jobs. + ''' + mock_opts = {'cachedir': '', + 'extension_modules': ''} + mock_data = {'fun': 'foo.bar', + 'jid': 123} + mock_jid_queue = [123] + minion = salt.minion.Minion(mock_opts, jid_queue=copy.copy(mock_jid_queue)) + ret = minion._handle_decoded_payload(mock_data) + self.assertEqual(minion.jid_queue, mock_jid_queue) + self.assertIsNone(ret) + + @patch('salt.minion.Minion.ctx', MagicMock(return_value={})) + @patch('salt.utils.process.SignalHandlingMultiprocessingProcess.start', MagicMock(return_value=True)) + @patch('salt.utils.process.SignalHandlingMultiprocessingProcess.join', MagicMock(return_value=True)) + def test_handle_decoded_payload_jid_queue_addition(self): + ''' + Tests that the _handle_decoded_payload function adds a jid to the minion's jid_queue when the new + jid isn't already present in the jid_queue. + ''' + mock_jid = 11111 + mock_opts = {'cachedir': '', + 'extension_modules': '', + 'minion_jid_queue_hwm': 100} + mock_data = {'fun': 'foo.bar', + 'jid': mock_jid} + mock_jid_queue = [123, 456] + minion = salt.minion.Minion(mock_opts, jid_queue=copy.copy(mock_jid_queue)) + + # Assert that the minion's jid_queue attribute matches the mock_jid_queue as a baseline + # This can help debug any test failures if the _handle_decoded_payload call fails. + self.assertEqual(minion.jid_queue, mock_jid_queue) + + # Call the _handle_decoded_payload function and update the mock_jid_queue to include the new + # mock_jid. The mock_jid should have been added to the jid_queue since the mock_jid wasn't + # previously included. The minion's jid_queue attribute and the mock_jid_queue should be equal. + minion._handle_decoded_payload(mock_data) + mock_jid_queue.append(mock_jid) + self.assertEqual(minion.jid_queue, mock_jid_queue) + + @patch('salt.minion.Minion.ctx', MagicMock(return_value={})) + @patch('salt.utils.process.SignalHandlingMultiprocessingProcess.start', MagicMock(return_value=True)) + @patch('salt.utils.process.SignalHandlingMultiprocessingProcess.join', MagicMock(return_value=True)) + def test_handle_decoded_payload_jid_queue_reduced_minion_jid_queue_hwm(self): + ''' + Tests that the _handle_decoded_payload function removes a jid from the minion's jid_queue when the + minion's jid_queue high water mark (minion_jid_queue_hwm) is hit. + ''' + mock_opts = {'cachedir': '', + 'extension_modules': '', + 'minion_jid_queue_hwm': 2} + mock_data = {'fun': 'foo.bar', + 'jid': 789} + mock_jid_queue = [123, 456] + minion = salt.minion.Minion(mock_opts, jid_queue=copy.copy(mock_jid_queue)) + + # Assert that the minion's jid_queue attribute matches the mock_jid_queue as a baseline + # This can help debug any test failures if the _handle_decoded_payload call fails. + self.assertEqual(minion.jid_queue, mock_jid_queue) + + # Call the _handle_decoded_payload function and check that the queue is smaller by one item + # and contains the new jid + minion._handle_decoded_payload(mock_data) + self.assertEqual(len(minion.jid_queue), 2) + self.assertEqual(minion.jid_queue, [456, 789]) + if __name__ == '__main__': from integration import run_tests From 7cdbc546f1b60fd1a1c3f6ec0699c377e57f3636 Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Wed, 7 Sep 2016 16:01:34 -0600 Subject: [PATCH 064/100] Back-port #36062 to 2015.8 (#36118) * Comment all lines in the minion config file * Add unit tests to check for all files in conf/ to be commented out * Make sure cont/cloud.profiles file is commented out Matches the cloud.providers file format and other sample config files. * Add unit tests for sample files in cloud.profiles.d, cloud.providers.d, and cloud.maps.d * Make sure all sample config files in cloud.maps.d are commented out * Make sure all sample config files in cloud.profiles.d are commented out --- conf/cloud.maps.d/cloud.map | 25 ++- conf/cloud.profiles | 46 +++-- conf/cloud.profiles.d/EC2-us-east-1.profiles | 181 +++++++++-------- conf/cloud.profiles.d/EC2-us-west-1.profiles | 163 ++++++++-------- conf/cloud.profiles.d/EC2-us-west-2.profiles | 181 +++++++++-------- conf/minion | 12 +- tests/unit/conf_test.py | 194 +++++++++++++++++++ 7 files changed, 503 insertions(+), 299 deletions(-) create mode 100644 tests/unit/conf_test.py diff --git a/conf/cloud.maps.d/cloud.map b/conf/cloud.maps.d/cloud.map index bfbe52de69..4cf8e715dd 100644 --- a/conf/cloud.maps.d/cloud.map +++ b/conf/cloud.maps.d/cloud.map @@ -4,17 +4,16 @@ # Because the location to this file must be explicitly declared when using it, # its actual location on disk is up to the user. +#fedora_rs: +# - fedora1 +# - fedora2 +# - fedora3 +# - fedora4 +# - fedora5 -fedora_rs: - - fedora1 - - fedora2 - - fedora3 - - fedora4 - - fedora5 - -ubuntu_rs: - - ubuntu1 - - ubuntu2 - - ubuntu3 - - ubuntu4 - - ubuntu5 +#ubuntu_rs: +# - ubuntu1 +# - ubuntu2 +# - ubuntu3 +# - ubuntu4 +# - ubuntu5 diff --git a/conf/cloud.profiles b/conf/cloud.profiles index d53aa58d50..0d02d5d2c4 100644 --- a/conf/cloud.profiles +++ b/conf/cloud.profiles @@ -1,23 +1,27 @@ -base_ec2: - provider: my-ec2-config - image: ami-e565ba8c - size: t1.micro - script: python-bootstrap - minion: - cheese: edam +# This file may be used in addition to, or instead of, the files in the +# cloud.profiles.d/ directory. The format for this file, and all files in that +# directory, is identical. -ubuntu_rs: - provider: my-openstack-rackspace-config - image: Ubuntu 12.04 LTS - size: 256 server - script: Ubuntu - minion: - cheese: edam +#base_ec2: +# provider: my-ec2-config +# image: ami-e565ba8c +# size: t1.micro +# script: python-bootstrap +# minion: +# cheese: edam -fedora_rs: - provider: my-openstack-rackspace-config - image: Fedora 17 - size: 256 server - script: Fedora - minion: - cheese: edam +#ubuntu_rs: +# provider: my-openstack-rackspace-config +# image: Ubuntu 12.04 LTS +# size: 256 server +# script: Ubuntu +# minion: +# cheese: edam + +#fedora_rs: +# provider: my-openstack-rackspace-config +# image: Fedora 17 +# size: 256 server +# script: Fedora +# minion: +# cheese: edam diff --git a/conf/cloud.profiles.d/EC2-us-east-1.profiles b/conf/cloud.profiles.d/EC2-us-east-1.profiles index c348543cf0..c11dbdb673 100644 --- a/conf/cloud.profiles.d/EC2-us-east-1.profiles +++ b/conf/cloud.profiles.d/EC2-us-east-1.profiles @@ -2,115 +2,114 @@ # Arch Linux # https://wiki.archlinux.org/index.php/Arch_Linux_AMIs_for_Amazon_Web_Services -arch_ec2: - provider: my-ec2-config - image: ami-6ee95107 - size: t1.micro - ssh_username: root - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#arch_ec2: +# provider: my-ec2-config +# image: ami-6ee95107 +# size: t1.micro +# ssh_username: root +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 -arch_cloud-init_ec2: - provider: my-ec2-config - image: ami-596de730 - size: t1.micro - ssh_username: root - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#arch_cloud-init_ec2: +# provider: my-ec2-config +# image: ami-596de730 +# size: t1.micro +# ssh_username: root +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 # Centos 6, available from ec2 marketplace for no-charge # http://wiki.centos.org/Cloud/AWS -centos_6: - provider: my-ec2-config - image: ami-86e15bef - size: t1.micro - ssh_username: root - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#centos_6: +# provider: my-ec2-config +# image: ami-86e15bef +# size: t1.micro +# ssh_username: root +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 # official Debian, available at no-charge from ec2 marketplace: # http://wiki.debian.org/Cloud/AmazonEC2Image -debian_squeeze_ec2: - provider: my-ec2-config - image: ami-a121a6c8 - size: t1.micro - ssh_username: admin - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#debian_squeeze_ec2: +# provider: my-ec2-config +# image: ami-a121a6c8 +# size: t1.micro +# ssh_username: admin +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 # Fedora project cloud images # https://fedoraproject.org/wiki/Cloud_images -fedora_17_ec2: - provider: my-ec2-config - image: ami-2ea50247 - size: t1.micro - ssh_username: ec2-user - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#fedora_17_ec2: +# provider: my-ec2-config +# image: ami-2ea50247 +# size: t1.micro +# ssh_username: ec2-user +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 -fedora_18_ec2: - provider: my-ec2-config - image: ami-6145cc08 - size: t1.micro - ssh_username: ec2-user - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#fedora_18_ec2: +# provider: my-ec2-config +# image: ami-6145cc08 +# size: t1.micro +# ssh_username: ec2-user +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 # FreeBSD 9.1 # http://www.daemonology.net/freebsd-on-ec2/ # this t1.micro instance does not auto-populate SSH keys see above link -freebsd_91_ec2: - provider: my-ec2-config - image: ami-5339bb3a - size: t1.micro - ssh_username: ec2-user - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#freebsd_91_ec2: +# provider: my-ec2-config +# image: ami-5339bb3a +# size: t1.micro +# ssh_username: ec2-user +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 -freebsd_91_4XL_ec2: - provider: my-ec2-config - image: ami-79088510 - size: Cluster Compute 4XL - ssh_username: ec2-user - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#freebsd_91_4XL_ec2: +# provider: my-ec2-config +# image: ami-79088510 +# size: Cluster Compute 4XL +# ssh_username: ec2-user +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 # Canonical Ubuntu LTS images # http://cloud-images.ubuntu.com/releases/ -ubuntu_lucid_ec2: - provider: my-ec2-config - image: ami-21e47148 - size: t1.micro - ssh_username: ubuntu - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 - -ubuntu_precise_ec2: - provider: my-ec2-config - image: ami-0145d268 - size: t1.micro - ssh_username: ubuntu - location: us-east-1 - minion: - grains: - cloud: ec2-us-east-1 +#ubuntu_lucid_ec2: +# provider: my-ec2-config +# image: ami-21e47148 +# size: t1.micro +# ssh_username: ubuntu +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 +#ubuntu_precise_ec2: +# provider: my-ec2-config +# image: ami-0145d268 +# size: t1.micro +# ssh_username: ubuntu +# location: us-east-1 +# minion: +# grains: +# cloud: ec2-us-east-1 diff --git a/conf/cloud.profiles.d/EC2-us-west-1.profiles b/conf/cloud.profiles.d/EC2-us-west-1.profiles index fc29501e3a..f4c055d3cd 100644 --- a/conf/cloud.profiles.d/EC2-us-west-1.profiles +++ b/conf/cloud.profiles.d/EC2-us-west-1.profiles @@ -2,105 +2,104 @@ # Arch Linux # https://wiki.archlinux.org/index.php/Arch_Linux_AMIs_for_Amazon_Web_Services -arch_ec2: - provider: my-ec2-config - image: ami-337d5b76 - size: t1.micro - ssh_username: root - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#arch_ec2: +# provider: my-ec2-config +# image: ami-337d5b76 +# size: t1.micro +# ssh_username: root +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 -arch_cloud-init_ec2: - provider: my-ec2-config - image: ami-6a5f7c2f - size: t1.micro - ssh_username: root - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#arch_cloud-init_ec2: +# provider: my-ec2-config +# image: ami-6a5f7c2f +# size: t1.micro +# ssh_username: root +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 # Centos 6, available from ec2 marketplace for no-charge # http://wiki.centos.org/Cloud/AWS -centos_6: - provider: my-ec2-config - image: ami-f61630b3 - size: t1.micro - ssh_username: root - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#centos_6: +# provider: my-ec2-config +# image: ami-f61630b3 +# size: t1.micro +# ssh_username: root +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 # official Debian, available at no-charge from ec2 marketplace: # http://wiki.debian.org/Cloud/AmazonEC2Image -debian_squeeze_ec2: - provider: my-ec2-config - image: ami-2c735269 - size: t1.micro - ssh_username: admin - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#debian_squeeze_ec2: +# provider: my-ec2-config +# image: ami-2c735269 +# size: t1.micro +# ssh_username: admin +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 # Fedora project cloud images # https://fedoraproject.org/wiki/Cloud_images -fedora_17_ec2: - provider: my-ec2-config - image: ami-877e24c2 - size: t1.micro - ssh_username: ec2-user - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#fedora_17_ec2: +# provider: my-ec2-config +# image: ami-877e24c2 +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 -fedora_18_ec2: - provider: my-ec2-config - image: ami-0899b94d - size: t1.micro - ssh_username: ec2-user - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#fedora_18_ec2: +# provider: my-ec2-config +# image: ami-0899b94d +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 # FreeBSD 9.1 # http://www.daemonology.net/freebsd-on-ec2/ # this t1.micro instance does not auto-populate SSH keys see above link -freebsd_91_ec2: - provider: my-ec2-config - image: ami-4c8baa09 - size: t1.micro - ssh_username: ec2-user - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#freebsd_91_ec2: +# provider: my-ec2-config +# image: ami-4c8baa09 +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 # Canonical Ubuntu LTS images # http://cloud-images.ubuntu.com/releases/ -ubuntu_lucid_ec2: - provider: my-ec2-config - image: ami-e63013a3 - size: t1.micro - ssh_username: ubuntu - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 - -ubuntu_precise_ec2: - provider: my-ec2-config - image: ami-3ed8fb7b - size: t1.micro - ssh_username: ubuntu - location: us-west-1 - minion: - grains: - cloud: ec2-us-west-1 +#ubuntu_lucid_ec2: +# provider: my-ec2-config +# image: ami-e63013a3 +# size: t1.micro +# ssh_username: ubuntu +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 +#ubuntu_precise_ec2: +# provider: my-ec2-config +# image: ami-3ed8fb7b +# size: t1.micro +# ssh_username: ubuntu +# location: us-west-1 +# minion: +# grains: +# cloud: ec2-us-west-1 diff --git a/conf/cloud.profiles.d/EC2-us-west-2.profiles b/conf/cloud.profiles.d/EC2-us-west-2.profiles index e7d7ba8ce6..f5520eddb2 100644 --- a/conf/cloud.profiles.d/EC2-us-west-2.profiles +++ b/conf/cloud.profiles.d/EC2-us-west-2.profiles @@ -2,115 +2,114 @@ # Arch Linux # https://wiki.archlinux.org/index.php/Arch_Linux_AMIs_for_Amazon_Web_Services -arch_ec2: - provider: my-ec2-config - image: ami-bcf77e8c - size: t1.micro - ssh_username: root - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#arch_ec2: +# provider: my-ec2-config +# image: ami-bcf77e8c +# size: t1.micro +# ssh_username: root +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 -arch_cloud-init_ec2: - provider: my-ec2-config - image: ami-6a5f7c2f - size: t1.micro - ssh_username: root - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#arch_cloud-init_ec2: +# provider: my-ec2-config +# image: ami-6a5f7c2f +# size: t1.micro +# ssh_username: root +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 # Centos 6, available from ec2 marketplace for no-charge # http://wiki.centos.org/Cloud/AWS -centos_6: - provider: my-ec2-config - image: ami-de5bd2ee - size: t1.micro - ssh_username: root - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#centos_6: +# provider: my-ec2-config +# image: ami-de5bd2ee +# size: t1.micro +# ssh_username: root +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 # official Debian, available at no-charge from ec2 marketplace: # http://wiki.debian.org/Cloud/AmazonEC2Image -debian_squeeze_ec2: - provider: my-ec2-config - image: ami-e4da52d4 - size: t1.micro - ssh_username: admin - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#debian_squeeze_ec2: +# provider: my-ec2-config +# image: ami-e4da52d4 +# size: t1.micro +# ssh_username: admin +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 # Fedora project cloud images # https://fedoraproject.org/wiki/Cloud_images -fedora_17_ec2: - provider: my-ec2-config - image: ami-8e69e5be - size: t1.micro - ssh_username: ec2-user - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#fedora_17_ec2: +# provider: my-ec2-config +# image: ami-8e69e5be +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 -fedora_18_ec2: - provider: my-ec2-config - image: ami-0266ed32 - size: t1.micro - ssh_username: ec2-user - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#fedora_18_ec2: +# provider: my-ec2-config +# image: ami-0266ed32 +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 # FreeBSD 9.1 # http://www.daemonology.net/freebsd-on-ec2/ # this t1.micro instance does not auto-populate SSH keys see above link -freebsd_91_ec2: - provider: my-ec2-config - image: ami-aa09819a - size: t1.micro - ssh_username: ec2-user - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#freebsd_91_ec2: +# provider: my-ec2-config +# image: ami-aa09819a +# size: t1.micro +# ssh_username: ec2-user +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 -freebsd_91_4XL_ec2: - provider: my-ec2-config - image: ami-66169e56 - size: Cluster Compute 4XL - ssh_username: ec2-user - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#freebsd_91_4XL_ec2: +# provider: my-ec2-config +# image: ami-66169e56 +# size: Cluster Compute 4XL +# ssh_username: ec2-user +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 # Canonical Ubuntu LTS images # http://cloud-images.ubuntu.com/releases/ -ubuntu_lucid_ec2: - provider: my-ec2-config - image: ami-6ec8425e - size: t1.micro - ssh_username: ubuntu - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 - -ubuntu_precise_ec2: - provider: my-ec2-config - image: ami-e0941ed0 - size: t1.micro - ssh_username: ubuntu - location: us-west-2 - minion: - grains: - cloud: ec2-us-west-2 +#ubuntu_lucid_ec2: +# provider: my-ec2-config +# image: ami-6ec8425e +# size: t1.micro +# ssh_username: ubuntu +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 +#ubuntu_precise_ec2: +# provider: my-ec2-config +# image: ami-e0941ed0 +# size: t1.micro +# ssh_username: ubuntu +# location: us-west-2 +# minion: +# grains: +# cloud: ec2-us-west-2 diff --git a/conf/minion b/conf/minion index 72d8b70e4c..43b0df7807 100644 --- a/conf/minion +++ b/conf/minion @@ -353,7 +353,17 @@ # access the master has to the minion. #disable_modules: [cmd,test] #disable_returners: [] -# + +# This is the reverse of disable_modules. The default, like disable_modules, is the empty list, +# but if this option is set to *anything* then *only* those modules will load. +# Note that this is a very large hammer and it can be quite difficult to keep the minion working +# the way you think it should since Salt uses many modules internally itself. At a bare minimum +# you need the following enabled or else the minion won't start. +#whitelist_modules: +# - cmdmod +# - test +# - config + # Modules can be loaded from arbitrary paths. This enables the easy deployment # of third party modules. Modules for returners and minions can be loaded. # Specify a list of extra directories to search for minion modules and diff --git a/tests/unit/conf_test.py b/tests/unit/conf_test.py new file mode 100644 index 0000000000..cce0023423 --- /dev/null +++ b/tests/unit/conf_test.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +''' +Unit tests for the files in the salt/conf directory. +''' + +# Import Python libs +from __future__ import absolute_import +import os + +# Import Salt Testing libs +from salttesting import skipIf, TestCase +from salttesting.helpers import ensure_in_syspath +from salttesting.mock import ( + NO_MOCK, + NO_MOCK_REASON, +) + +ensure_in_syspath('../') + +# Import Salt libs +import salt.config + +SAMPLE_CONF_DIR = os.path.dirname(os.path.realpath(__file__)).split('tests')[0] + 'conf/' + + +@skipIf(NO_MOCK, NO_MOCK_REASON) +class ConfTest(TestCase): + ''' + Validate files in the salt/conf directory. + ''' + + def test_conf_master_sample_is_commented(self): + ''' + The sample config file located in salt/conf/master must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + master_config = SAMPLE_CONF_DIR + 'master' + ret = salt.config._read_conf_file(master_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + master_config + ) + ) + + def test_conf_minion_sample_is_commented(self): + ''' + The sample config file located in salt/conf/minion must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + minion_config = SAMPLE_CONF_DIR + 'minion' + ret = salt.config._read_conf_file(minion_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + minion_config + ) + ) + + def test_conf_cloud_sample_is_commented(self): + ''' + The sample config file located in salt/conf/cloud must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + cloud_config = SAMPLE_CONF_DIR + 'cloud' + ret = salt.config._read_conf_file(cloud_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + cloud_config + ) + ) + + def test_conf_cloud_profiles_sample_is_commented(self): + ''' + The sample config file located in salt/conf/cloud.profiles must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + cloud_profiles_config = SAMPLE_CONF_DIR + 'cloud.profiles' + ret = salt.config._read_conf_file(cloud_profiles_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + cloud_profiles_config + ) + ) + + def test_conf_cloud_providers_sample_is_commented(self): + ''' + The sample config file located in salt/conf/cloud.providers must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + cloud_providers_config = SAMPLE_CONF_DIR + 'cloud.providers' + ret = salt.config._read_conf_file(cloud_providers_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + cloud_providers_config + ) + ) + + def test_conf_proxy_sample_is_commented(self): + ''' + The sample config file located in salt/conf/proxy must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + proxy_config = SAMPLE_CONF_DIR + 'proxy' + ret = salt.config._read_conf_file(proxy_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + proxy_config + ) + ) + + def test_conf_roster_sample_is_commented(self): + ''' + The sample config file located in salt/conf/roster must be completely + commented out. This test checks for any lines that are not commented or blank. + ''' + roster_config = SAMPLE_CONF_DIR + 'roster' + ret = salt.config._read_conf_file(roster_config) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + roster_config + ) + ) + + def test_conf_cloud_profiles_d_files_are_commented(self): + ''' + All cloud profile sample configs in salt/conf/cloud.profiles.d/* must be completely + commented out. This test loops through all of the files in that directory to check + for any lines that are not commented or blank. + ''' + cloud_sample_files = os.listdir(SAMPLE_CONF_DIR + 'cloud.profiles.d/') + for conf_file in cloud_sample_files: + profile_conf = SAMPLE_CONF_DIR + 'cloud.profiles.d/' + conf_file + ret = salt.config._read_conf_file(profile_conf) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + conf_file + ) + ) + + def test_conf_cloud_providers_d_files_are_commented(self): + ''' + All cloud profile sample configs in salt/conf/cloud.providers.d/* must be completely + commented out. This test loops through all of the files in that directory to check + for any lines that are not commented or blank. + ''' + cloud_sample_files = os.listdir(SAMPLE_CONF_DIR + 'cloud.providers.d/') + for conf_file in cloud_sample_files: + provider_conf = SAMPLE_CONF_DIR + 'cloud.providers.d/' + conf_file + ret = salt.config._read_conf_file(provider_conf) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + conf_file + ) + ) + + def test_conf_cloud_maps_d_files_are_commented(self): + ''' + All cloud profile sample configs in salt/conf/cloud.maps.d/* must be completely + commented out. This test loops through all of the files in that directory to check + for any lines that are not commented or blank. + ''' + cloud_sample_files = os.listdir(SAMPLE_CONF_DIR + 'cloud.maps.d/') + for conf_file in cloud_sample_files: + map_conf = SAMPLE_CONF_DIR + 'cloud.maps.d/' + conf_file + ret = salt.config._read_conf_file(map_conf) + self.assertEqual( + ret, + {}, + 'Sample config file \'{0}\' must be commented out.'.format( + conf_file + ) + ) + + +if __name__ == '__main__': + from integration import run_tests + run_tests(ConfTest, needs_daemon=False) From d0bfbe88200a1ebff26c96d8d7fd7aefef5afba7 Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Wed, 7 Sep 2016 16:02:45 -0600 Subject: [PATCH 065/100] Fix Windows salt-master (#36119) The salt-master on Windows was broken by PR #35703 due to the change in how the ReqServer object was started in a new process. The new way failed to correctly pickle/unpickle the args/kwargs. To fix this, we use `__setstate__` and `__getstate__` similar to how other objects in the same file handle it (such as the `Maintenance` object). Signed-off-by: Sergey Kizunov --- salt/master.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/salt/master.py b/salt/master.py index d6f045f351..45d807fbf0 100644 --- a/salt/master.py +++ b/salt/master.py @@ -683,6 +683,21 @@ class ReqServer(SignalHandlingMultiprocessingProcess): self.key = key self.secrets = secrets + # __setstate__ and __getstate__ are only used on Windows. + # We do this so that __init__ will be invoked on Windows in the child + # process so that a register_after_fork() equivalent will work on Windows. + def __setstate__(self, state): + self._is_child = True + self.__init__(state['opts'], state['key'], state['mkey'], + log_queue=state['log_queue'], secrets=state['secrets']) + + def __getstate__(self): + return {'opts': self.opts, + 'key': self.key, + 'mkey': self.master_key, + 'log_queue': self.log_queue, + 'secrets': self.secrets} + def _handle_signals(self, signum, sigframe): # pylint: disable=unused-argument self.destroy(signum) super(ReqServer, self)._handle_signals(signum, sigframe) From 640f0c17c66884f29cc178436e8042b7e5ddead9 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Wed, 7 Sep 2016 17:12:05 -0500 Subject: [PATCH 066/100] pygit2: Prevent traceback on initial gitfs setup Newer pygit2 releases require that this value be a string, they will not accept a bool here. --- salt/utils/gitfs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/utils/gitfs.py b/salt/utils/gitfs.py index e19c76dcbc..01541b8c4e 100644 --- a/salt/utils/gitfs.py +++ b/salt/utils/gitfs.py @@ -1213,7 +1213,7 @@ class Pygit2(GitProvider): self.repo.config.set_multivar( 'http.sslVerify', '', - self.ssl_verify + str(self.ssl_verify).lower() ) except os.error: # This exception occurs when two processes are trying to write From 277939e0c35199f00e1e5143bdca310f51998dbc Mon Sep 17 00:00:00 2001 From: David Boucha Date: Tue, 6 Sep 2016 15:54:59 -0600 Subject: [PATCH 067/100] don't stacktrace on import pwd on Windows --- salt/modules/snapper.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py index aa39984bff..805f33c598 100644 --- a/salt/modules/snapper.py +++ b/salt/modules/snapper.py @@ -17,7 +17,10 @@ import logging import os import time import difflib -from pwd import getpwuid +try: + from pwd import getpwuid +except ImportError: + pass from salt.exceptions import CommandExecutionError import salt.utils From 453e80c61131044756caa0461689c73b1642d64c Mon Sep 17 00:00:00 2001 From: David Boucha Date: Wed, 7 Sep 2016 12:03:10 -0600 Subject: [PATCH 068/100] Add check for pwd module to the __virtual__ func --- salt/modules/snapper.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py index 805f33c598..38f1aa231b 100644 --- a/salt/modules/snapper.py +++ b/salt/modules/snapper.py @@ -19,8 +19,9 @@ import time import difflib try: from pwd import getpwuid + HAS_PWD = True except ImportError: - pass + HAS_PWD = False from salt.exceptions import CommandExecutionError import salt.utils @@ -85,6 +86,8 @@ def __virtual__(): return False, error_msg.format(snapper_error) elif not bus: return False, error_msg.format(system_bus_error) + elif not HAS_PWD: + return False, error_msg.format('pwd module not available') return 'snapper' From 64a576fc6eb4dc8c781e5ab0be0f05cea6ed9397 Mon Sep 17 00:00:00 2001 From: David Boucha Date: Wed, 7 Sep 2016 17:25:15 -0600 Subject: [PATCH 069/100] add windows core grain test --- tests/integration/grains/core.py | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/integration/grains/core.py diff --git a/tests/integration/grains/core.py b/tests/integration/grains/core.py new file mode 100644 index 0000000000..fa12ed327e --- /dev/null +++ b/tests/integration/grains/core.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +''' +Test the core grains +''' + +# Import python libs +from __future__ import absolute_import + +# Import Salt Testing libs +from salttesting import skipIf +from salttesting.helpers import ensure_in_syspath + +ensure_in_syspath('../../') + +# Import salt libs +import integration +import salt.utils +if salt.utils.is_windows(): + try: + import salt.modules.reg + except: + pass + + +class TestGrainsCore(integration.ModuleCase): + ''' + Test the core grains grains + ''' + @skipIf(not salt.utils.is_windows(), 'Only run on Windows') + def test_win_cpu_model(self): + ''' + test grains['cpu_model'] + ''' + opts = self.minion_opts + cpu_model_text = salt.modules.reg.read_value( + "HKEY_LOCAL_MACHINE", + "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", + "ProcessorNameString").get('vdata') + self.assertEqual( + self.run_function('grains.items')['cpu_model'], + cpu_model_text + ) From 035a212a9b415c7a322d12403f1cec2c3f77fd79 Mon Sep 17 00:00:00 2001 From: Jonathan Ballet Date: Thu, 8 Sep 2016 11:53:34 +0200 Subject: [PATCH 070/100] doc: fix doc formatting for salt.states.mount --- salt/states/mount.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/salt/states/mount.py b/salt/states/mount.py index 8eb9a45184..cd8235a80f 100644 --- a/salt/states/mount.py +++ b/salt/states/mount.py @@ -103,28 +103,38 @@ def mounted(name, device otherwise. extra_mount_invisible_options - A list of extra options that are not visible through the /proc/self/mountinfo - interface. If a option is not visible through this interface it will always - remount the device. This Option extends the builtin mount_invisible_options list. + A list of extra options that are not visible through the + ``/proc/self/mountinfo`` interface. + + If a option is not visible through this interface it will always remount + the device. This option extends the builtin ``mount_invisible_options`` + list. extra_mount_invisible_keys - A list of extra key options that are not visible through the /proc/self/mountinfo - interface. If a key option is not visible through this interface it will always - remount the device. This Option extends the builtin mount_invisible_keys list. - A good example for a key Option is the password Option: + A list of extra key options that are not visible through the + ``/proc/self/mountinfo`` interface. + + If a key option is not visible through this interface it will always + remount the device. This option extends the builtin + ``mount_invisible_keys`` list. + + A good example for a key option is the password option:: + password=badsecret extra_ignore_fs_keys A dict of filesystem options which should not force a remount. This will update - the internal dictionary. The dict should look like this: + the internal dictionary. The dict should look like this:: + { 'ramfs': ['size'] } extra_mount_translate_options A dict of mount options that gets translated when mounted. To prevent a remount - add additional Options to the default dictionary. This will update the internal - dictionary. The dictionary should look like this: + add additional options to the default dictionary. This will update the internal + dictionary. The dictionary should look like this:: + { 'tcp': 'proto=tcp', 'udp': 'proto=udp' From d1b9a4061e37739da21bf35e3ac3feae003ecabe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Tue, 6 Sep 2016 11:21:05 +0100 Subject: [PATCH 071/100] Fixing skipped boto tests to prevent errors if boto3 does not exists. --- tests/unit/modules/boto_vpc_test.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/unit/modules/boto_vpc_test.py b/tests/unit/modules/boto_vpc_test.py index e7e09faf57..cbf36a892d 100644 --- a/tests/unit/modules/boto_vpc_test.py +++ b/tests/unit/modules/boto_vpc_test.py @@ -118,6 +118,13 @@ def _has_required_moto(): return True +@skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(HAS_BOTO is False, 'The boto module must be installed.') +@skipIf(HAS_MOTO is False, 'The moto module must be installed.') +@skipIf(_has_required_boto() is False, 'The boto module must be greater than' + ' or equal to version {0}' + .format(required_boto_version)) +@skipIf(_has_required_moto() is False, 'The moto version must be >= to version {0}'.format(required_moto_version)) class BotoVpcTestCaseBase(TestCase): def setUp(self): boto_vpc.__context__ = {} @@ -230,13 +237,6 @@ class BotoVpcTestCaseMixin(object): return rtbl -@skipIf(NO_MOCK, NO_MOCK_REASON) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {0}' - .format(required_boto_version)) -@skipIf(_has_required_moto() is False, 'The moto version must be >= to version {0}'.format(required_moto_version)) class BotoVpcTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): ''' TestCase for salt.modules.boto_vpc module From 5977f1f54c86084c6827eca530e6fe6ad4c8055a Mon Sep 17 00:00:00 2001 From: Eric Radman Date: Wed, 20 Apr 2016 16:01:21 -0400 Subject: [PATCH 072/100] Skip utils_test if timelib is not installed (#32699) date_cast() throws a RuntimeError, not an ImportError --- tests/unit/utils/utils_test.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/unit/utils/utils_test.py b/tests/unit/utils/utils_test.py index 261af69b59..11f0bafc22 100644 --- a/tests/unit/utils/utils_test.py +++ b/tests/unit/utils/utils_test.py @@ -527,14 +527,9 @@ class UtilsTestCase(TestCase): ret = utils.date_cast('Mon Dec 23 10:19:15 MST 2013') expected_ret = datetime.datetime(2013, 12, 23, 10, 19, 15) self.assertEqual(ret, expected_ret) - except ImportError: - try: - ret = utils.date_cast('Mon Dec 23 10:19:15 MST 2013') - expected_ret = datetime.datetime(2013, 12, 23, 10, 19, 15) - self.assertEqual(ret, expected_ret) - except RuntimeError: - # Unparseable without timelib installed - self.skipTest('\'timelib\' is not installed') + except RuntimeError: + # Unparseable without timelib installed + self.skipTest('\'timelib\' is not installed') @skipIf(not HAS_TIMELIB, '\'timelib\' is not installed') def test_date_format(self): From 1fb6340fef861539efc181b31b773a05d3f10bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mihai=20Dinc=C4=83?= Date: Tue, 23 Aug 2016 18:49:53 +0200 Subject: [PATCH 073/100] Fix tests (#35693) * Fix tests/unit/modules/useradd_test.py::UserAddTestCase::test_info * Fix unit/pyobjects_test.py::MapTests::test_map * Fix tests/unit/pyobjects_test.py::RendererTests::test_extend * Fix tests/unit/pyobjects_test.py::RendererTests::test_requisite_implicit_list --- tests/unit/modules/useradd_test.py | 6 ++---- tests/unit/pyobjects_test.py | 11 +++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/unit/modules/useradd_test.py b/tests/unit/modules/useradd_test.py index 7e646b6823..cc9e610366 100644 --- a/tests/unit/modules/useradd_test.py +++ b/tests/unit/modules/useradd_test.py @@ -326,7 +326,7 @@ class UserAddTestCase(TestCase): ''' Test the user information ''' - self.assertEqual(useradd.info('salt'), {}) + self.assertEqual(useradd.info('username-that-doesnt-exist'), {}) mock = MagicMock(return_value=pwd.struct_passwd(('_TEST_GROUP', '*', @@ -336,9 +336,7 @@ class UserAddTestCase(TestCase): '/var/virusmails', '/usr/bin/false'))) with patch.object(pwd, 'getpwnam', mock): - mock = MagicMock(return_value='Group Name') - with patch.object(useradd, 'list_groups', mock): - self.assertEqual(useradd.info('salt')['name'], '_TEST_GROUP') + self.assertEqual(useradd.info('username-that-doesnt-exist')['name'], '_TEST_GROUP') # 'list_groups' function tests: 1 diff --git a/tests/unit/pyobjects_test.py b/tests/unit/pyobjects_test.py index f1c3e29ad3..3eb4bd56e0 100644 --- a/tests/unit/pyobjects_test.py +++ b/tests/unit/pyobjects_test.py @@ -54,10 +54,18 @@ include('http') extend_template = '''#!pyobjects include('http') + +from salt.utils.pyobjects import StateFactory +Service = StateFactory('service') + Service.running(extend('apache'), watch=[{'file': '/etc/file'}]) ''' map_template = '''#!pyobjects +from salt.utils.pyobjects import StateFactory +Service = StateFactory('service') + + class Samba(Map): __merge__ = 'samba:lookup' @@ -127,6 +135,9 @@ from salt://password.sls import password ''' requisite_implicit_list_template = '''#!pyobjects +from salt.utils.pyobjects import StateFactory +Service = StateFactory('service') + with Pkg.installed("pkg"): Service.running("service", watch=File("file"), require=Cmd("cmd")) ''' From b5ca02c867936c953ae6cc0275ce63fba124c724 Mon Sep 17 00:00:00 2001 From: Eric Radman Date: Tue, 12 Apr 2016 10:41:25 -0400 Subject: [PATCH 074/100] Fix tests that assert CommandExecutionError (#32485) Trying to assert that an exception was raised using helper_open.write.assertRaises() is bogus--there is no such method. Use standard unittest.assertRaises() instead. --- salt/modules/linux_sysctl.py | 7 +++++-- tests/unit/modules/linux_sysctl_test.py | 19 ++++++++++++------- tests/unit/modules/mount_test.py | 18 +++++++----------- tests/unit/modules/puppet_test.py | 14 +++++++++++--- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/salt/modules/linux_sysctl.py b/salt/modules/linux_sysctl.py index 90b3a8f470..b7b8bd9201 100644 --- a/salt/modules/linux_sysctl.py +++ b/salt/modules/linux_sysctl.py @@ -41,8 +41,11 @@ def _check_systemd_salt_config(): sysctl_dir = os.path.split(conf)[0] if not os.path.exists(sysctl_dir): os.makedirs(sysctl_dir) - with salt.utils.fopen(conf, 'w'): - pass + try: + salt.utils.fopen(conf, 'w').close() + except (IOError, OSError): + msg = 'Could not create file: {0}' + raise CommandExecutionError(msg.format(conf)) return conf diff --git a/tests/unit/modules/linux_sysctl_test.py b/tests/unit/modules/linux_sysctl_test.py index aeef75b1fc..eb8b7e2b82 100644 --- a/tests/unit/modules/linux_sysctl_test.py +++ b/tests/unit/modules/linux_sysctl_test.py @@ -84,17 +84,22 @@ class LinuxSysctlTestCase(TestCase): self.assertEqual(linux_sysctl.assign( 'net.ipv4.ip_forward', 1), ret) - @patch('os.path.isfile', MagicMock(return_value=False)) def test_persist_no_conf_failure(self): ''' Tests adding of config file failure ''' - with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - linux_sysctl.persist, - 'net.ipv4.ip_forward', - 1, config=None) + asn_cmd = {'pid': 1337, 'retcode': 0, + 'stderr': "sysctl: permission denied", 'stdout': ''} + mock_asn_cmd = MagicMock(return_value=asn_cmd) + cmd = "sysctl -w net.ipv4.ip_forward=1" + mock_cmd = MagicMock(return_value=cmd) + with patch.dict(linux_sysctl.__salt__, {'cmd.run_stdout': mock_cmd, + 'cmd.run_all': mock_asn_cmd}): + with patch('salt.utils.fopen', mock_open()) as m_open: + self.assertRaises(CommandExecutionError, + linux_sysctl.persist, + 'net.ipv4.ip_forward', + 1, config=None) @patch('os.path.isfile', MagicMock(return_value=False)) def test_persist_no_conf_success(self): diff --git a/tests/unit/modules/mount_test.py b/tests/unit/modules/mount_test.py index 282539d546..b5c3cb529b 100644 --- a/tests/unit/modules/mount_test.py +++ b/tests/unit/modules/mount_test.py @@ -102,13 +102,13 @@ class MountTestCase(TestCase): with patch.object(mount, 'fstab', mock): self.assertTrue(mount.rm_fstab('name', 'device')) - mock = MagicMock(return_value={'name': 'name'}) - with patch.object(mount, 'fstab', mock): + mock_fstab = MagicMock(return_value={'name': 'name'}) + with patch.object(mount, 'fstab', mock_fstab): with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - mount.rm_fstab, - config=None) + m_open.side_effect = IOError(13, 'Permission denied:', '/file') + self.assertRaises(CommandExecutionError, + mount.rm_fstab, + 'name', 'device') def test_set_fstab(self): ''' @@ -144,11 +144,7 @@ class MountTestCase(TestCase): mock = MagicMock(return_value={'name': 'name'}) with patch.object(mount, 'fstab', mock): - with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - mount.rm_automaster, - 'name', 'device') + self.assertTrue(mount.rm_automaster('name', 'device')) def test_set_automaster(self): ''' diff --git a/tests/unit/modules/puppet_test.py b/tests/unit/modules/puppet_test.py index 6a43fd4451..eb0bd81539 100644 --- a/tests/unit/modules/puppet_test.py +++ b/tests/unit/modules/puppet_test.py @@ -91,10 +91,12 @@ class PuppetTestCase(TestCase): with patch('salt.utils.fopen', mock_open()): self.assertTrue(puppet.disable()) + try: with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - puppet.disable) + m_open.side_effect = IOError(13, 'Permission denied:', '/file') + self.assertRaises(CommandExecutionError, puppet.disable) + except StopIteration: + pass def test_status(self): ''' @@ -154,10 +156,16 @@ class PuppetTestCase(TestCase): mock_open(read_data="resources: 1")): self.assertDictEqual(puppet.summary(), {'resources': 1}) +<<<<<<< HEAD with patch('salt.utils.fopen', mock_open()) as m_open: helper_open = m_open() helper_open.write.assertRaises(CommandExecutionError, puppet.summary) +======= + with patch('salt.utils.fopen', mock_open()) as m_open: + m_open.side_effect = IOError(13, 'Permission denied:', '/file') + self.assertRaises(CommandExecutionError, puppet.summary) +>>>>>>> 4b58bfc... Fix tests that assert CommandExecutionError (#32485) def test_plugin_sync(self): ''' From 305bab8be011b860ee9ca4bf838e12c3447788d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Wed, 7 Sep 2016 12:36:18 +0100 Subject: [PATCH 075/100] Fixed _interfaces_ifconfig output for SunOS test --- tests/unit/utils/network.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/utils/network.py b/tests/unit/utils/network.py index 89db8481c9..72ca85746b 100644 --- a/tests/unit/utils/network.py +++ b/tests/unit/utils/network.py @@ -150,16 +150,17 @@ class NetworkTestCase(TestCase): interfaces = network._interfaces_ifconfig(SOLARIS) self.assertEqual(interfaces, {'ilbext0': {'inet': [{'address': '10.10.11.11', + 'broadcast': '10.10.11.31', + 'netmask': '255.255.255.224'}, + {'address': '10.10.11.12', 'broadcast': '10.10.11.31', 'netmask': '255.255.255.224'}], - 'inet6': [{'address': '::', - 'prefixlen': '0'}], + 'inet6': [], 'up': True}, 'ilbint0': {'inet': [{'address': '10.6.0.11', 'broadcast': '10.6.0.255', 'netmask': '255.255.255.0'}], - 'inet6': [{'address': '::', - 'prefixlen': '0'}], + 'inet6': [], 'up': True}, 'lo0': {'inet': [{'address': '127.0.0.1', 'netmask': '255.0.0.0'}], @@ -174,8 +175,7 @@ class NetworkTestCase(TestCase): 'up': True}, 'vpn0': {'inet': [{'address': '10.6.0.14', 'netmask': '255.0.0.0'}], - 'inet6': [{'address': '::', - 'prefixlen': '0'}], + 'inet6': [], 'up': True}} ) From 2605f348491bb68af3c0c27de7d289930abb138a Mon Sep 17 00:00:00 2001 From: kstreee Date: Mon, 1 Aug 2016 09:44:57 +0000 Subject: [PATCH 076/100] Fix missing first data in stream when subscribing stream using a function 'read_async'. --- tests/unit/netapi/rest_tornado/test_utils.py | 27 ++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/unit/netapi/rest_tornado/test_utils.py b/tests/unit/netapi/rest_tornado/test_utils.py index 1dd18d5c5b..c22c288daf 100644 --- a/tests/unit/netapi/rest_tornado/test_utils.py +++ b/tests/unit/netapi/rest_tornado/test_utils.py @@ -103,7 +103,8 @@ class TestEventListener(AsyncTestCase): event_listener = saltnado.EventListener({}, # we don't use mod_opts, don't save? {'sock_dir': SOCK_DIR, 'transport': 'zeromq'}) - event_future = event_listener.get_event(1, 'evt1', self.stop) # get an event future + self._finished = False # fit to event_listener's behavior + event_future = event_listener.get_event(self, 'evt1', self.stop) # get an event future me.fire_event({'data': 'foo2'}, 'evt2') # fire an event we don't want me.fire_event({'data': 'foo1'}, 'evt1') # fire an event we do want self.wait() # wait for the future @@ -113,6 +114,27 @@ class TestEventListener(AsyncTestCase): self.assertEqual(event_future.result()['tag'], 'evt1') self.assertEqual(event_future.result()['data']['data'], 'foo1') + def test_set_event_handler(self): + ''' + Test subscribing events using set_event_handler + ''' + with eventpublisher_process(): + me = event.MasterEvent(SOCK_DIR) + event_listener = saltnado.EventListener({}, # we don't use mod_opts, don't save? + {'sock_dir': SOCK_DIR, + 'transport': 'zeromq'}) + self._finished = False # fit to event_listener's behavior + event_future = event_listener.get_event(self, + tag='evt', + callback=self.stop, + timeout=1, + ) # get an event future + me.fire_event({'data': 'foo'}, 'evt') # fire an event we do want + self.wait() + + # check that we subscribed the event we wanted + self.assertEqual(len(event_listener.timeout_map), 0) + def test_timeout(self): ''' Make sure timeouts work correctly @@ -121,7 +143,8 @@ class TestEventListener(AsyncTestCase): event_listener = saltnado.EventListener({}, # we don't use mod_opts, don't save? {'sock_dir': SOCK_DIR, 'transport': 'zeromq'}) - event_future = event_listener.get_event(1, + self._finished = False # fit to event_listener's behavior + event_future = event_listener.get_event(self, tag='evt1', callback=self.stop, timeout=1, From ec0cc943e0160808136d2264ee09f6e7ad019dab Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Mon, 14 Sep 2015 17:54:51 +0100 Subject: [PATCH 077/100] Make sure spm tests are picked up by runtests. Lists in py2 don't have the clear method --- tests/unit/{spm.py => spm_test.py} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename tests/unit/{spm.py => spm_test.py} (98%) diff --git a/tests/unit/spm.py b/tests/unit/spm_test.py similarity index 98% rename from tests/unit/spm.py rename to tests/unit/spm_test.py index d2c0bf7d0b..1adc3774c2 100644 --- a/tests/unit/spm.py +++ b/tests/unit/spm_test.py @@ -129,12 +129,12 @@ class SPMTest(TestCase): ('summary', 'Summary: {0}')): assert line.format(_F1['definition'][key]) in lines # Reinstall with force=False, should fail - self.ui._error.clear() + self.ui._error = [] self.client.run(['local', 'install', pkgpath]) assert len(self.ui._error) > 0 # Reinstall with force=True, should succeed __opts__['force'] = True - self.ui._error.clear() + self.ui._error = [] self.client.run(['local', 'install', pkgpath]) assert len(self.ui._error) == 0 __opts__['force'] = False @@ -167,7 +167,7 @@ class SPMTest(TestCase): ) for args in fail_args: - self.ui._error.clear() + self.ui._error = [] self.client.run(args) assert len(self.ui._error) > 0 From 4e9733ad6ddde976edbb5f582320d7bb6a8565df Mon Sep 17 00:00:00 2001 From: rallytime Date: Fri, 8 Jul 2016 13:26:08 -0600 Subject: [PATCH 078/100] Rename dockerio.py unit tests to dockerio_test.py These tests have never run automatically because of an incorrect file name. Added a skipIf on these tests as they are currently non-functioning and the module they're testing has been deprecated. --- tests/unit/states/{dockerio.py => dockerio_test.py} | 1 + 1 file changed, 1 insertion(+) rename tests/unit/states/{dockerio.py => dockerio_test.py} (98%) diff --git a/tests/unit/states/dockerio.py b/tests/unit/states/dockerio_test.py similarity index 98% rename from tests/unit/states/dockerio.py rename to tests/unit/states/dockerio_test.py index c73b633a41..54f51bea97 100644 --- a/tests/unit/states/dockerio.py +++ b/tests/unit/states/dockerio_test.py @@ -20,6 +20,7 @@ def provision_state(module, fixture): @skipIf(NO_MOCK, NO_MOCK_REASON) +@skipIf(True, 'Skipped: This module has been deprecated.') class DockerStateTestCase(TestCase): def test_docker_run_success(self): from salt.states import dockerio From 46e4bb58e55f294c867172b7665018b8d0e9a026 Mon Sep 17 00:00:00 2001 From: Dmitry Kuzmenko Date: Thu, 19 Nov 2015 18:42:53 +0300 Subject: [PATCH 079/100] Fixed LoadAuthTestCase --- tests/unit/auth_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/auth_test.py b/tests/unit/auth_test.py index b6bddb255d..c39a350a0a 100644 --- a/tests/unit/auth_test.py +++ b/tests/unit/auth_test.py @@ -48,7 +48,7 @@ class LoadAuthTestCase(TestCase): 'eauth': 'pam' }, expected_extra_kws=auth.AUTH_INTERNAL_KEYWORDS) ret = self.lauth.load_name(valid_eauth_load) - format_call_mock.assert_has_calls(expected_ret) + format_call_mock.assert_has_calls((expected_ret,)) def test_get_groups(self): valid_eauth_load = {'username': 'test_user', @@ -63,7 +63,7 @@ class LoadAuthTestCase(TestCase): 'eauth': 'pam' }, expected_extra_kws=auth.AUTH_INTERNAL_KEYWORDS) self.lauth.get_groups(valid_eauth_load) - format_call_mock.assert_has_calls(expected_ret) + format_call_mock.assert_has_calls((expected_ret,)) @patch('zmq.Context', MagicMock()) From 04b1a4a9caea68239b736b7a00626e5db41c8372 Mon Sep 17 00:00:00 2001 From: Dmitry Kuzmenko Date: Wed, 25 Nov 2015 15:31:20 +0300 Subject: [PATCH 080/100] Fixed use of assert_has_calls in tests. The method logic was changed in mock-1.1.0. This mades the use of the method compatible with both <1.1.0 and >=1.1.0 --- tests/unit/auth_test.py | 4 ++-- tests/unit/modules/cron_test.py | 2 +- tests/unit/modules/mysql_test.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/auth_test.py b/tests/unit/auth_test.py index c39a350a0a..d68cb25d51 100644 --- a/tests/unit/auth_test.py +++ b/tests/unit/auth_test.py @@ -48,7 +48,7 @@ class LoadAuthTestCase(TestCase): 'eauth': 'pam' }, expected_extra_kws=auth.AUTH_INTERNAL_KEYWORDS) ret = self.lauth.load_name(valid_eauth_load) - format_call_mock.assert_has_calls((expected_ret,)) + format_call_mock.assert_has_calls((expected_ret,), any_order=True) def test_get_groups(self): valid_eauth_load = {'username': 'test_user', @@ -63,7 +63,7 @@ class LoadAuthTestCase(TestCase): 'eauth': 'pam' }, expected_extra_kws=auth.AUTH_INTERNAL_KEYWORDS) self.lauth.get_groups(valid_eauth_load) - format_call_mock.assert_has_calls((expected_ret,)) + format_call_mock.assert_has_calls((expected_ret,), any_order=True) @patch('zmq.Context', MagicMock()) diff --git a/tests/unit/modules/cron_test.py b/tests/unit/modules/cron_test.py index 12314873a7..89c3e82a1a 100644 --- a/tests/unit/modules/cron_test.py +++ b/tests/unit/modules/cron_test.py @@ -577,7 +577,7 @@ class PsTestCase(TestCase): '# Lines below here are managed by Salt, do not edit\n', '@hourly echo Hi!\n']) ret = cron.set_special('DUMMY_USER', '@hourly', 'echo Hi!') - write_cron_lines_mock.assert_has_calls(expected_write_call) + write_cron_lines_mock.assert_has_calls((expected_write_call,), any_order=True) def test__get_cron_date_time(self): ret = cron._get_cron_date_time(minute=STUB_CRON_TIMESTAMP['minute'], diff --git a/tests/unit/modules/mysql_test.py b/tests/unit/modules/mysql_test.py index 8cd3ecaebd..85b71ba0e4 100644 --- a/tests/unit/modules/mysql_test.py +++ b/tests/unit/modules/mysql_test.py @@ -293,10 +293,10 @@ class MySQLTestCase(TestCase): with patch.dict(mysql.__salt__, {'config.option': MagicMock()}): function(*args, **kwargs) if isinstance(expected_sql, dict): - calls = (call().cursor().execute('{0}'.format(expected_sql['sql']), expected_sql['sql_args'])) + calls = call().cursor().execute('{0}'.format(expected_sql['sql']), expected_sql['sql_args']) else: - calls = (call().cursor().execute('{0}'.format(expected_sql))) - connect_mock.assert_has_calls(calls) + calls = call().cursor().execute('{0}'.format(expected_sql)) + connect_mock.assert_has_calls((calls,), True) if __name__ == '__main__': From 63ff7310098a550c9ea1d481ae33e175244a9103 Mon Sep 17 00:00:00 2001 From: twangboy Date: Fri, 22 Jan 2016 17:32:42 -0700 Subject: [PATCH 081/100] Fixed tests --- .../unit/modules/{darwin_sysctl_test.py => mac_sysctl_test.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/unit/modules/{darwin_sysctl_test.py => mac_sysctl_test.py} (98%) diff --git a/tests/unit/modules/darwin_sysctl_test.py b/tests/unit/modules/mac_sysctl_test.py similarity index 98% rename from tests/unit/modules/darwin_sysctl_test.py rename to tests/unit/modules/mac_sysctl_test.py index 9b8e9ff2a4..e02f149d9e 100644 --- a/tests/unit/modules/darwin_sysctl_test.py +++ b/tests/unit/modules/mac_sysctl_test.py @@ -7,7 +7,7 @@ from __future__ import absolute_import # Import Salt Libs -from salt.modules import darwin_sysctl +from salt.modules import mac_sysctl from salt.exceptions import CommandExecutionError # Import Salt Testing Libs From f8c0b439b8bff3b385fe5d20552edc6f348e7546 Mon Sep 17 00:00:00 2001 From: twangboy Date: Fri, 22 Jan 2016 19:20:24 -0700 Subject: [PATCH 082/100] Fixed more lint --- tests/unit/modules/mac_sysctl_test.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/unit/modules/mac_sysctl_test.py b/tests/unit/modules/mac_sysctl_test.py index e02f149d9e..e90ec64c05 100644 --- a/tests/unit/modules/mac_sysctl_test.py +++ b/tests/unit/modules/mac_sysctl_test.py @@ -25,13 +25,13 @@ from salttesting.mock import ( ensure_in_syspath('../../') # Globals -darwin_sysctl.__salt__ = {} +mac_sysctl.__salt__ = {} @skipIf(NO_MOCK, NO_MOCK_REASON) class DarwinSysctlTestCase(TestCase): ''' - TestCase for salt.modules.darwin_sysctl module + TestCase for salt.modules.mac_sysctl module ''' def test_get(self): @@ -39,8 +39,8 @@ class DarwinSysctlTestCase(TestCase): Tests the return of get function ''' mock_cmd = MagicMock(return_value='foo') - with patch.dict(darwin_sysctl.__salt__, {'cmd.run': mock_cmd}): - self.assertEqual(darwin_sysctl.get('kern.ostype'), 'foo') + with patch.dict(mac_sysctl.__salt__, {'cmd.run': mock_cmd}): + self.assertEqual(mac_sysctl.get('kern.ostype'), 'foo') def test_assign_cmd_failed(self): ''' @@ -49,9 +49,9 @@ class DarwinSysctlTestCase(TestCase): cmd = {'pid': 3548, 'retcode': 1, 'stderr': '', 'stdout': 'net.inet.icmp.icmplim: 250 -> 50'} mock_cmd = MagicMock(return_value=cmd) - with patch.dict(darwin_sysctl.__salt__, {'cmd.run_all': mock_cmd}): + with patch.dict(mac_sysctl.__salt__, {'cmd.run_all': mock_cmd}): self.assertRaises(CommandExecutionError, - darwin_sysctl.assign, + mac_sysctl.assign, 'net.inet.icmp.icmplim', 50) def test_assign(self): @@ -62,8 +62,8 @@ class DarwinSysctlTestCase(TestCase): 'stdout': 'net.inet.icmp.icmplim: 250 -> 50'} ret = {'net.inet.icmp.icmplim': '50'} mock_cmd = MagicMock(return_value=cmd) - with patch.dict(darwin_sysctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertEqual(darwin_sysctl.assign( + with patch.dict(mac_sysctl.__salt__, {'cmd.run_all': mock_cmd}): + self.assertEqual(mac_sysctl.assign( 'net.inet.icmp.icmplim', 50), ret) @patch('os.path.isfile', MagicMock(return_value=False)) @@ -74,7 +74,7 @@ class DarwinSysctlTestCase(TestCase): with patch('salt.utils.fopen', mock_open()) as m_open: helper_open = m_open() helper_open.write.assertRaises(CommandExecutionError, - darwin_sysctl.persist, + mac_sysctl.persist, 'net.inet.icmp.icmplim', 50, config=None) @@ -84,7 +84,7 @@ class DarwinSysctlTestCase(TestCase): Tests successful add of config file when previously not one ''' with patch('salt.utils.fopen', mock_open()) as m_open: - darwin_sysctl.persist('net.inet.icmp.icmplim', 50) + mac_sysctl.persist('net.inet.icmp.icmplim', 50) helper_open = m_open() helper_open.write.assert_called_once_with( '#\n# Kernel sysctl configuration\n#\n') @@ -97,7 +97,7 @@ class DarwinSysctlTestCase(TestCase): to_write = '#\n# Kernel sysctl configuration\n#\n' m_calls_list = [call.writelines(['net.inet.icmp.icmplim=50', '\n'])] with patch('salt.utils.fopen', mock_open(read_data=to_write)) as m_open: - darwin_sysctl.persist('net.inet.icmp.icmplim', 50, config=to_write) + mac_sysctl.persist('net.inet.icmp.icmplim', 50, config=to_write) helper_open = m_open() calls_list = helper_open.method_calls self.assertEqual(calls_list, m_calls_list) From c65aefee201363c216e7020ac4e8d465c3030429 Mon Sep 17 00:00:00 2001 From: Eric Radman Date: Tue, 12 Apr 2016 10:41:25 -0400 Subject: [PATCH 083/100] Fix tests that assert CommandExecutionError (#32485) Trying to assert that an exception was raised using helper_open.write.assertRaises() is bogus--there is no such method. Use standard unittest.assertRaises() instead. --- tests/unit/modules/mac_sysctl_test.py | 10 +++++----- tests/unit/modules/puppet_test.py | 23 ++++++++--------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/tests/unit/modules/mac_sysctl_test.py b/tests/unit/modules/mac_sysctl_test.py index e90ec64c05..533397bcfc 100644 --- a/tests/unit/modules/mac_sysctl_test.py +++ b/tests/unit/modules/mac_sysctl_test.py @@ -72,11 +72,11 @@ class DarwinSysctlTestCase(TestCase): Tests adding of config file failure ''' with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - mac_sysctl.persist, - 'net.inet.icmp.icmplim', - 50, config=None) + m_open.side_effect = IOError(13, 'Permission denied', '/file') + self.assertRaises(CommandExecutionError, + mac_sysctl.persist, + 'net.inet.icmp.icmplim', + 50, config=None) @patch('os.path.isfile', MagicMock(return_value=False)) def test_persist_no_conf_success(self): diff --git a/tests/unit/modules/puppet_test.py b/tests/unit/modules/puppet_test.py index eb0bd81539..dcc488a62c 100644 --- a/tests/unit/modules/puppet_test.py +++ b/tests/unit/modules/puppet_test.py @@ -91,12 +91,12 @@ class PuppetTestCase(TestCase): with patch('salt.utils.fopen', mock_open()): self.assertTrue(puppet.disable()) - try: - with patch('salt.utils.fopen', mock_open()) as m_open: - m_open.side_effect = IOError(13, 'Permission denied:', '/file') - self.assertRaises(CommandExecutionError, puppet.disable) - except StopIteration: - pass + try: + with patch('salt.utils.fopen', mock_open()) as m_open: + m_open.side_effect = IOError(13, 'Permission denied:', '/file') + self.assertRaises(CommandExecutionError, puppet.disable) + except StopIteration: + pass def test_status(self): ''' @@ -156,16 +156,9 @@ class PuppetTestCase(TestCase): mock_open(read_data="resources: 1")): self.assertDictEqual(puppet.summary(), {'resources': 1}) -<<<<<<< HEAD with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - puppet.summary) -======= - with patch('salt.utils.fopen', mock_open()) as m_open: - m_open.side_effect = IOError(13, 'Permission denied:', '/file') - self.assertRaises(CommandExecutionError, puppet.summary) ->>>>>>> 4b58bfc... Fix tests that assert CommandExecutionError (#32485) + m_open.side_effect = IOError(13, 'Permission denied:', '/file') + self.assertRaises(CommandExecutionError, puppet.summary) def test_plugin_sync(self): ''' From f74ca15f50fc913a80b0094a6b6fbb519cb012e2 Mon Sep 17 00:00:00 2001 From: Mike Place Date: Wed, 20 Jul 2016 08:04:00 -0600 Subject: [PATCH 084/100] Remove test for file dir behavior Refs #34809 --- tests/unit/states/file_test.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/tests/unit/states/file_test.py b/tests/unit/states/file_test.py index 2168a6b947..1b2caf48c9 100644 --- a/tests/unit/states/file_test.py +++ b/tests/unit/states/file_test.py @@ -1318,26 +1318,6 @@ class FileTestCase(TestCase): (name, source, preserve=True), ret) - with patch.object(os.path, 'isdir', mock_t): - with patch.dict(filestate.__opts__, {'test': False}): - with patch.object(shutil, 'copy', - MagicMock(side_effect=[IOError, - True])): - comt = ('Failed to copy "{0}" to "{1}"' - .format(source, name)) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.copy - (name, source, - preserve=True), ret) - - comt = ('Copied "{0}" to "{1}"'.format(source, - name)) - ret.update({'comment': comt, 'result': True, - 'changes': {name: source}}) - self.assertDictEqual(filestate.copy - (name, source, - preserve=True), ret) - # 'rename' function tests: 1 def test_rename(self): From 2cf6f36d89913aa3ce15d16d769b1abbc2e95879 Mon Sep 17 00:00:00 2001 From: abednarik Date: Fri, 18 Dec 2015 17:14:04 -0300 Subject: [PATCH 085/100] modules.darwin_sysctl: __virtual__ return err msg. Updated message in darwin_sysctl module when return False if OS is not OSX. --- salt/modules/darwin_sysctl.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/salt/modules/darwin_sysctl.py b/salt/modules/darwin_sysctl.py index dce868a28e..fe9166647a 100644 --- a/salt/modules/darwin_sysctl.py +++ b/salt/modules/darwin_sysctl.py @@ -19,7 +19,10 @@ def __virtual__(): ''' Only run on Darwin (OS X) systems ''' - return __virtualname__ if __grains__['os'] == 'MacOS' else False + if __grains__['os'] == 'MacOS': + return __virtualname__ + return (False, 'The darwin_sysctl execution module cannot be loaded: ' + 'only available on MacOS systems.') def show(config_file=False): From 9f9aa4779c02359f5cc6b822542dec366cde6264 Mon Sep 17 00:00:00 2001 From: twangboy Date: Fri, 22 Jan 2016 12:04:11 -0700 Subject: [PATCH 086/100] rename darwin_sysctl.py to mac_sysctl.py --- salt/modules/{darwin_sysctl.py => mac_sysctl.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename salt/modules/{darwin_sysctl.py => mac_sysctl.py} (100%) diff --git a/salt/modules/darwin_sysctl.py b/salt/modules/mac_sysctl.py similarity index 100% rename from salt/modules/darwin_sysctl.py rename to salt/modules/mac_sysctl.py From 1c260e4bd0aa678d12bebee81751572a69882109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Tue, 6 Sep 2016 12:44:36 +0100 Subject: [PATCH 087/100] Fix tests to prevent errors when libcloud is not present --- tests/unit/cloud/clouds/dimensiondata_test.py | 10 ++++++++-- tests/unit/cloud/clouds/gce_test.py | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/unit/cloud/clouds/dimensiondata_test.py b/tests/unit/cloud/clouds/dimensiondata_test.py index aa7f2c07fe..ee01d6522e 100644 --- a/tests/unit/cloud/clouds/dimensiondata_test.py +++ b/tests/unit/cloud/clouds/dimensiondata_test.py @@ -8,7 +8,13 @@ # Import Python libs from __future__ import absolute_import -import libcloud.security + +try: + import libcloud.security + HAS_LIBCLOUD = True +except ImportError: + HAS_LIBCLOUD = False + import platform import os @@ -44,7 +50,7 @@ ON_SUSE = True if 'SuSE' in platform.dist() else False ON_MAC = True if 'Darwin' in platform.system() else False if not os.path.exists('/etc/ssl/certs/YaST-CA.pem') and ON_SUSE: - if os.path.isfile('/etc/ssl/ca-bundle.pem'): + if os.path.isfile('/etc/ssl/ca-bundle.pem') and HAS_LIBCLOUD: libcloud.security.CA_CERTS_PATH.append('/etc/ssl/ca-bundle.pem') else: HAS_CERTS = False diff --git a/tests/unit/cloud/clouds/gce_test.py b/tests/unit/cloud/clouds/gce_test.py index 87824eb798..c90f8abb9b 100644 --- a/tests/unit/cloud/clouds/gce_test.py +++ b/tests/unit/cloud/clouds/gce_test.py @@ -8,7 +8,13 @@ # Import Python libs from __future__ import absolute_import -import libcloud.security + +try: + import libcloud.security + HAS_LIBCLOUD = True +except ImportError: + HAS_LIBCLOUD = False + import platform import os @@ -51,7 +57,7 @@ ON_SUSE = True if 'SuSE' in platform.dist() else False ON_MAC = True if 'Darwin' in platform.system() else False if not os.path.exists('/etc/ssl/certs/YaST-CA.pem') and ON_SUSE: - if os.path.isfile('/etc/ssl/ca-bundle.pem'): + if os.path.isfile('/etc/ssl/ca-bundle.pem') and HAS_LIBCLOUD: libcloud.security.CA_CERTS_PATH.append('/etc/ssl/ca-bundle.pem') else: HAS_CERTS = False From d1d806f893d58d36088c8e741c65632a0c2994f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Wed, 7 Sep 2016 16:36:35 +0100 Subject: [PATCH 088/100] Fix PortageConfigTestCase in case of portage is not present --- tests/unit/modules/portage_config.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/unit/modules/portage_config.py b/tests/unit/modules/portage_config.py index 8da1ebedd0..6275442cea 100644 --- a/tests/unit/modules/portage_config.py +++ b/tests/unit/modules/portage_config.py @@ -11,7 +11,7 @@ from __future__ import absolute_import # Import Salt Testing libs from salttesting import skipIf, TestCase from salttesting.helpers import ensure_in_syspath -from salttesting.mock import NO_MOCK, NO_MOCK_REASON +from salttesting.mock import NO_MOCK, NO_MOCK_REASON, MagicMock ensure_in_syspath('../../') # Import salt libs @@ -20,6 +20,10 @@ from salt.modules import portage_config @skipIf(NO_MOCK, NO_MOCK_REASON) class PortageConfigTestCase(TestCase): + class DummyAtom(object): + def __init__(self, atom): + self.cp, self.repo = atom.split("::") if "::" in atom else (atom, None) + def test_get_config_file_wildcards(self): pairs = [ ('*/*::repo', '/etc/portage/package.mask/repo'), @@ -29,7 +33,11 @@ class PortageConfigTestCase(TestCase): ('cat/pkg::repo', '/etc/portage/package.mask/cat/pkg'), ] + portage_config.portage = MagicMock() for (atom, expected) in pairs: + dummy_atom = self.DummyAtom(atom) + portage_config.portage.dep.Atom = MagicMock(return_value=dummy_atom) + portage_config._p_to_cp = MagicMock(return_value=dummy_atom.cp) self.assertEqual(portage_config._get_config_file('mask', atom), expected) if __name__ == '__main__': From 29814f9d43b75141602c8ca10783221a266750f9 Mon Sep 17 00:00:00 2001 From: Eric Radman Date: Wed, 20 Apr 2016 16:01:21 -0400 Subject: [PATCH 089/100] Skip utils_test if timelib is not installed (#32699) date_cast() throws a RuntimeError, not an ImportError --- tests/unit/utils/utils_test.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/unit/utils/utils_test.py b/tests/unit/utils/utils_test.py index 261af69b59..11f0bafc22 100644 --- a/tests/unit/utils/utils_test.py +++ b/tests/unit/utils/utils_test.py @@ -527,14 +527,9 @@ class UtilsTestCase(TestCase): ret = utils.date_cast('Mon Dec 23 10:19:15 MST 2013') expected_ret = datetime.datetime(2013, 12, 23, 10, 19, 15) self.assertEqual(ret, expected_ret) - except ImportError: - try: - ret = utils.date_cast('Mon Dec 23 10:19:15 MST 2013') - expected_ret = datetime.datetime(2013, 12, 23, 10, 19, 15) - self.assertEqual(ret, expected_ret) - except RuntimeError: - # Unparseable without timelib installed - self.skipTest('\'timelib\' is not installed') + except RuntimeError: + # Unparseable without timelib installed + self.skipTest('\'timelib\' is not installed') @skipIf(not HAS_TIMELIB, '\'timelib\' is not installed') def test_date_format(self): From 8b480167e129104e77dc93cac248c855d88b8cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mihai=20Dinc=C4=83?= Date: Tue, 23 Aug 2016 18:49:53 +0200 Subject: [PATCH 090/100] Fix tests (#35693) * Fix tests/unit/modules/useradd_test.py::UserAddTestCase::test_info * Fix unit/pyobjects_test.py::MapTests::test_map * Fix tests/unit/pyobjects_test.py::RendererTests::test_extend * Fix tests/unit/pyobjects_test.py::RendererTests::test_requisite_implicit_list --- tests/unit/modules/useradd_test.py | 6 ++---- tests/unit/pyobjects_test.py | 11 +++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/tests/unit/modules/useradd_test.py b/tests/unit/modules/useradd_test.py index 7e646b6823..cc9e610366 100644 --- a/tests/unit/modules/useradd_test.py +++ b/tests/unit/modules/useradd_test.py @@ -326,7 +326,7 @@ class UserAddTestCase(TestCase): ''' Test the user information ''' - self.assertEqual(useradd.info('salt'), {}) + self.assertEqual(useradd.info('username-that-doesnt-exist'), {}) mock = MagicMock(return_value=pwd.struct_passwd(('_TEST_GROUP', '*', @@ -336,9 +336,7 @@ class UserAddTestCase(TestCase): '/var/virusmails', '/usr/bin/false'))) with patch.object(pwd, 'getpwnam', mock): - mock = MagicMock(return_value='Group Name') - with patch.object(useradd, 'list_groups', mock): - self.assertEqual(useradd.info('salt')['name'], '_TEST_GROUP') + self.assertEqual(useradd.info('username-that-doesnt-exist')['name'], '_TEST_GROUP') # 'list_groups' function tests: 1 diff --git a/tests/unit/pyobjects_test.py b/tests/unit/pyobjects_test.py index f1c3e29ad3..3eb4bd56e0 100644 --- a/tests/unit/pyobjects_test.py +++ b/tests/unit/pyobjects_test.py @@ -54,10 +54,18 @@ include('http') extend_template = '''#!pyobjects include('http') + +from salt.utils.pyobjects import StateFactory +Service = StateFactory('service') + Service.running(extend('apache'), watch=[{'file': '/etc/file'}]) ''' map_template = '''#!pyobjects +from salt.utils.pyobjects import StateFactory +Service = StateFactory('service') + + class Samba(Map): __merge__ = 'samba:lookup' @@ -127,6 +135,9 @@ from salt://password.sls import password ''' requisite_implicit_list_template = '''#!pyobjects +from salt.utils.pyobjects import StateFactory +Service = StateFactory('service') + with Pkg.installed("pkg"): Service.running("service", watch=File("file"), require=Cmd("cmd")) ''' From 158bcbff65e1dfd31a962be7ab6076fa814bd080 Mon Sep 17 00:00:00 2001 From: Eric Radman Date: Tue, 12 Apr 2016 10:41:25 -0400 Subject: [PATCH 091/100] Fix tests that assert CommandExecutionError (#32485) Trying to assert that an exception was raised using helper_open.write.assertRaises() is bogus--there is no such method. Use standard unittest.assertRaises() instead. --- salt/modules/linux_sysctl.py | 7 +++++-- tests/unit/modules/linux_sysctl_test.py | 19 ++++++++++++------- tests/unit/modules/mac_sysctl_test.py | 10 +++++----- tests/unit/modules/mount_test.py | 14 +++++--------- tests/unit/modules/puppet_test.py | 15 ++++++++------- 5 files changed, 35 insertions(+), 30 deletions(-) diff --git a/salt/modules/linux_sysctl.py b/salt/modules/linux_sysctl.py index 4f63740977..48b4ef104d 100644 --- a/salt/modules/linux_sysctl.py +++ b/salt/modules/linux_sysctl.py @@ -41,8 +41,11 @@ def _check_systemd_salt_config(): sysctl_dir = os.path.split(conf)[0] if not os.path.exists(sysctl_dir): os.makedirs(sysctl_dir) - with salt.utils.fopen(conf, 'w'): - pass + try: + salt.utils.fopen(conf, 'w').close() + except (IOError, OSError): + msg = 'Could not create file: {0}' + raise CommandExecutionError(msg.format(conf)) return conf diff --git a/tests/unit/modules/linux_sysctl_test.py b/tests/unit/modules/linux_sysctl_test.py index 9daeeec9fd..81972cbda1 100644 --- a/tests/unit/modules/linux_sysctl_test.py +++ b/tests/unit/modules/linux_sysctl_test.py @@ -84,17 +84,22 @@ class LinuxSysctlTestCase(TestCase): self.assertEqual(linux_sysctl.assign( 'net.ipv4.ip_forward', 1), ret) - @patch('os.path.isfile', MagicMock(return_value=False)) def test_persist_no_conf_failure(self): ''' Tests adding of config file failure ''' - with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - linux_sysctl.persist, - 'net.ipv4.ip_forward', - 1, config=None) + asn_cmd = {'pid': 1337, 'retcode': 0, + 'stderr': "sysctl: permission denied", 'stdout': ''} + mock_asn_cmd = MagicMock(return_value=asn_cmd) + cmd = "sysctl -w net.ipv4.ip_forward=1" + mock_cmd = MagicMock(return_value=cmd) + with patch.dict(linux_sysctl.__salt__, {'cmd.run_stdout': mock_cmd, + 'cmd.run_all': mock_asn_cmd}): + with patch('salt.utils.fopen', mock_open()) as m_open: + self.assertRaises(CommandExecutionError, + linux_sysctl.persist, + 'net.ipv4.ip_forward', + 1, config=None) @patch('os.path.isfile', MagicMock(return_value=False)) @patch('os.path.exists', MagicMock(return_value=True)) diff --git a/tests/unit/modules/mac_sysctl_test.py b/tests/unit/modules/mac_sysctl_test.py index e90ec64c05..533397bcfc 100644 --- a/tests/unit/modules/mac_sysctl_test.py +++ b/tests/unit/modules/mac_sysctl_test.py @@ -72,11 +72,11 @@ class DarwinSysctlTestCase(TestCase): Tests adding of config file failure ''' with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - mac_sysctl.persist, - 'net.inet.icmp.icmplim', - 50, config=None) + m_open.side_effect = IOError(13, 'Permission denied', '/file') + self.assertRaises(CommandExecutionError, + mac_sysctl.persist, + 'net.inet.icmp.icmplim', + 50, config=None) @patch('os.path.isfile', MagicMock(return_value=False)) def test_persist_no_conf_success(self): diff --git a/tests/unit/modules/mount_test.py b/tests/unit/modules/mount_test.py index 290c368014..b2cf904586 100644 --- a/tests/unit/modules/mount_test.py +++ b/tests/unit/modules/mount_test.py @@ -141,10 +141,10 @@ class MountTestCase(TestCase): with patch.dict(mount.__grains__, {'kernel': ''}): with patch.object(mount, 'fstab', mock_fstab): with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - mount.rm_fstab, - config=None) + m_open.side_effect = IOError(13, 'Permission denied:', '/file') + self.assertRaises(CommandExecutionError, + mount.rm_fstab, + 'name', 'device') def test_set_fstab(self): ''' @@ -180,11 +180,7 @@ class MountTestCase(TestCase): mock = MagicMock(return_value={'name': 'name'}) with patch.object(mount, 'fstab', mock): - with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - mount.rm_automaster, - 'name', 'device') + self.assertTrue(mount.rm_automaster('name', 'device')) def test_set_automaster(self): ''' diff --git a/tests/unit/modules/puppet_test.py b/tests/unit/modules/puppet_test.py index 02bc2e104d..2cdd6969b4 100644 --- a/tests/unit/modules/puppet_test.py +++ b/tests/unit/modules/puppet_test.py @@ -85,10 +85,12 @@ class PuppetTestCase(TestCase): with patch('salt.utils.fopen', mock_open()): self.assertTrue(puppet.disable()) - with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - puppet.disable) + try: + with patch('salt.utils.fopen', mock_open()) as m_open: + m_open.side_effect = IOError(13, 'Permission denied:', '/file') + self.assertRaises(CommandExecutionError, puppet.disable) + except StopIteration: + pass def test_status(self): ''' @@ -145,9 +147,8 @@ class PuppetTestCase(TestCase): self.assertDictEqual(puppet.summary(), {'resources': 1}) with patch('salt.utils.fopen', mock_open()) as m_open: - helper_open = m_open() - helper_open.write.assertRaises(CommandExecutionError, - puppet.summary) + m_open.side_effect = IOError(13, 'Permission denied:', '/file') + self.assertRaises(CommandExecutionError, puppet.summary) def test_plugin_sync(self): ''' From 52a7ed605ea79b7d806dca2f2d475ae31fdc17c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Wed, 7 Sep 2016 12:36:18 +0100 Subject: [PATCH 092/100] Fixed _interfaces_ifconfig output for SunOS test --- tests/unit/utils/network.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/unit/utils/network.py b/tests/unit/utils/network.py index 89db8481c9..72ca85746b 100644 --- a/tests/unit/utils/network.py +++ b/tests/unit/utils/network.py @@ -150,16 +150,17 @@ class NetworkTestCase(TestCase): interfaces = network._interfaces_ifconfig(SOLARIS) self.assertEqual(interfaces, {'ilbext0': {'inet': [{'address': '10.10.11.11', + 'broadcast': '10.10.11.31', + 'netmask': '255.255.255.224'}, + {'address': '10.10.11.12', 'broadcast': '10.10.11.31', 'netmask': '255.255.255.224'}], - 'inet6': [{'address': '::', - 'prefixlen': '0'}], + 'inet6': [], 'up': True}, 'ilbint0': {'inet': [{'address': '10.6.0.11', 'broadcast': '10.6.0.255', 'netmask': '255.255.255.0'}], - 'inet6': [{'address': '::', - 'prefixlen': '0'}], + 'inet6': [], 'up': True}, 'lo0': {'inet': [{'address': '127.0.0.1', 'netmask': '255.0.0.0'}], @@ -174,8 +175,7 @@ class NetworkTestCase(TestCase): 'up': True}, 'vpn0': {'inet': [{'address': '10.6.0.14', 'netmask': '255.0.0.0'}], - 'inet6': [{'address': '::', - 'prefixlen': '0'}], + 'inet6': [], 'up': True}} ) From 0e13118f6e2f95d49d89e39882d51aeaf530f387 Mon Sep 17 00:00:00 2001 From: Denys Havrysh Date: Thu, 8 Sep 2016 18:25:11 +0300 Subject: [PATCH 093/100] Document `owner` kwarg for `postgres_schema.present` state function (#36147) --- salt/states/postgres_schema.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/salt/states/postgres_schema.py b/salt/states/postgres_schema.py index 6b509e90cb..f09d413820 100644 --- a/salt/states/postgres_schema.py +++ b/salt/states/postgres_schema.py @@ -39,6 +39,9 @@ def present(dbname, name, name The name of the schema to manage + owner + The database user that will be the owner of the schema + db_user database username if different from config or default @@ -99,7 +102,7 @@ def absent(dbname, name, db_user=None, db_password=None, db_host=None, db_port=None): ''' - Ensure that the named schema is absent + Ensure that the named schema is absent. dbname The database's name will work on From 6242702288e2aa790ccf7a97bb1cfeb3513989fe Mon Sep 17 00:00:00 2001 From: Mike Place Date: Fri, 9 Sep 2016 00:27:28 +0900 Subject: [PATCH 094/100] Fix issue with cp.push (#36136) Incorrect handling of os.path.splitdrive() --- salt/modules/cp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/modules/cp.py b/salt/modules/cp.py index 56985ee12c..42e15755db 100644 --- a/salt/modules/cp.py +++ b/salt/modules/cp.py @@ -787,7 +787,7 @@ def push(path, keep_symlinks=False, upload_path=None): load_path_normal = os.path.normpath(load_path) # If this is Windows and a drive letter is present, remove it - load_path_split_drive = os.path.splitdrive(load_path_normal)[1:] + load_path_split_drive = os.path.splitdrive(load_path_normal)[1] # Finally, split the remaining path into a list for delivery to the master load_path_list = os.path.split(load_path_split_drive) From ecb0979be7af97cd9165958efc2f782f80aff1d5 Mon Sep 17 00:00:00 2001 From: Jacob Hammons Date: Thu, 8 Sep 2016 11:10:56 -0600 Subject: [PATCH 095/100] Adds #36055 to release notes --- doc/topics/releases/2016.3.3.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/topics/releases/2016.3.3.rst b/doc/topics/releases/2016.3.3.rst index f53015c4ae..a6b0019e39 100644 --- a/doc/topics/releases/2016.3.3.rst +++ b/doc/topics/releases/2016.3.3.rst @@ -5,6 +5,11 @@ Salt 2016.3.3 Release Notes Version 2016.3.3 is a bugfix release for :doc:`2016.3.0 `. +Known Issues +------------ + +:issue:`36055`: Salt Cloud events (``salt/cloud``) are not generated on the +master event bus when provisioning cloud systems. Changes for v2016.3.2..2016.3.3 ------------------------------- From abe398271bdad7fde3a337150ce25201ffcc4fa1 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Thu, 8 Sep 2016 14:02:21 -0500 Subject: [PATCH 096/100] Use string.ascii_lowercase (#36163) This is less error-prone than defining a string with all lowercase letters. This also uses a tuple for the join as the tuple is a lighter-weight type and performs the join almost twice as fast as when this is done with a list, as can be seen below: >>> import timeit >>> list_timer = timeit.Timer('":".join(["foo", "bar"])') >>> tuple_timer = timeit.Timer('":".join(("foo", "bar"))') >>> list_timer.repeat() [0.18616199493408203, 0.14670491218566895, 0.14609003067016602] >>> tuple_timer.repeat() [0.10539507865905762, 0.08876705169677734, 0.08883404731750488] >>> --- salt/fileclient.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/salt/fileclient.py b/salt/fileclient.py index f654cce689..d73ede3d40 100644 --- a/salt/fileclient.py +++ b/salt/fileclient.py @@ -8,6 +8,7 @@ from __future__ import absolute_import import contextlib import logging import os +import string import shutil import ftplib from tornado.httputil import parse_response_start_line, HTTPInputError @@ -465,8 +466,8 @@ class Client(object): url_path = os.path.join( url_data.netloc, url_data.path).rstrip(os.sep) - if url_scheme and url_scheme.lower() in 'abcdefghijklmnopqrstuvwxyz': - url_path = ':'.join([url_scheme, url_path]) + if url_scheme and url_scheme.lower() in string.ascii_lowercase: + url_path = ':'.join((url_scheme, url_path)) url_scheme = 'file' if url_scheme in ('file', ''): From 0e8e66877a2a68ee717efd859c6075b07d65a4b1 Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Thu, 8 Sep 2016 13:03:12 -0600 Subject: [PATCH 097/100] Back-port #36067 to carbon (#36159) * Fix signal handling We had a little mix-up with the args ordering for our signal handling. This sends the proper signal to processes on cleanup. I have also temporarily disabled the pytest engines because they were causing the minions to try to to connect to a master IPC socket which could not be found. This put the minions into a continual futex state which was not playing well with kill sigs. * Lint --- salt/utils/process.py | 5 +++-- tests/integration/__init__.py | 21 +++------------------ 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/salt/utils/process.py b/salt/utils/process.py index a1ed756779..dc387e5e93 100644 --- a/salt/utils/process.py +++ b/salt/utils/process.py @@ -504,8 +504,9 @@ class ProcessManager(object): continue log.trace('Killing pid {0}: {1}'.format(pid, p_map['Process'])) try: - os.kill(signal.SIGKILL, pid) - except OSError: + os.kill(pid, signal.SIGKILL) + except OSError as exc: + log.exception(exc) # in case the process has since decided to die, os.kill returns OSError if not p_map['Process'].is_alive(): # The process is no longer alive, remove it from the process map dictionary diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index bb646fe7be..abe4bad68c 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -135,7 +135,6 @@ except ImportError: cmdline = process.as_dict() except psutil.NoSuchProcess as exc: log.debug('No such process found. Stacktrace: {0}'.format(exc)) - log.info('Sending %s to process: %s', sigint_name, cmdline) process.send_signal(sigint) try: @@ -648,8 +647,9 @@ class SaltMaster(SaltDaemonScriptBase): def get_check_ports(self): #return set([self.config['runtests_conn_check_port']]) return set([self.config['ret_port'], - self.config['publish_port'], - self.config['runtests_conn_check_port']]) + self.config['publish_port']]) + # Disabled along with Pytest config until fixed. +# self.config['runtests_conn_check_port']]) def get_script_args(self): #return ['-l', 'debug'] @@ -1158,22 +1158,7 @@ class TestDaemon(object): syndic_opts[optname] = optname_path syndic_master_opts[optname] = optname_path - master_opts['runtests_conn_check_port'] = get_unused_localhost_port() - minion_opts['runtests_conn_check_port'] = get_unused_localhost_port() - sub_minion_opts['runtests_conn_check_port'] = get_unused_localhost_port() - syndic_opts['runtests_conn_check_port'] = get_unused_localhost_port() - syndic_master_opts['runtests_conn_check_port'] = get_unused_localhost_port() - for conf in (master_opts, minion_opts, sub_minion_opts, syndic_opts, syndic_master_opts): - if 'engines' not in conf: - conf['engines'] = [] - conf['engines'].append({'salt_runtests': {}}) - - if 'engines_dirs' not in conf: - conf['engines_dirs'] = [] - - conf['engines_dirs'].insert(0, ENGINES_DIR) - if 'log_handlers_dirs' not in conf: conf['log_handlers_dirs'] = [] conf['log_handlers_dirs'].insert(0, LOG_HANDLERS_DIR) From 3445a333d5ec93c20f8a64fccbaf136aaf6e6843 Mon Sep 17 00:00:00 2001 From: Nicole Thomas Date: Thu, 8 Sep 2016 15:49:01 -0600 Subject: [PATCH 098/100] Remove unclosed backticks in walkthrough doc (#36170) --- doc/topics/tutorials/walkthrough.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/topics/tutorials/walkthrough.rst b/doc/topics/tutorials/walkthrough.rst index 58fe9cc327..0bda9976ef 100644 --- a/doc/topics/tutorials/walkthrough.rst +++ b/doc/topics/tutorials/walkthrough.rst @@ -91,7 +91,7 @@ the firewall tutorial is available :doc:`here `. Finding the Salt Master ~~~~~~~~~~~~~~~~~~~~~~~ -When a minion starts, by default it searches for a system that resolves to the ``salt`` hostname`` on the network. +When a minion starts, by default it searches for a system that resolves to the ``salt`` hostname on the network. If found, the minion initiates the handshake and key authentication process with the Salt master. This means that the easiest configuration approach is to set internal DNS to resolve the name ``salt`` back to the Salt Master IP. From 1a7996125ab3b60840a9816d50da6e2172b0e6f6 Mon Sep 17 00:00:00 2001 From: Joseph Hall Date: Thu, 8 Sep 2016 15:49:45 -0600 Subject: [PATCH 099/100] Fix breakage from updates to __utils__ (#36168) --- salt/cloud/clouds/azurearm.py | 6 +++--- salt/utils/cloud.py | 12 +++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/salt/cloud/clouds/azurearm.py b/salt/cloud/clouds/azurearm.py index 4fa08b4870..c3f14b3236 100644 --- a/salt/cloud/clouds/azurearm.py +++ b/salt/cloud/clouds/azurearm.py @@ -473,7 +473,7 @@ def show_instance(name, resource_group=None, call=None): # pylint: disable=unus data['network_profile']['network_interfaces'] = ifaces data['resource_group'] = resource_group - salt.utils.cloud.cache_node( + __utils__['cloud.cache_node']( salt.utils.simple_types_filter(data), __active_provider_name__, __opts__ @@ -1118,7 +1118,7 @@ def create(vm_): vm_['password'] = config.get_cloud_config_value( 'ssh_password', vm_, __opts__ ) - ret = salt.utils.cloud.bootstrap(vm_, __opts__) + ret = __utils__['cloud.bootstrap'](vm_, __opts__) data = show_instance(vm_['name'], call='action') log.info('Created Cloud VM \'{0[name]}\''.format(vm_)) @@ -1179,7 +1179,7 @@ def destroy(name, conn=None, call=None, kwargs=None): # pylint: disable=unused- result.wait() if __opts__.get('update_cachedir', False) is True: - salt.utils.cloud.delete_minion_cachedir(name, __active_provider_name__.split(':')[0], __opts__) + __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) cleanup_disks = config.get_cloud_config_value( 'cleanup_disks', diff --git a/salt/utils/cloud.py b/salt/utils/cloud.py index 5f46240f4c..fbba0328ea 100644 --- a/salt/utils/cloud.py +++ b/salt/utils/cloud.py @@ -2607,13 +2607,16 @@ def delete_minion_cachedir(minion_id, provider, opts, base=None): all cachedirs to find the minion's cache file. Needs `update_cachedir` set to True. ''' - if opts.get('update_cachedir', False) is False: + if isinstance(opts, dict): + __opts__.update(opts) + + if __opts__.get('update_cachedir', False) is False: return if base is None: base = __opts__['cachedir'] - driver = next(six.iterkeys(opts['providers'][provider])) + driver = next(six.iterkeys(__opts__['providers'][provider])) fname = '{0}.p'.format(minion_id) for cachedir in 'requested', 'active': path = os.path.join(base, cachedir, driver, provider, fname) @@ -2839,7 +2842,10 @@ def cache_node(node, provider, opts): .. versionadded:: 2014.7.0 ''' - if 'update_cachedir' not in opts or not opts['update_cachedir']: + if isinstance(opts, dict): + __opts__.update(opts) + + if 'update_cachedir' not in __opts__ or not __opts__['update_cachedir']: return if not os.path.exists(os.path.join(__opts__['cachedir'], 'active')): From c30f697dfd17975c5bf793a1a7d583ef97b5c4a1 Mon Sep 17 00:00:00 2001 From: rallytime Date: Thu, 8 Sep 2016 17:23:15 -0600 Subject: [PATCH 100/100] Pylint fix --- tests/unit/cloud/clouds/dimensiondata_test.py | 3 --- tests/unit/cloud/clouds/gce_test.py | 3 --- 2 files changed, 6 deletions(-) diff --git a/tests/unit/cloud/clouds/dimensiondata_test.py b/tests/unit/cloud/clouds/dimensiondata_test.py index c1b41d0763..822d1af00d 100644 --- a/tests/unit/cloud/clouds/dimensiondata_test.py +++ b/tests/unit/cloud/clouds/dimensiondata_test.py @@ -15,9 +15,6 @@ try: except ImportError: HAS_LIBCLOUD = False -import platform -import os - # Import Salt Libs from salt.cloud.clouds import dimensiondata from salt.exceptions import SaltCloudSystemExit diff --git a/tests/unit/cloud/clouds/gce_test.py b/tests/unit/cloud/clouds/gce_test.py index a4e46f71f5..bf97dd8a54 100644 --- a/tests/unit/cloud/clouds/gce_test.py +++ b/tests/unit/cloud/clouds/gce_test.py @@ -15,9 +15,6 @@ try: except ImportError: HAS_LIBCLOUD = False -import platform -import os - # Import Salt Libs from salt.cloud.clouds import gce from salt.exceptions import SaltCloudSystemExit