From 1e6a8a23c87dfdd489b85fe8bb3aa3c8e3e836b0 Mon Sep 17 00:00:00 2001 From: KYTG Date: Thu, 8 Feb 2024 14:31:59 -0500 Subject: [PATCH] Update files with pre-commit --- .gitignore | 3 +- LICENSE | 1 - README.rst | 6 +- codecov.yml | 1 - pepper/__init__.py | 11 +- pepper/cli.py | 589 ++++++++++++++++---------- pepper/exceptions.py | 3 - pepper/libpepper.py | 336 ++++++++------- pepper/retcode.py | 62 +-- pepper/script.py | 84 ++-- setup.py | 72 ++-- tests/__init__.py | 1 - tests/conftest.py | 179 ++++---- tests/integration/__init__.py | 1 - tests/integration/test_clients.py | 41 +- tests/integration/test_json.py | 25 +- tests/integration/test_local.py | 5 +- tests/integration/test_login_opts.py | 61 +-- tests/integration/test_poller.py | 17 +- tests/integration/test_profile.py | 7 +- tests/integration/test_token.py | 35 +- tests/integration/test_vanilla.py | 9 +- tests/requirements.txt | 2 +- tests/unit/__init__.py | 1 - tests/unit/test_init.py | 14 +- tests/unit/test_login_details.py | 22 +- tests/unit/test_retcodes_error.py | 9 +- tests/unit/test_retcodes_fail_fail.py | 43 +- tests/unit/test_retcodes_fail_none.py | 41 +- tests/unit/test_retcodes_fail_pass.py | 45 +- tests/unit/test_retcodes_none_none.py | 39 +- tests/unit/test_retcodes_pass_fail.py | 47 +- tests/unit/test_retcodes_pass_none.py | 45 +- tests/unit/test_retcodes_pass_pass.py | 49 +-- tests/unit/test_token.py | 26 +- 35 files changed, 1036 insertions(+), 896 deletions(-) diff --git a/.gitignore b/.gitignore index 9c9e542..be26a46 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ MANIFEST dist/ salt_pepper.egg-info/ -.tox/ +.nox/ +artifacts/ diff --git a/LICENSE b/LICENSE index e711964..c21a46d 100644 --- a/LICENSE +++ b/LICENSE @@ -13,4 +13,3 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - diff --git a/README.rst b/README.rst index 17992d8..6b285a3 100644 --- a/README.rst +++ b/README.rst @@ -59,10 +59,10 @@ Examples leveraging the runner client. Configuration ------------- -You can configure pepper through the command line, using environment variables -or in a configuration file ``$HOME/.pepperrc`` with the following syntax : +You can configure pepper through the command line, using environment variables +or in a configuration file ``$HOME/.pepperrc`` with the following syntax : -.. code-block:: +.. code-block:: [main] SALTAPI_URL=https://localhost:8000/ diff --git a/codecov.yml b/codecov.yml index 0448b17..2ef3d9f 100644 --- a/codecov.yml +++ b/codecov.yml @@ -25,4 +25,3 @@ comment: layout: "header, diff" behavior: default require_changes: no - diff --git a/pepper/__init__.py b/pepper/__init__.py index 267c5e2..83bdf72 100644 --- a/pepper/__init__.py +++ b/pepper/__init__.py @@ -1,14 +1,15 @@ -''' +""" Pepper is a CLI front-end to salt-api -''' +""" import pkg_resources -from pepper.libpepper import Pepper, PepperException +from pepper.libpepper import Pepper +from pepper.libpepper import PepperException -__all__ = ('__version__', 'Pepper', 'PepperException') +__all__ = ("__version__", "Pepper", "PepperException") try: - __version__ = pkg_resources.get_distribution('salt_pepper').version + __version__ = pkg_resources.get_distribution("salt_pepper").version except pkg_resources.DistributionNotFound: # package is not installed __version__ = None diff --git a/pepper/cli.py b/pepper/cli.py index 68901d9..fed0258 100644 --- a/pepper/cli.py +++ b/pepper/cli.py @@ -1,8 +1,7 @@ -''' +""" A CLI interface to a remote salt-api instance -''' -from __future__ import print_function +""" import getpass import json import logging @@ -12,13 +11,12 @@ import textwrap import time -# Import Pepper Libraries import pepper -from pepper.exceptions import ( - PepperAuthException, - PepperArgumentsException, - PepperException, -) +from pepper.exceptions import PepperArgumentsException +from pepper.exceptions import PepperAuthException +from pepper.exceptions import PepperException + +# Import Pepper Libraries try: @@ -46,95 +44,127 @@ logger = logging.getLogger(__name__) -class PepperCli(object): +class PepperCli: def __init__(self, seconds_to_wait=3): self.seconds_to_wait = seconds_to_wait self.parser = self.get_parser() - self.parser.option_groups.extend([ - self.add_globalopts(), - self.add_tgtopts(), - self.add_authopts(), - self.add_retcodeopts(), - ]) + self.parser.option_groups.extend( + [ + self.add_globalopts(), + self.add_tgtopts(), + self.add_authopts(), + self.add_retcodeopts(), + ] + ) self.parse() def get_parser(self): return optparse.OptionParser( - description=__doc__, - usage='%prog [opts]', - version=pepper.__version__) + description=__doc__, usage="%prog [opts]", version=pepper.__version__ + ) def parse(self): - ''' + """ Parse all args - ''' + """ self.parser.add_option( - '-c', dest='config', - default=os.environ.get( - 'PEPPERRC', - os.path.join(os.path.expanduser('~'), '.pepperrc') - ), - help=textwrap.dedent(''' + "-c", + dest="config", + default=os.environ.get("PEPPERRC", os.path.join(os.path.expanduser("~"), ".pepperrc")), + help=textwrap.dedent( + """ Configuration file location. Default is a file path in the "PEPPERRC" environment variable or ~/.pepperrc. - '''), + """ + ), ) self.parser.add_option( - '-p', dest='profile', - default=os.environ.get('PEPPERPROFILE', 'main'), - help=textwrap.dedent(''' + "-p", + dest="profile", + default=os.environ.get("PEPPERPROFILE", "main"), + help=textwrap.dedent( + """ Profile in config file to use. Default is "PEPPERPROFILE" environment variable or 'main' - '''), + """ + ), ) self.parser.add_option( - '-m', dest='master', + "-m", + dest="master", default=os.environ.get( - 'MASTER_CONFIG', - os.path.join(os.path.expanduser('~'), '.config', 'pepper', 'master') + "MASTER_CONFIG", + os.path.join(os.path.expanduser("~"), ".config", "pepper", "master"), ), - help=textwrap.dedent(''' + help=textwrap.dedent( + """ Master Configuration file location for configuring outputters. default: ~/.config/pepper/master - '''), + """ + ), ) self.parser.add_option( - '-o', '--out', dest='output', default=None, - help=textwrap.dedent(''' + "-o", + "--out", + dest="output", + default=None, + help=textwrap.dedent( + """ Salt outputter to use for printing out returns. - ''') + """ + ), ) self.parser.add_option( - '--output-file', dest='output_file', default=None, - help=textwrap.dedent(''' + "--output-file", + dest="output_file", + default=None, + help=textwrap.dedent( + """ File to put command output in - ''') + """ + ), ) self.parser.add_option( - '-v', dest='verbose', default=0, action='count', - help=textwrap.dedent(''' + "-v", + dest="verbose", + default=0, + action="count", + help=textwrap.dedent( + """ Increment output verbosity; may be specified multiple times - '''), + """ + ), ) self.parser.add_option( - '-H', '--debug-http', dest='debug_http', default=False, action='store_true', - help=textwrap.dedent(''' + "-H", + "--debug-http", + dest="debug_http", + default=False, + action="store_true", + help=textwrap.dedent( + """ Output the HTTP request/response headers on stderr - '''), + """ + ), ) self.parser.add_option( - '--ignore-ssl-errors', action='store_true', dest='ignore_ssl_certificate_errors', default=False, - help=textwrap.dedent(''' + "--ignore-ssl-errors", + action="store_true", + dest="ignore_ssl_certificate_errors", + default=False, + help=textwrap.dedent( + """ Ignore any SSL certificate that may be encountered. Note that it is recommended to resolve certificate errors for production. - '''), + """ + ), ) self.options, self.args = self.parser.parse_args() @@ -146,237 +176,348 @@ def parse(self): self.parser.error("Options %s are mutually exclusive" % s) def add_globalopts(self): - ''' + """ Misc global options - ''' - optgroup = optparse.OptionGroup(self.parser, "Pepper ``salt`` Options", "Mimic the ``salt`` CLI") + """ + optgroup = optparse.OptionGroup( + self.parser, "Pepper ``salt`` Options", "Mimic the ``salt`` CLI" + ) optgroup.add_option( - '-t', '--timeout', dest='timeout', type='int', default=60, - help=textwrap.dedent(''' + "-t", + "--timeout", + dest="timeout", + type="int", + default=60, + help=textwrap.dedent( + """ Specify wait time (in seconds) before returning control to the shell - '''), + """ + ), ) optgroup.add_option( - '--client', dest='client', default='local', - help=textwrap.dedent(''' + "--client", + dest="client", + default="local", + help=textwrap.dedent( + """ specify the salt-api client to use (local, local_async, runner, etc) - '''), + """ + ), ) optgroup.add_option( - '--json', dest='json_input', - help=textwrap.dedent(''' + "--json", + dest="json_input", + help=textwrap.dedent( + """ Enter JSON at the CLI instead of positional (text) arguments. This is useful for arguments that need complex data structures. Specifying this argument will cause positional arguments to be ignored. - '''), + """ + ), ) optgroup.add_option( - '--json-file', dest='json_file', - help=textwrap.dedent(''' + "--json-file", + dest="json_file", + help=textwrap.dedent( + """ Specify file containing the JSON to be used by pepper - '''), + """ + ), ) optgroup.add_option( - '--fail-if-incomplete', action='store_true', dest='fail_if_minions_dont_respond', default=False, - help=textwrap.dedent(''' + "--fail-if-incomplete", + action="store_true", + dest="fail_if_minions_dont_respond", + default=False, + help=textwrap.dedent( + """ Return a failure exit code if not all minions respond. This option requires the authenticated user have access to run the `jobs.list_jobs` runner function. - '''), + """ + ), ) return optgroup def add_tgtopts(self): - ''' + """ Targeting - ''' - optgroup = optparse.OptionGroup(self.parser, "Targeting Options", "Target which minions to run commands on") + """ + optgroup = optparse.OptionGroup( + self.parser, "Targeting Options", "Target which minions to run commands on" + ) - optgroup.defaults.update({'expr_form': 'glob'}) + optgroup.defaults.update({"expr_form": "glob"}) optgroup.add_option( - '-E', '--pcre', dest='expr_form', action='store_const', const='pcre', + "-E", + "--pcre", + dest="expr_form", + action="store_const", + const="pcre", help="Target hostnames using PCRE regular expressions", ) optgroup.add_option( - '-L', '--list', dest='expr_form', action='store_const', const='list', + "-L", + "--list", + dest="expr_form", + action="store_const", + const="list", help="Specify a comma delimited list of hostnames", ) optgroup.add_option( - '-G', '--grain', dest='expr_form', action='store_const', const='grain', + "-G", + "--grain", + dest="expr_form", + action="store_const", + const="grain", help="Target based on system properties", ) optgroup.add_option( - '--grain-pcre', dest='expr_form', action='store_const', const='grain_pcre', + "--grain-pcre", + dest="expr_form", + action="store_const", + const="grain_pcre", help="Target based on PCRE matches on system properties", ) optgroup.add_option( - '-I', '--pillar', dest='expr_form', action='store_const', const='pillar', + "-I", + "--pillar", + dest="expr_form", + action="store_const", + const="pillar", help="Target based on pillar values", ) optgroup.add_option( - '--pillar-pcre', dest='expr_form', action='store_const', const='pillar_pcre', - help="Target based on PCRE matches on pillar values" + "--pillar-pcre", + dest="expr_form", + action="store_const", + const="pillar_pcre", + help="Target based on PCRE matches on pillar values", ) optgroup.add_option( - '-R', '--range', dest='expr_form', action='store_const', const='range', + "-R", + "--range", + dest="expr_form", + action="store_const", + const="range", help="Target based on range expression", ) optgroup.add_option( - '-C', '--compound', dest='expr_form', action='store_const', const='compound', + "-C", + "--compound", + dest="expr_form", + action="store_const", + const="compound", help="Target based on compound expression", ) optgroup.add_option( - '-N', '--nodegroup', dest='expr_form', action='store_const', const='nodegroup', + "-N", + "--nodegroup", + dest="expr_form", + action="store_const", + const="nodegroup", help="Target based on a named nodegroup", ) - optgroup.add_option('--batch', dest='batch', default=None) + optgroup.add_option("--batch", dest="batch", default=None) return optgroup def add_authopts(self): - ''' + """ Authentication options - ''' + """ optgroup = optparse.OptionGroup( - self.parser, "Authentication Options", - textwrap.dedent(''' + self.parser, + "Authentication Options", + textwrap.dedent( + """ Authentication credentials can optionally be supplied via the environment variables: SALTAPI_URL, SALTAPI_USER, SALTAPI_PASS, SALTAPI_EAUTH. - '''), + """ + ), ) optgroup.add_option( - '-u', '--saltapi-url', dest='saltapiurl', - help="Specify the host url. Defaults to https://localhost:8080" + "-u", + "--saltapi-url", + dest="saltapiurl", + help="Specify the host url. Defaults to https://localhost:8080", ) optgroup.add_option( - '-a', '--auth', '--eauth', '--extended-auth', dest='eauth', - help=textwrap.dedent(''' + "-a", + "--auth", + "--eauth", + "--extended-auth", + dest="eauth", + help=textwrap.dedent( + """ Specify the external_auth backend to authenticate against and interactively prompt for credentials - '''), + """ + ), ) optgroup.add_option( - '--username', dest='username', - help=textwrap.dedent(''' + "--username", + dest="username", + help=textwrap.dedent( + """ Optional, defaults to user name. will be prompt if empty unless --non-interactive - '''), + """ + ), ) optgroup.add_option( - '--password', dest='password', - help=textwrap.dedent(''' + "--password", + dest="password", + help=textwrap.dedent( + """ Optional, but will be prompted unless --non-interactive - '''), + """ + ), ) optgroup.add_option( - '--token-expire', dest='token_expire', - help=textwrap.dedent(''' + "--token-expire", + dest="token_expire", + help=textwrap.dedent( + """ Set eauth token expiry in seconds. Must be allowed per user. See the `token_expire_user_override` Master setting for more info. - '''), + """ + ), ) optgroup.add_option( - '--non-interactive', action='store_false', dest='interactive', default=True, - help=textwrap.dedent(''' + "--non-interactive", + action="store_false", + dest="interactive", + default=True, + help=textwrap.dedent( + """ Optional, fail rather than waiting for input - ''') + """ + ), ) optgroup.add_option( - '-T', '--make-token', default=False, dest='mktoken', action='store_true', - help=textwrap.dedent(''' + "-T", + "--make-token", + default=False, + dest="mktoken", + action="store_true", + help=textwrap.dedent( + """ Generate and save an authentication token for re-use. The token is generated and made available for the period defined in the Salt Master. - '''), + """ + ), ) optgroup.add_option( - '-r', '--run-uri', default=False, dest='userun', action='store_true', - help=textwrap.dedent(''' + "-r", + "--run-uri", + default=False, + dest="userun", + action="store_true", + help=textwrap.dedent( + """ Use an eauth token from /token and send commands through the /run URL instead of the traditional session token approach. - '''), + """ + ), ) optgroup.add_option( - '-x', dest='cache', + "-x", + dest="cache", default=os.environ.get( - 'PEPPERCACHE', - os.path.join(os.path.expanduser('~'), '.peppercache') + "PEPPERCACHE", os.path.join(os.path.expanduser("~"), ".peppercache") ), - help=textwrap.dedent(''' + help=textwrap.dedent( + """ Cache file location. Default is a file path in the "PEPPERCACHE" environment variable or ~/.peppercache. - '''), + """ + ), ) return optgroup def add_retcodeopts(self): - ''' + """ ret code validation options - ''' + """ optgroup = optparse.OptionGroup( - self.parser, "retcode Field Validation Options", "Validate return.HOST.retcode fields") + self.parser, "retcode Field Validation Options", "Validate return.HOST.retcode fields" + ) optgroup.add_option( - '--fail-any', dest='fail_any', action='store_true', - help="Fail if any of retcode field is non zero.") + "--fail-any", + dest="fail_any", + action="store_true", + help="Fail if any of retcode field is non zero.", + ) optgroup.add_option( - '--fail-any-none', dest='fail_any_none', action='store_true', - help="Fail if any of retcode field is non zero or there is no retcode at all.") + "--fail-any-none", + dest="fail_any_none", + action="store_true", + help="Fail if any of retcode field is non zero or there is no retcode at all.", + ) optgroup.add_option( - '--fail-all', dest='fail_all', action='store_true', - help="Fail if all retcode fields are non zero.") + "--fail-all", + dest="fail_all", + action="store_true", + help="Fail if all retcode fields are non zero.", + ) optgroup.add_option( - '--fail-all-none', dest='fail_all_none', action='store_true', - help="Fail if all retcode fields are non zero or there is no retcode at all.") + "--fail-all-none", + dest="fail_all_none", + action="store_true", + help="Fail if all retcode fields are non zero or there is no retcode at all.", + ) return optgroup def get_login_details(self): - ''' + """ This parses the config file, environment variables and command line options and returns the config values Order of parsing: command line options, ~/.pepperrc, environment, defaults - ''' + """ # setting default values results = { - 'SALTAPI_USER': None, - 'SALTAPI_PASS': None, - 'SALTAPI_EAUTH': 'auto', + "SALTAPI_USER": None, + "SALTAPI_PASS": None, + "SALTAPI_EAUTH": "auto", } try: @@ -396,39 +537,41 @@ def get_login_details(self): for key, value in list(results.items()): results[key] = os.environ.get(key, results[key]) - if results['SALTAPI_EAUTH'] == 'kerberos': - results['SALTAPI_PASS'] = None + if results["SALTAPI_EAUTH"] == "kerberos": + results["SALTAPI_PASS"] = None if self.options.eauth: - results['SALTAPI_EAUTH'] = self.options.eauth + results["SALTAPI_EAUTH"] = self.options.eauth if self.options.token_expire: - results['SALTAPI_TOKEN_EXPIRE'] = self.options.token_expire - if self.options.username is None and results['SALTAPI_USER'] is None: + results["SALTAPI_TOKEN_EXPIRE"] = self.options.token_expire + if self.options.username is None and results["SALTAPI_USER"] is None: if self.options.interactive: - results['SALTAPI_USER'] = input('Username: ') + results["SALTAPI_USER"] = input("Username: ") else: raise PepperAuthException("SALTAPI_USER required") else: if self.options.username is not None: - results['SALTAPI_USER'] = self.options.username - if self.options.password is None and \ - results['SALTAPI_PASS'] is None and \ - results['SALTAPI_EAUTH'] != 'kerberos': + results["SALTAPI_USER"] = self.options.username + if ( + self.options.password is None + and results["SALTAPI_PASS"] is None + and results["SALTAPI_EAUTH"] != "kerberos" + ): if self.options.interactive: - results['SALTAPI_PASS'] = getpass.getpass(prompt='Password: ') + results["SALTAPI_PASS"] = getpass.getpass(prompt="Password: ") else: raise PepperAuthException("SALTAPI_PASS required") else: if self.options.password is not None: - results['SALTAPI_PASS'] = self.options.password + results["SALTAPI_PASS"] = self.options.password return results def parse_url(self): - ''' + """ Determine api url - ''' - url = 'https://localhost:8000/' + """ + url = "https://localhost:8000/" try: config = ConfigParser(interpolation=None) @@ -452,28 +595,28 @@ def parse_url(self): return url def parse_login(self): - ''' + """ Extract the authentication credentials - ''' + """ login_details = self.get_login_details() # Auth values placeholder; grab interactively at CLI or from config - username = login_details['SALTAPI_USER'] - password = login_details['SALTAPI_PASS'] - eauth = login_details['SALTAPI_EAUTH'] + username = login_details["SALTAPI_USER"] + password = login_details["SALTAPI_PASS"] + eauth = login_details["SALTAPI_EAUTH"] ret = dict(username=username, password=password, eauth=eauth) - token_expire = login_details.get('SALTAPI_TOKEN_EXPIRE', None) + token_expire = login_details.get("SALTAPI_TOKEN_EXPIRE", None) if token_expire: - ret['token_expire'] = int(token_expire) + ret["token_expire"] = int(token_expire) return ret def parse_cmd(self, api): - ''' + """ Extract the low data for a command from the passed CLI params - ''' + """ # Short-circuit if JSON was given. if self.options.json_input: try: @@ -483,84 +626,84 @@ def parse_cmd(self, api): if self.options.json_file: try: - with open(self.options.json_file, 'r') as json_content: + with open(self.options.json_file) as json_content: try: return json.load(json_content) except JSONDecodeError: raise PepperArgumentsException("Invalid JSON given.") except FileNotFoundError: - raise PepperArgumentsException('Cannot open file: %s', self.options.json_file) + raise PepperArgumentsException("Cannot open file: %s", self.options.json_file) args = list(self.args) - client = self.options.client if not self.options.batch else 'local_batch' - low = {'client': client} + client = self.options.client if not self.options.batch else "local_batch" + low = {"client": client} - if client.startswith('local'): + if client.startswith("local"): if len(args) < 2: self.parser.error("Command or target not specified") - low['tgt_type'] = self.options.expr_form - low['tgt'] = args.pop(0) - low['fun'] = args.pop(0) - low['batch'] = self.options.batch - low['arg'] = args - elif client.startswith('runner'): - low['fun'] = args.pop(0) + low["tgt_type"] = self.options.expr_form + low["tgt"] = args.pop(0) + low["fun"] = args.pop(0) + low["batch"] = self.options.batch + low["arg"] = args + elif client.startswith("runner"): + low["fun"] = args.pop(0) # post https://github.com/saltstack/salt/pull/50124, kwargs can be # passed as is in foo=bar form, splitting and deserializing will # happen in salt-api. additionally, the presence of salt-version header # means we are neon or newer, so don't need a finer grained check if api.salt_version: - low['arg'] = args + low["arg"] = args else: for arg in args: - if '=' in arg: - key, value = arg.split('=', 1) + if "=" in arg: + key, value = arg.split("=", 1) try: low[key] = json.loads(value) except JSONDecodeError: low[key] = value else: - low.setdefault('arg', []).append(arg) - elif client.startswith('wheel'): - low['fun'] = args.pop(0) + low.setdefault("arg", []).append(arg) + elif client.startswith("wheel"): + low["fun"] = args.pop(0) # see above comment in runner arg handling if api.salt_version: - low['arg'] = args + low["arg"] = args else: for arg in args: - if '=' in arg: - key, value = arg.split('=', 1) + if "=" in arg: + key, value = arg.split("=", 1) try: low[key] = json.loads(value) except JSONDecodeError: low[key] = value else: - low.setdefault('arg', []).append(arg) - elif client.startswith('ssh'): + low.setdefault("arg", []).append(arg) + elif client.startswith("ssh"): if len(args) < 2: self.parser.error("Command or target not specified") - low['tgt_type'] = self.options.expr_form - low['tgt'] = args.pop(0) - low['fun'] = args.pop(0) - low['batch'] = self.options.batch - low['arg'] = args + low["tgt_type"] = self.options.expr_form + low["tgt"] = args.pop(0) + low["fun"] = args.pop(0) + low["batch"] = self.options.batch + low["arg"] = args else: - raise PepperException('Client not implemented: {0}'.format(client)) + raise PepperException("Client not implemented: {}".format(client)) return [low] def poll_for_returns(self, api, load): - ''' + """ Run a command with the local_async client and periodically poll the job cache for returns for the job. - ''' - load[0]['client'] = 'local_async' + """ + load[0]["client"] = "local_async" async_ret = self.low(api, load) - jid = async_ret['return'][0]['jid'] - nodes = async_ret['return'][0]['minions'] + jid = async_ret["return"][0]["jid"] + nodes = async_ret["return"][0]["minions"] ret_nodes = [] exit_code = 1 @@ -574,18 +717,23 @@ def poll_for_returns(self, api, load): exit_code = 1 break - jid_ret = self.low(api, [{ - 'client': 'runner', - 'fun': 'jobs.lookup_jid', - 'kwarg': { - 'jid': jid, - }, - }]) - - inner_ret = jid_ret['return'][0] + jid_ret = self.low( + api, + [ + { + "client": "runner", + "fun": "jobs.lookup_jid", + "kwarg": { + "jid": jid, + }, + } + ], + ) + + inner_ret = jid_ret["return"][0] # sometimes ret is nested in data - if 'data' in inner_ret: - inner_ret = inner_ret['data'] + if "data" in inner_ret: + inner_ret = inner_ret["data"] responded = set(inner_ret.keys()) ^ set(ret_nodes) @@ -602,7 +750,7 @@ def poll_for_returns(self, api, load): exit_code = exit_code if self.options.fail_if_minions_dont_respond else 0 failed = list(set(ret_nodes) ^ set(nodes)) if failed: - yield exit_code, [{'Failed': failed}] + yield exit_code, [{"Failed": failed}] def login(self, api): login = api.token if self.options.userun else api.login @@ -610,24 +758,24 @@ def login(self, api): if self.options.mktoken: token_file = self.options.cache try: - with open(token_file, 'rt') as f: + with open(token_file) as f: auth = json.load(f) - if auth['expire'] < time.time()+30: - logger.error('Login token expired') - raise Exception('Login token expired') + if auth["expire"] < time.time() + 30: + logger.error("Login token expired") + raise Exception("Login token expired") except Exception as e: if e.args[0] != 2: - logger.error('Unable to load login token from {0} {1}'.format(token_file, str(e))) + logger.error("Unable to load login token from {} {}".format(token_file, str(e))) if os.path.isfile(token_file): os.remove(token_file) auth = login(**self.parse_login()) try: oldumask = os.umask(0) fdsc = os.open(token_file, os.O_WRONLY | os.O_CREAT, 0o600) - with os.fdopen(fdsc, 'wt') as f: + with os.fdopen(fdsc, "wt") as f: json.dump(auth, f) except Exception as e: - logger.error('Unable to save token to {0} {1}'.format(token_file, str(e))) + logger.error("Unable to save token to {} {}".format(token_file, str(e))) finally: os.umask(oldumask) else: @@ -638,25 +786,25 @@ def login(self, api): return auth def low(self, api, load): - path = '/run' if self.options.userun else '/' + path = "/run" if self.options.userun else "/" if self.options.userun: for i in load: - i['token'] = self.auth['token'] + i["token"] = self.auth["token"] # having a defined salt_version means changes from https://github.com/saltstack/salt/pull/51979 # are available if backend is tornado, so safe to supply timeout if self.options.timeout and api.salt_version: for i in load: - if not i.get('client', '').startswith('wheel'): - i['timeout'] = self.options.timeout + if not i.get("client", "").startswith("wheel"): + i["timeout"] = self.options.timeout return api.low(load, path=path) def run(self): - ''' + """ Parse all arguments and call salt-api - ''' + """ # set up logging rootLogger = logging.getLogger(name=None) rootLogger.addHandler(logging.StreamHandler()) @@ -665,15 +813,16 @@ def run(self): api = pepper.Pepper( self.parse_url(), debug_http=self.options.debug_http, - ignore_ssl_errors=self.options.ignore_ssl_certificate_errors) + ignore_ssl_errors=self.options.ignore_ssl_certificate_errors, + ) self.login(api) load = self.parse_cmd(api) for entry in load: - if not entry.get('client', '').startswith('wheel'): - entry['full_return'] = True + if not entry.get("client", "").startswith("wheel"): + entry["full_return"] = True if self.options.fail_if_minions_dont_respond: for exit_code, ret in self.poll_for_returns(api, load): # pragma: no cover diff --git a/pepper/exceptions.py b/pepper/exceptions.py index a66a259..5e4f5ef 100644 --- a/pepper/exceptions.py +++ b/pepper/exceptions.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - class PepperAuthException(Exception): pass diff --git a/pepper/libpepper.py b/pepper/libpepper.py index c9ab461..7df3143 100644 --- a/pepper/libpepper.py +++ b/pepper/libpepper.py @@ -1,9 +1,9 @@ -''' +""" A Python library for working with Salt's REST API (Specifically the rest_cherrypy netapi module.) -''' +""" import json import logging import re @@ -17,20 +17,34 @@ pass try: - from urllib.request import HTTPHandler, HTTPSHandler, Request, urlopen, \ - install_opener, build_opener + from urllib.request import ( + HTTPHandler, + HTTPSHandler, + Request, + urlopen, + install_opener, + build_opener, + ) from urllib.error import HTTPError, URLError import urllib.parse as urlparse except ImportError: - from urllib2 import HTTPHandler, HTTPSHandler, Request, urlopen, install_opener, build_opener, \ - HTTPError, URLError + from urllib2 import ( + HTTPHandler, + HTTPSHandler, + Request, + urlopen, + install_opener, + build_opener, + HTTPError, + URLError, + ) import urlparse logger = logging.getLogger(__name__) -class Pepper(object): - ''' +class Pepper: + """ A thin wrapper for making HTTP calls to the salt-api rest_cherrpy REST interface @@ -56,9 +70,10 @@ class Pepper(object): u'ms-3': True, u'ms-4': True}]} - ''' - def __init__(self, api_url='https://localhost:8000', debug_http=False, ignore_ssl_errors=False): - ''' + """ + + def __init__(self, api_url="https://localhost:8000", debug_http=False, ignore_ssl_errors=False): + """ Initialize the class with the URL of the API :param api_url: Host or IP address of the salt-api URL; @@ -70,11 +85,10 @@ def __init__(self, api_url='https://localhost:8000', debug_http=False, ignore_ss :raises PepperException: if the api_url is misformed - ''' + """ split = urlparse.urlsplit(api_url) - if split.scheme not in ['http', 'https']: - raise PepperException("salt-api URL missing HTTP(s) protocol: {0}" - .format(api_url)) + if split.scheme not in ["http", "https"]: + raise PepperException("salt-api URL missing HTTP(s) protocol: {}".format(api_url)) self.api_url = api_url self.debug_http = int(debug_http) @@ -83,7 +97,7 @@ def __init__(self, api_url='https://localhost:8000', debug_http=False, ignore_ss self.salt_version = None def req_stream(self, path): - ''' + """ A thin wrapper to get a response from saltstack api. The body of the response will not be downloaded immediately. Make sure to close the connection after use. @@ -96,37 +110,38 @@ def req_stream(self, path): :return: :class:`Response ` object :rtype: requests.Response - ''' + """ import requests headers = { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'X-Requested-With': 'XMLHttpRequest', + "Accept": "application/json", + "Content-Type": "application/json", + "X-Requested-With": "XMLHttpRequest", } - if self.auth and 'token' in self.auth and self.auth['token']: - headers.setdefault('X-Auth-Token', self.auth['token']) + if self.auth and "token" in self.auth and self.auth["token"]: + headers.setdefault("X-Auth-Token", self.auth["token"]) else: - raise PepperException('Authentication required') + raise PepperException("Authentication required") return - params = {'url': self._construct_url(path), - 'headers': headers, - 'verify': self._ssl_verify is True, - 'stream': True - } + params = { + "url": self._construct_url(path), + "headers": headers, + "verify": self._ssl_verify is True, + "stream": True, + } try: resp = requests.get(**params) if resp.status_code == 401: - raise PepperException(str(resp.status_code) + ':Authentication denied') + raise PepperException(str(resp.status_code) + ":Authentication denied") return if resp.status_code == 500: - raise PepperException(str(resp.status_code) + ':Server error.') + raise PepperException(str(resp.status_code) + ":Server error.") return if resp.status_code == 404: - raise PepperException(str(resp.status_code) + ' :This request returns nothing.') + raise PepperException(str(resp.status_code) + " :This request returns nothing.") return except PepperException as e: print(e) @@ -134,41 +149,42 @@ def req_stream(self, path): return resp def req_get(self, path): - ''' + """ A thin wrapper from get http method of saltstack api api = Pepper('http://ipaddress/api/') print(api.login('salt','salt','pam')) print(api.req_get('/keys')) - ''' + """ import requests headers = { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'X-Requested-With': 'XMLHttpRequest', + "Accept": "application/json", + "Content-Type": "application/json", + "X-Requested-With": "XMLHttpRequest", } - if self.auth and 'token' in self.auth and self.auth['token']: - headers.setdefault('X-Auth-Token', self.auth['token']) + if self.auth and "token" in self.auth and self.auth["token"]: + headers.setdefault("X-Auth-Token", self.auth["token"]) else: - raise PepperException('Authentication required') + raise PepperException("Authentication required") return - params = {'url': self._construct_url(path), - 'headers': headers, - 'verify': self._ssl_verify is True, - } + params = { + "url": self._construct_url(path), + "headers": headers, + "verify": self._ssl_verify is True, + } try: resp = requests.get(**params) if resp.status_code == 401: - raise PepperException(str(resp.status_code) + ':Authentication denied') + raise PepperException(str(resp.status_code) + ":Authentication denied") return if resp.status_code == 500: - raise PepperException(str(resp.status_code) + ':Server error.') + raise PepperException(str(resp.status_code) + ":Server error.") return if resp.status_code == 404: - raise PepperException(str(resp.status_code) + ' :This request returns nothing.') + raise PepperException(str(resp.status_code) + " :This request returns nothing.") return except PepperException as e: print(e) @@ -176,7 +192,7 @@ def req_get(self, path): return resp.json() def req(self, path, data=None): - ''' + """ A thin wrapper around urllib2 to send requests and return the response If the current instance contains an authentication token it will be @@ -184,15 +200,16 @@ def req(self, path, data=None): :rtype: dictionary - ''' - if ((hasattr(data, 'get') and data.get('eauth') == 'kerberos') - or self.auth.get('eauth') == 'kerberos'): + """ + if (hasattr(data, "get") and data.get("eauth") == "kerberos") or self.auth.get( + "eauth" + ) == "kerberos": return self.req_requests(path, data) headers = { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'X-Requested-With': 'XMLHttpRequest', + "Accept": "application/json", + "Content-Type": "application/json", + "X-Requested-With": "XMLHttpRequest", } opener = build_opener() @@ -216,11 +233,11 @@ def req(self, path, data=None): # Add POST data to request if data is not None: - req.add_header('Content-Length', clen) + req.add_header("Content-Length", clen) # Add auth header to request - if path != '/run' and self.auth and 'token' in self.auth and self.auth['token']: - req.add_header('X-Auth-Token', self.auth['token']) + if path != "/run" and self.auth and "token" in self.auth and self.auth["token"]: + req.add_header("X-Auth-Token", self.auth["token"]) # Send request try: @@ -229,34 +246,34 @@ def req(self, path, data=None): f = urlopen(req, context=con) else: f = urlopen(req) - content = f.read().decode('utf-8') - if (self.debug_http): - logger.debug('Response: %s', content) + content = f.read().decode("utf-8") + if self.debug_http: + logger.debug("Response: %s", content) ret = json.loads(content) - if not self.salt_version and 'x-salt-version' in f.headers: - self._parse_salt_version(f.headers['x-salt-version']) + if not self.salt_version and "x-salt-version" in f.headers: + self._parse_salt_version(f.headers["x-salt-version"]) except (HTTPError, URLError) as exc: - logger.debug('Error with request', exc_info=True) - status = getattr(exc, 'code', None) + logger.debug("Error with request", exc_info=True) + status = getattr(exc, "code", None) if status == 401: - raise PepperException('Authentication denied') + raise PepperException("Authentication denied") if status == 500: - raise PepperException('Server error.') + raise PepperException("Server error.") - logger.error('Error with request: {0}'.format(exc)) + logger.error("Error with request: {}".format(exc)) raise except AttributeError: - logger.debug('Error converting response from JSON', exc_info=True) - raise PepperException('Unable to parse the server response.') + logger.debug("Error converting response from JSON", exc_info=True) + raise PepperException("Unable to parse the server response.") return ret def req_requests(self, path, data=None): - ''' + """ A thin wrapper around request and request_kerberos to send requests and return the response @@ -265,181 +282,180 @@ def req_requests(self, path, data=None): :rtype: dictionary - ''' + """ import requests from requests_gssapi import HTTPSPNEGOAuth, OPTIONAL + auth = HTTPSPNEGOAuth(mutual_authentication=OPTIONAL) headers = { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'X-Requested-With': 'XMLHttpRequest', + "Accept": "application/json", + "Content-Type": "application/json", + "X-Requested-With": "XMLHttpRequest", } - if self.auth and 'token' in self.auth and self.auth['token']: - headers.setdefault('X-Auth-Token', self.auth['token']) + if self.auth and "token" in self.auth and self.auth["token"]: + headers.setdefault("X-Auth-Token", self.auth["token"]) # Optionally toggle SSL verification - params = {'url': self._construct_url(path), - 'headers': headers, - 'verify': self._ssl_verify is True, - 'auth': auth, - 'data': json.dumps(data), - } - logger.debug('postdata {0}'.format(params)) + params = { + "url": self._construct_url(path), + "headers": headers, + "verify": self._ssl_verify is True, + "auth": auth, + "data": json.dumps(data), + } + logger.debug("postdata {}".format(params)) resp = requests.post(**params) if resp.status_code == 401: # TODO should be resp.raise_from_status - raise PepperException('Authentication denied') + raise PepperException("Authentication denied") if resp.status_code == 500: # TODO should be resp.raise_from_status - raise PepperException('Server error.') + raise PepperException("Server error.") - if not self.salt_version and 'x-salt-version' in resp.headers: - self._parse_salt_version(resp.headers['x-salt-version']) + if not self.salt_version and "x-salt-version" in resp.headers: + self._parse_salt_version(resp.headers["x-salt-version"]) return resp.json() - def low(self, lowstate, path='/'): - ''' + def low(self, lowstate, path="/"): + """ Execute a command through salt-api and return the response :param string path: URL path to be joined with the API hostname :param list lowstate: a list of lowstate dictionaries - ''' + """ return self.req(path, lowstate) - def local(self, tgt, fun, arg=None, kwarg=None, expr_form='glob', - timeout=None, ret=None): - ''' + def local(self, tgt, fun, arg=None, kwarg=None, expr_form="glob", timeout=None, ret=None): + """ Run a single command using the ``local`` client Wraps :meth:`low`. - ''' + """ low = { - 'client': 'local', - 'tgt': tgt, - 'fun': fun, + "client": "local", + "tgt": tgt, + "fun": fun, } if arg: - low['arg'] = arg + low["arg"] = arg if kwarg: - low['kwarg'] = kwarg + low["kwarg"] = kwarg if expr_form: - low['expr_form'] = expr_form + low["expr_form"] = expr_form if timeout: - low['timeout'] = timeout + low["timeout"] = timeout if ret: - low['ret'] = ret + low["ret"] = ret return self.low([low]) - def local_async(self, tgt, fun, arg=None, kwarg=None, expr_form='glob', - timeout=None, ret=None): - ''' + def local_async(self, tgt, fun, arg=None, kwarg=None, expr_form="glob", timeout=None, ret=None): + """ Run a single command using the ``local_async`` client Wraps :meth:`low`. - ''' + """ low = { - 'client': 'local_async', - 'tgt': tgt, - 'fun': fun, + "client": "local_async", + "tgt": tgt, + "fun": fun, } if arg: - low['arg'] = arg + low["arg"] = arg if kwarg: - low['kwarg'] = kwarg + low["kwarg"] = kwarg if expr_form: - low['expr_form'] = expr_form + low["expr_form"] = expr_form if timeout: - low['timeout'] = timeout + low["timeout"] = timeout if ret: - low['ret'] = ret + low["ret"] = ret return self.low([low]) - def local_batch(self, tgt, fun, arg=None, kwarg=None, expr_form='glob', - batch='50%', ret=None): - ''' + def local_batch(self, tgt, fun, arg=None, kwarg=None, expr_form="glob", batch="50%", ret=None): + """ Run a single command using the ``local_batch`` client Wraps :meth:`low`. - ''' + """ low = { - 'client': 'local_batch', - 'tgt': tgt, - 'fun': fun, + "client": "local_batch", + "tgt": tgt, + "fun": fun, } if arg: - low['arg'] = arg + low["arg"] = arg if kwarg: - low['kwarg'] = kwarg + low["kwarg"] = kwarg if expr_form: - low['expr_form'] = expr_form + low["expr_form"] = expr_form if batch: - low['batch'] = batch + low["batch"] = batch if ret: - low['ret'] = ret + low["ret"] = ret return self.low([low]) def lookup_jid(self, jid): - ''' + """ Get job results Wraps :meth:`runner`. - ''' + """ - return self.runner('jobs.lookup_jid', jid='{0}'.format(jid)) + return self.runner("jobs.lookup_jid", jid="{}".format(jid)) def runner(self, fun, arg=None, **kwargs): - ''' + """ Run a single command using the ``runner`` client Usage:: runner('jobs.lookup_jid', jid=12345) - ''' + """ low = { - 'client': 'runner', - 'fun': fun, + "client": "runner", + "fun": fun, } if arg: - low['arg'] = arg + low["arg"] = arg low.update(kwargs) return self.low([low]) def wheel(self, fun, arg=None, kwarg=None, **kwargs): - ''' + """ Run a single command using the ``wheel`` client Usage:: wheel('key.accept', match='myminion') - ''' + """ low = { - 'client': 'wheel', - 'fun': fun, + "client": "wheel", + "fun": fun, } if arg: - low['arg'] = arg + low["arg"] = arg if kwarg: - low['kwarg'] = kwarg + low["kwarg"] = kwarg low.update(kwargs) @@ -449,34 +465,32 @@ def _send_auth(self, path, **kwargs): return self.req(path, kwargs) def login(self, username=None, password=None, eauth=None, **kwargs): - ''' + """ Authenticate with salt-api and return the user permissions and authentication token or an empty dict - ''' + """ local = locals() kwargs.update( - dict( - (key, local[key]) for key in ( - 'username', - 'password', - 'eauth' - ) if local.get(key, None) is not None - ) + { + key: local[key] + for key in ("username", "password", "eauth") + if local.get(key, None) is not None + } ) - self.auth = self._send_auth('/login', **kwargs).get('return', [{}])[0] + self.auth = self._send_auth("/login", **kwargs).get("return", [{}])[0] return self.auth def token(self, **kwargs): - ''' + """ Get an eauth token from Salt for use with the /run URL - ''' - self.auth = self._send_auth('/token', **kwargs)[0] + """ + self.auth = self._send_auth("/token", **kwargs)[0] return self.auth def _construct_url(self, path): - ''' + """ Construct the url to salt-api for the given path Args: @@ -485,20 +499,20 @@ def _construct_url(self, path): >>> api = Pepper('https://localhost:8000/salt-api/') >>> api._construct_url('/login') 'https://localhost:8000/salt-api/login' - ''' + """ - relative_path = path.lstrip('/') + relative_path = path.lstrip("/") return urlparse.urljoin(self.api_url, relative_path) def _parse_salt_version(self, version): # borrow from salt.version git_describe_regex = re.compile( - r'(?:[^\d]+)?(?P[\d]{1,4})' - r'\.(?P[\d]{1,2})' - r'(?:\.(?P[\d]{0,2}))?' - r'(?:\.(?P[\d]{0,2}))?' - r'(?:(?Prc|a|b|alpha|beta|nb)(?P[\d]{1}))?' - r'(?:(?:.*)-(?P(?:[\d]+|n/a))-(?P[a-z0-9]{8}))?' + r"(?:[^\d]+)?(?P[\d]{1,4})" + r"\.(?P[\d]{1,2})" + r"(?:\.(?P[\d]{0,2}))?" + r"(?:\.(?P[\d]{0,2}))?" + r"(?:(?Prc|a|b|alpha|beta|nb)(?P[\d]{1}))?" + r"(?:(?:.*)-(?P(?:[\d]+|n/a))-(?P[a-z0-9]{8}))?" ) match = git_describe_regex.match(version) if match: diff --git a/pepper/retcode.py b/pepper/retcode.py index 11b254c..d8ce50e 100644 --- a/pepper/retcode.py +++ b/pepper/retcode.py @@ -1,16 +1,16 @@ -''' +""" A retcode validator -''' +""" -class PepperRetcode(object): - ''' +class PepperRetcode: + """ Validation container - ''' + """ def validate(self, options, result): - ''' + """ Validate result dictionary retcode values. :param options: optparse options @@ -18,7 +18,7 @@ def validate(self, options, result): :param result: dictionary from Saltstack master :return: exit code - ''' + """ if options.fail_any: return self.validate_fail_any(result) if options.fail_any_none: @@ -31,7 +31,7 @@ def validate(self, options, result): @staticmethod def validate_fail_any(result): - ''' + """ Validate result dictionary retcode values. Returns 0 if no retcode keys. Returns first non zero retcode if any of recodes is non zero. @@ -39,19 +39,21 @@ def validate_fail_any(result): :param result: dictionary from Saltstack master :return: exit code - ''' + """ if isinstance(result, list): if isinstance(result[0], dict): minion = result[0] - retcodes = list(minion[name].get('retcode') - for name in minion if isinstance(minion[name], dict) and - minion[name].get('retcode') is not None) + retcodes = list( + minion[name].get("retcode") + for name in minion + if isinstance(minion[name], dict) and minion[name].get("retcode") is not None + ) return next((r for r in retcodes if r != 0), 0) return 0 @staticmethod def validate_fail_any_none(result): - ''' + """ Validate result dictionary retcode values. Returns -1 if no retcode keys. Returns first non zero retcode if any of recodes is non zero. @@ -59,13 +61,15 @@ def validate_fail_any_none(result): :param result: dictionary from Saltstack master :return: exit code - ''' + """ if isinstance(result, list): if isinstance(result[0], dict): minion = result[0] - retcodes = list(minion[name].get('retcode') - for name in minion if isinstance(minion[name], dict) and - minion[name].get('retcode') is not None) + retcodes = list( + minion[name].get("retcode") + for name in minion + if isinstance(minion[name], dict) and minion[name].get("retcode") is not None + ) if not retcodes: return -1 # there are no retcodes return next((r for r in retcodes if r != 0), 0) @@ -73,7 +77,7 @@ def validate_fail_any_none(result): @staticmethod def validate_fail_all(result): - ''' + """ Validate result dictionary retcode values. Returns 0 if no retcode keys. Returns first non zero retcode if all recodes are non zero. @@ -81,20 +85,22 @@ def validate_fail_all(result): :param result: dictionary from Saltstack master :return: exit code - ''' + """ if isinstance(result, list): if isinstance(result[0], dict): minion = result[0] - retcodes = list(minion[name].get('retcode') - for name in minion if isinstance(minion[name], dict) and - minion[name].get('retcode') is not None) + retcodes = list( + minion[name].get("retcode") + for name in minion + if isinstance(minion[name], dict) and minion[name].get("retcode") is not None + ) if all(r != 0 for r in retcodes): return next((r for r in retcodes if r != 0), 0) return 0 @staticmethod def validate_fail_all_none(result): - ''' + """ Validate result dictionary retcode values. Returns -1 if no retcode keys. Returns first non zero retcode if all recodes are non zero. @@ -102,13 +108,15 @@ def validate_fail_all_none(result): :param result: dictionary from Saltstack master :return: exit code - ''' + """ if isinstance(result, list): if isinstance(result[0], dict): minion = result[0] - retcodes = list(minion[name].get('retcode') - for name in minion if isinstance(minion[name], dict) and - minion[name].get('retcode') is not None) + retcodes = list( + minion[name].get("retcode") + for name in minion + if isinstance(minion[name], dict) and minion[name].get("retcode") is not None + ) if not retcodes: return -1 # there are no retcodes if all(r != 0 for r in retcodes): diff --git a/pepper/script.py b/pepper/script.py index 21afe63..0429385 100755 --- a/pepper/script.py +++ b/pepper/script.py @@ -1,25 +1,22 @@ #!/usr/bin/env python -''' +""" A CLI interface to a remote salt-api instance -''' -from __future__ import print_function - -import sys +""" import json import logging +import sys from pepper.cli import PepperCli +from pepper.exceptions import PepperArgumentsException +from pepper.exceptions import PepperAuthException +from pepper.exceptions import PepperException from pepper.retcode import PepperRetcode -from pepper.exceptions import ( - PepperException, - PepperAuthException, - PepperArgumentsException, -) try: import salt.loader import salt.config import salt.output + HAS_SALT = True except ImportError: HAS_SALT = False @@ -27,7 +24,7 @@ logger = logging.getLogger(__name__) -class Pepper(object): +class Pepper: def __init__(self): self.cli = PepperCli() if HAS_SALT: @@ -35,81 +32,83 @@ def __init__(self): else: self.opts = {} if self.cli.options.output_file is not None: - self.opts['output_file'] = self.cli.options.output_file + self.opts["output_file"] = self.cli.options.output_file @property def output(self): - if not hasattr(self, 'modules'): + if not hasattr(self, "modules"): self.modules = salt.loader.minion_mods(self.opts) try: oput = self.modules[self.cli.args[1]].__outputter__ except (KeyError, AttributeError, TypeError): - oput = 'nested' + oput = "nested" return oput def __call__(self): try: for exit_code, result in self.cli.run(): if HAS_SALT and self.opts: - logger.debug('Use Salt outputters') + logger.debug("Use Salt outputters") result = json.loads(result) # unwrap ret in some cases - if 'return' in result: - result = result['return'] + if "return" in result: + result = result["return"] for ret in result: if isinstance(ret, dict): - if self.cli.options.client.startswith('local'): + if self.cli.options.client.startswith("local"): for minionid, minionret in ret.items(): # rest_tornado doesnt return full_return directly # it will always be from get_event, so the output differs slightly - if isinstance(minionret, dict) and 'return' in minionret: + if isinstance(minionret, dict) and "return" in minionret: # version >= 2017.7 salt.output.display_output( - {minionid: minionret['return']}, - self.cli.options.output or minionret.get('out', None) or 'nested', - opts=self.opts + {minionid: minionret["return"]}, + self.cli.options.output + or minionret.get("out", None) + or "nested", + opts=self.opts, ) # cherrypy returns with ret via full_return - elif isinstance(minionret, dict) and 'ret' in minionret: + elif isinstance(minionret, dict) and "ret" in minionret: # version >= 2017.7 salt.output.display_output( - {minionid: minionret['ret']}, - self.cli.options.output or minionret.get('out', None) or 'nested', - opts=self.opts + {minionid: minionret["ret"]}, + self.cli.options.output + or minionret.get("out", None) + or "nested", + opts=self.opts, ) else: salt.output.display_output( {minionid: minionret}, self.cli.options.output or self.output, - opts=self.opts + opts=self.opts, ) - elif 'data' in ret: + elif "data" in ret: # unfold runners - outputter = ret.get('outputter', 'nested') - if isinstance(ret['data'], dict) and 'return' in ret['data']: - ret = ret['data']['return'] + outputter = ret.get("outputter", "nested") + if isinstance(ret["data"], dict) and "return" in ret["data"]: + ret = ret["data"]["return"] salt.output.display_output( - ret, - self.cli.options.output or outputter, - opts=self.opts + ret, self.cli.options.output or outputter, opts=self.opts ) else: salt.output.display_output( {self.cli.options.client: ret}, - self.cli.options.output or ret.get('outputter', 'nested'), - opts=self.opts + self.cli.options.output or ret.get("outputter", "nested"), + opts=self.opts, ) else: salt.output.display_output( {self.cli.options.client: ret}, - self.cli.options.output or 'nested', + self.cli.options.output or "nested", opts=self.opts, ) else: if self.cli.options.output_file is not None: - with open(self.cli.options.output_file, 'a') as ofile: + with open(self.cli.options.output_file, "a") as ofile: print(result, file=ofile) else: print(result) @@ -118,13 +117,16 @@ def __call__(self): return PepperRetcode().validate(self.cli.options, result) return exit_code except (PepperException, PepperAuthException, PepperArgumentsException) as exc: - print('Pepper error: {0}'.format(exc), file=sys.stderr) + print("Pepper error: {}".format(exc), file=sys.stderr) return 1 except KeyboardInterrupt: # TODO: mimic CLI and output JID on ctrl-c return 0 except Exception as e: print(e) - print('Uncaught Pepper error (increase verbosity for the full traceback).', file=sys.stderr) - logger.debug('Uncaught traceback:', exc_info=True) + print( + "Uncaught Pepper error (increase verbosity for the full traceback).", + file=sys.stderr, + ) + logger.debug("Uncaught traceback:", exc_info=True) return 1 diff --git a/setup.py b/setup.py index c6d645b..9ccbd42 100644 --- a/setup.py +++ b/setup.py @@ -1,51 +1,51 @@ #!/usr/bin/env python -''' +""" A CLI front-end to a running salt-api system -''' +""" import setuptools -with open("README.rst", "r") as fh: +with open("README.rst") as fh: long_description = fh.read() setup_kwargs = { - 'name': 'salt-pepper', - 'description': __doc__.strip(), - 'author': 'Seth House', - 'author_email': 'shouse@saltstack.com', - 'url': 'http://saltstack.com', - 'long_description': long_description, - 'long_description_content_type': "text/x-rst", - 'use_scm_version': True, - 'setup_requires': ['setuptools_scm'], - 'classifiers': [ - 'Programming Language :: Python', - 'Programming Language :: Cython', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: Information Technology', - 'Intended Audience :: System Administrators', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: POSIX :: Linux', - 'Topic :: System :: Clustering', - 'Topic :: System :: Distributed Computing', + "name": "salt-pepper", + "description": __doc__.strip(), + "author": "Seth House", + "author_email": "shouse@saltstack.com", + "url": "http://saltstack.com", + "long_description": long_description, + "long_description_content_type": "text/x-rst", + "use_scm_version": True, + "setup_requires": ["setuptools_scm"], + "classifiers": [ + "Programming Language :: Python", + "Programming Language :: Cython", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "Intended Audience :: System Administrators", + "License :: OSI Approved :: Apache Software License", + "Operating System :: POSIX :: Linux", + "Topic :: System :: Clustering", + "Topic :: System :: Distributed Computing", ], - 'packages': [ - 'pepper', + "packages": [ + "pepper", ], - 'extras_require': { - 'kerberos': ["requests-gssapi>=1.1.0"], + "extras_require": { + "kerberos": ["requests-gssapi>=1.1.0"], }, - 'scripts': [ - 'scripts/pepper', - ] + "scripts": [ + "scripts/pepper", + ], } -if __name__ == '__main__': +if __name__ == "__main__": setuptools.setup(**setup_kwargs) diff --git a/tests/__init__.py b/tests/__init__.py index 40a96af..e69de29 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/tests/conftest.py b/tests/conftest.py index 5cf86aa..9237cc9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals, print_function - # Import python libraries import logging import os.path @@ -9,30 +6,30 @@ import tempfile import textwrap -# Import Salt Libraries -import salt.utils.yaml as yaml - -# Import pytest libraries import pytest +import salt.utils.yaml as yaml from pytestskipmarkers.utils import ports -from saltfactories.utils import random_string, running_username +from saltfactories.utils import random_string +from saltfactories.utils import running_username -# Import Pepper libraries -import pepper import pepper.script +# Import Salt Libraries +# Import pytest libraries +# Import Pepper libraries + log = logging.getLogger(__name__) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def sshd_config_dir(salt_factories): config_dir = salt_factories.get_root_dir_for_daemon("sshd") yield config_dir shutil.rmtree(str(config_dir), ignore_errors=True) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_sshd_server(salt_factories, sshd_config_dir, session_master): sshd_config_dict = { "Protocol": "2", @@ -71,7 +68,7 @@ def session_sshd_server(salt_factories, sshd_config_dir, session_master): yield factory -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_ssh_roster_config(session_sshd_server, session_master): roster_contents = """ localhost: @@ -82,9 +79,7 @@ def session_ssh_roster_config(session_sshd_server, session_master): mine_functions: test.arg: ['itworked'] """.format( - session_sshd_server.listen_port, - running_username(), - session_sshd_server.client_key + session_sshd_server.listen_port, running_username(), session_sshd_server.client_key ) with pytest.helpers.temp_file( "roster", roster_contents, session_master.config_dir @@ -92,17 +87,18 @@ def session_ssh_roster_config(session_sshd_server, session_master): yield roster_file -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def salt_api_port(): - ''' + """ Returns an unused localhost port for the api port - ''' + """ return ports.get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def pepperconfig(salt_api_port): - config = textwrap.dedent(''' + config = textwrap.dedent( + """ [main] SALTAPI_URL=http://localhost:{0}/ SALTAPI_USER=pepper @@ -126,121 +122,128 @@ def pepperconfig(salt_api_port): SALTAPI_EAUTH=sharedsecret [noopts] SALTAPI_URL=http://localhost:{0}/ - '''.format(salt_api_port)) - with open('tests/.pepperrc', 'w') as pepper_file: + """.format( + salt_api_port + ) + ) + with open("tests/.pepperrc", "w") as pepper_file: print(config, file=pepper_file) yield - os.remove('tests/.pepperrc') + os.remove("tests/.pepperrc") @pytest.fixture def pepper_client(session_salt_api, salt_api_port): - client = pepper.Pepper('http://localhost:{0}'.format(salt_api_port)) - client.login('pepper', 'pepper', 'sharedsecret') + client = pepper.Pepper("http://localhost:{}".format(salt_api_port)) + client.login("pepper", "pepper", "sharedsecret") return client @pytest.fixture def tokfile(): tokdir = tempfile.mkdtemp() - yield os.path.join(tokdir, 'peppertok.json') + yield os.path.join(tokdir, "peppertok.json") shutil.rmtree(tokdir) @pytest.fixture def output_file(): - ''' + """ Returns the path to the salt master configuration file - ''' + """ out_dir = tempfile.mkdtemp() - yield os.path.join(out_dir, 'output') + yield os.path.join(out_dir, "output") shutil.rmtree(out_dir) -@pytest.fixture(params=['/run', '/login']) +@pytest.fixture(params=["/run", "/login"]) def pepper_cli(request, session_salt_api, salt_api_port, output_file, session_sshd_server): - ''' + """ Wrapper to invoke Pepper with common params and inside an empty env - ''' - if request.config.getoption('--salt-api-backend') == 'rest_tornado' and request.param == '/run': + """ + if request.config.getoption("--salt-api-backend") == "rest_tornado" and request.param == "/run": pytest.xfail("rest_tornado does not support /run endpoint until next release") def_args = [ - '--out=json', - '--output-file={0}'.format(output_file), - '-c', 'tests/.pepperrc', + "--out=json", + "--output-file={}".format(output_file), + "-c", + "tests/.pepperrc", ] - if request.param == '/run': - def_args = ['--run-uri'] + def_args + if request.param == "/run": + def_args = ["--run-uri"] + def_args def _run_pepper_cli(*args, **kwargs): - sys.argv = ['pepper', '-p', kwargs.pop('profile', 'main')] + def_args + list(args) + sys.argv = ["pepper", "-p", kwargs.pop("profile", "main")] + def_args + list(args) exitcode = pepper.script.Pepper()() try: - with open(output_file, 'r') as result: + with open(output_file) as result: try: return yaml.load(result) except yaml.parser.ParserError: result.seek(0) - return [yaml.load('{0}}}'.format(ret).strip('"')) for ret in result.read().split('}"\n') if ret] + return [ + yaml.load("{0}}}".format(ret).strip('"')) + for ret in result.read().split('}"\n') + if ret + ] except Exception as exc: - log.error('ExitCode %s: %s', exitcode, exc) + log.error("ExitCode %s: %s", exitcode, exc) return exitcode return _run_pepper_cli -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_master_factory(request, salt_factories, session_master_config_overrides): return salt_factories.salt_master_daemon( - random_string("master-"), - overrides=session_master_config_overrides + random_string("master-"), overrides=session_master_config_overrides ) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_master(session_master_factory): with session_master_factory.started(): yield session_master_factory -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_master_config_overrides(request, salt_api_port, salt_api_backend): return { salt_api_backend: { - 'port': salt_api_port, - 'disable_ssl': True, + "port": salt_api_port, + "disable_ssl": True, }, - 'external_auth': { - 'sharedsecret': { - 'pepper': [ - '.*', - '@jobs', - '@wheel', - '@runner', + "external_auth": { + "sharedsecret": { + "pepper": [ + ".*", + "@jobs", + "@wheel", + "@runner", ], }, }, - 'sharedsecret': 'pepper', - 'token_expire': 94670856, - 'ignore_host_keys': True, - 'ssh_wipe': True, - 'netapi_enable_clients': [ - 'local', - 'local_async', - 'local_subset', - 'ssh', - 'runner', - 'runner_async', - 'wheel', - 'wheel_async', - 'run' - ] + "sharedsecret": "pepper", + "token_expire": 94670856, + "ignore_host_keys": True, + "ssh_wipe": True, + "netapi_enable_clients": [ + "local", + "local_async", + "local_subset", + "ssh", + "runner", + "runner_async", + "wheel", + "wheel_async", + "run", + ], } -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_minion_factory(session_master_factory): """Return a factory for a randomly named minion connected to master.""" minion_factory = session_master_factory.salt_minion_daemon(random_string("minion-")) @@ -250,40 +253,40 @@ def session_minion_factory(session_master_factory): return minion_factory -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_minion(session_master, session_minion_factory): # noqa assert session_master.is_running() with session_minion_factory.started(): yield session_minion_factory -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_minion_id(session_minion): return session_minion.id -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def salt_api_backend(request): - ''' + """ Return the salt-api backend (cherrypy or tornado) - ''' - backend = request.config.getoption('--salt-api-backend') + """ + backend = request.config.getoption("--salt-api-backend") if backend is not None: return backend - backend = request.config.getini('salt_api_backend') + backend = request.config.getini("salt_api_backend") if backend is not None: return backend - return 'rest_cherrypy' + return "rest_cherrypy" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_salt_api_factory(session_master_factory): return session_master_factory.salt_api_daemon() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_salt_api(session_master, session_salt_api_factory): assert session_master.is_running() with session_salt_api_factory.started(): @@ -292,8 +295,8 @@ def session_salt_api(session_master, session_salt_api_factory): def pytest_addoption(parser): parser.addoption( - '--salt-api-backend', - action='store', - default='rest_cherrypy', - help='which backend to use for salt-api, must be one of rest_cherrypy or rest_tornado', - ) + "--salt-api-backend", + action="store", + default="rest_cherrypy", + help="which backend to use for salt-api, must be one of rest_cherrypy or rest_tornado", + ) diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index 40a96af..e69de29 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/tests/integration/test_clients.py b/tests/integration/test_clients.py index 2932df6..6a00be3 100644 --- a/tests/integration/test_clients.py +++ b/tests/integration/test_clients.py @@ -1,29 +1,32 @@ -# -*- coding: utf-8 -*- import json import pathlib + import pytest def test_local_bad_opts(pepper_cli): with pytest.raises(SystemExit): - pepper_cli('*') + pepper_cli("*") with pytest.raises(SystemExit): - pepper_cli('test.ping') + pepper_cli("test.ping") with pytest.raises(SystemExit): - pepper_cli('--client=ssh', 'test.ping') + pepper_cli("--client=ssh", "test.ping") with pytest.raises(SystemExit): - pepper_cli('--client=ssh', '*') + pepper_cli("--client=ssh", "*") @pytest.mark.xfail( 'config.getoption("--salt-api-backend") == "rest_tornado"', - reason="timeout kwarg isnt popped until next version of salt/tornado" + reason="timeout kwarg isnt popped until next version of salt/tornado", ) def test_runner_client(pepper_cli): ret = pepper_cli( - '--timeout=123', '--client=runner', 'test.arg', - 'one', 'two=what', - 'three={0}'.format(json.dumps({"hello": "world"})), + "--timeout=123", + "--client=runner", + "test.arg", + "one", + "two=what", + "three={}".format(json.dumps({"hello": "world"})), ) assert ret == {"args": ["one"], "kwargs": {"three": {"hello": "world"}, "two": "what"}} @@ -33,7 +36,7 @@ def test_runner_client(pepper_cli): reason="wheelClient unimplemented for now on tornado", ) def test_wheel_client_arg(pepper_cli, session_minion): - ret = pepper_cli('--client=wheel', 'minions.connected') + ret = pepper_cli("--client=wheel", "minions.connected") assert ret == [session_minion.id] @@ -43,13 +46,15 @@ def test_wheel_client_arg(pepper_cli, session_minion): ) def test_wheel_client_kwargs(pepper_cli, session_master): ret = pepper_cli( - '--client=wheel', 'config.update_config', 'file_name=pepper', - 'yaml_contents={0}'.format(json.dumps({"timeout": 5})), + "--client=wheel", + "config.update_config", + "file_name=pepper", + "yaml_contents={}".format(json.dumps({"timeout": 5})), ) - assert ret == 'Wrote pepper.conf' + assert ret == "Wrote pepper.conf" - default_include_dir = pathlib.Path(session_master.config['default_include']).parent - pepper_config = (pathlib.Path(session_master.config_dir) / default_include_dir / 'pepper.conf') + default_include_dir = pathlib.Path(session_master.config["default_include"]).parent + pepper_config = pathlib.Path(session_master.config_dir) / default_include_dir / "pepper.conf" assert pepper_config.exists @@ -58,10 +63,10 @@ def test_wheel_client_kwargs(pepper_cli, session_master): reason="sshClient unimplemented for now on tornado", ) def test_ssh_client(pepper_cli, session_ssh_roster_config): - ret = pepper_cli('--client=ssh', '*', 'test.ping') - assert ret['ssh']['localhost']['return'] is True + ret = pepper_cli("--client=ssh", "*", "test.ping") + assert ret["ssh"]["localhost"]["return"] is True def test_bad_client(pepper_cli): - ret = pepper_cli('--client=whatever') + ret = pepper_cli("--client=whatever") assert ret == 1 diff --git a/tests/integration/test_json.py b/tests/integration/test_json.py index c9c1bf7..c3fa4ce 100644 --- a/tests/integration/test_json.py +++ b/tests/integration/test_json.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import print_function - import os import shutil import tempfile @@ -8,40 +5,40 @@ def test_local_json(pepper_cli, session_minion_id): json = '[{"tgt": "*", "fun": "test.ping", "client": "local"}]' - ret = pepper_cli('--json', json) + ret = pepper_cli("--json", json) assert ret[session_minion_id] is True def test_local_json_bad(pepper_cli): - json = '{what}' - ret = pepper_cli('--json', json) + json = "{what}" + ret = pepper_cli("--json", json) assert ret == 1 def test_local_json_file(pepper_cli, session_minion_id): - tmpjson = os.path.join(tempfile.mkdtemp(), 'json') - with open(tmpjson, 'w') as tmpfile: + tmpjson = os.path.join(tempfile.mkdtemp(), "json") + with open(tmpjson, "w") as tmpfile: print( '[{"client": "local", "tgt": "*", "fun": "test.ping"}]', file=tmpfile, ) - ret = pepper_cli('--json-file', tmpjson) + ret = pepper_cli("--json-file", tmpjson) shutil.rmtree(os.path.dirname(tmpjson)) assert ret[session_minion_id] is True def test_local_json_file_bad(pepper_cli): - tmpjson = os.path.join(tempfile.mkdtemp(), 'json') - with open(tmpjson, 'w') as tmpfile: + tmpjson = os.path.join(tempfile.mkdtemp(), "json") + with open(tmpjson, "w") as tmpfile: print( - '{what}', + "{what}", file=tmpfile, ) - ret = pepper_cli('--json-file', tmpjson) + ret = pepper_cli("--json-file", tmpjson) shutil.rmtree(os.path.dirname(tmpjson)) assert ret == 1 def test_local_json_no_file(pepper_cli): - ret = pepper_cli('--json-file', '/tmp/wahteverfile') + ret = pepper_cli("--json-file", "/tmp/wahteverfile") assert ret == 1 diff --git a/tests/integration/test_local.py b/tests/integration/test_local.py index ba0c24c..4a66244 100644 --- a/tests/integration/test_local.py +++ b/tests/integration/test_local.py @@ -1,5 +1,2 @@ -# -*- coding: utf-8 -*- - - def test_local(pepper_client, session_minion_id): - assert pepper_client.local('*', 'test.ping')['return'][0][session_minion_id] is True + assert pepper_client.local("*", "test.ping")["return"][0][session_minion_id] is True diff --git a/tests/integration/test_login_opts.py b/tests/integration/test_login_opts.py index 894b93a..d9c4b86 100644 --- a/tests/integration/test_login_opts.py +++ b/tests/integration/test_login_opts.py @@ -1,58 +1,59 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import - - def test_cli_opts(pepper_cli, session_minion_id, salt_api_port): - '''Test the using a profile''' + """Test the using a profile""" ret = pepper_cli( - '--saltapi-url=http://localhost:{0}/'.format(salt_api_port), - '--eauth=sharedsecret', - '--username=pepper', - '--password=pepper', - '*', 'test.ping', - profile='noprofile', + "--saltapi-url=http://localhost:{}/".format(salt_api_port), + "--eauth=sharedsecret", + "--username=pepper", + "--password=pepper", + "*", + "test.ping", + profile="noprofile", ) assert ret[session_minion_id] is True def test_cli_opts_not_in_profile(pepper_cli, session_minion_id, salt_api_port): - '''Test the using a profile''' + """Test the using a profile""" ret = pepper_cli( - '--eauth=sharedsecret', - '--username=pepper', - '--password=pepper', - '*', 'test.ping', - profile='noopts', + "--eauth=sharedsecret", + "--username=pepper", + "--password=pepper", + "*", + "test.ping", + profile="noopts", ) assert ret[session_minion_id] is True def test_cli_api_not_in_profile(pepper_cli, session_minion_id, salt_api_port): - '''Test the using a profile''' + """Test the using a profile""" ret = pepper_cli( - '--saltapi-url=http://localhost:{0}/'.format(salt_api_port), - '*', 'test.ping', - profile='noapi', + "--saltapi-url=http://localhost:{}/".format(salt_api_port), + "*", + "test.ping", + profile="noapi", ) assert ret[session_minion_id] is True def test_no_username(pepper_cli, session_minion_id, salt_api_port): - '''Test the using a profile''' + """Test the using a profile""" ret = pepper_cli( - '--non-interactive', - '*', 'test.ping', - profile='noopts', + "--non-interactive", + "*", + "test.ping", + profile="noopts", ) assert ret == 1 def test_no_password(pepper_cli, session_minion_id, salt_api_port): - '''Test the using a profile''' + """Test the using a profile""" ret = pepper_cli( - '--username=pepper', - '--non-interactive', - '*', 'test.ping', - profile='noopts', + "--username=pepper", + "--non-interactive", + "*", + "test.ping", + profile="noopts", ) assert ret == 1 diff --git a/tests/integration/test_poller.py b/tests/integration/test_poller.py index 01a26dd..305d35c 100644 --- a/tests/integration/test_poller.py +++ b/tests/integration/test_poller.py @@ -1,21 +1,18 @@ -# -*- coding: utf-8 -*- - - def test_local_poll(pepper_cli, session_minion_id): - '''Test the returns poller for localclient''' - ret = pepper_cli('--fail-if-incomplete', '*', 'test.sleep', '1') + """Test the returns poller for localclient""" + ret = pepper_cli("--fail-if-incomplete", "*", "test.sleep", "1") assert ret[session_minion_id] is True assert len(ret) == 1 def test_local_poll_long(pepper_cli, session_minion_id): - '''Test the returns poller for localclient''' - ret = pepper_cli('--fail-if-incomplete', '*', 'test.sleep', '30') + """Test the returns poller for localclient""" + ret = pepper_cli("--fail-if-incomplete", "*", "test.sleep", "30") assert ret[session_minion_id] is True assert len(ret) == 1 def test_local_poll_timeout(pepper_cli, session_minion_id): - '''Test the returns poller for localclient''' - ret = pepper_cli('--timeout=5', '--fail-if-incomplete', '*', 'test.sleep', '30') - assert ret == {'Failed': [session_minion_id]} + """Test the returns poller for localclient""" + ret = pepper_cli("--timeout=5", "--fail-if-incomplete", "*", "test.sleep", "30") + assert ret == {"Failed": [session_minion_id]} diff --git a/tests/integration/test_profile.py b/tests/integration/test_profile.py index 289ae02..e9bfe17 100644 --- a/tests/integration/test_profile.py +++ b/tests/integration/test_profile.py @@ -1,7 +1,4 @@ -# -*- coding: utf-8 -*- - - def test_config_profile(pepper_cli, session_minion_id): - '''Test the using a profile''' - ret = pepper_cli('*', 'test.ping', profile='pepper') + """Test the using a profile""" + ret = pepper_cli("*", "test.ping", profile="pepper") assert ret[session_minion_id] is True diff --git a/tests/integration/test_token.py b/tests/integration/test_token.py index 13a78d6..9b8131d 100644 --- a/tests/integration/test_token.py +++ b/tests/integration/test_token.py @@ -1,33 +1,42 @@ -# -*- coding: utf-8 -*- import json import time def test_local_token(tokfile, pepper_cli, session_minion_id): - '''Test local execution with token file''' - ret = pepper_cli('-x', tokfile, '--make-token', '*', 'test.ping') + """Test local execution with token file""" + ret = pepper_cli("-x", tokfile, "--make-token", "*", "test.ping") assert ret[session_minion_id] is True def test_runner_token(tokfile, pepper_cli): - '''Test runner execution with token file''' - ret = pepper_cli('-x', tokfile, '--make-token', '--client', 'runner', 'test.metasyntactic') + """Test runner execution with token file""" + ret = pepper_cli("-x", tokfile, "--make-token", "--client", "runner", "test.metasyntactic") exps = [ - 'foo', 'bar', 'baz', 'qux', 'quux', 'quuz', 'corge', 'grault', - 'garply', 'waldo', 'fred', 'plugh', 'xyzzy', 'thud' + "foo", + "bar", + "baz", + "qux", + "quux", + "quuz", + "corge", + "grault", + "garply", + "waldo", + "fred", + "plugh", + "xyzzy", + "thud", ] assert all(exp in ret for exp in exps) def test_token_expire(tokfile, pepper_cli): - '''Test token override param''' + """Test token override param""" now = time.time() - pepper_cli('-x', tokfile, '--make-token', - '--token-expire', '94670856', - '*', 'test.ping') + pepper_cli("-x", tokfile, "--make-token", "--token-expire", "94670856", "*", "test.ping") - with open(tokfile, 'r') as tfile: + with open(tokfile) as tfile: token = json.load(tfile) - diff = (now + float(94670856)) - token['expire'] + diff = (now + float(94670856)) - token["expire"] # Allow for 10-second window between request and master-side auth. assert diff < 10 diff --git a/tests/integration/test_vanilla.py b/tests/integration/test_vanilla.py index 2864b5a..69bfa44 100644 --- a/tests/integration/test_vanilla.py +++ b/tests/integration/test_vanilla.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- import pytest def test_local(pepper_cli, session_minion_id): - '''Sanity-check: Has at least one minion - /run - /login query type is parameterized''' - ret = pepper_cli('*', 'test.ping') + """Sanity-check: Has at least one minion - /run - /login query type is parameterized""" + ret = pepper_cli("*", "test.ping") assert ret[session_minion_id] is True @@ -13,6 +12,6 @@ def test_local(pepper_cli, session_minion_id): reason="this is broken in rest_tornado until future release", ) def test_long_local(pepper_cli, session_minion_id): - '''Test a long call blocks until the return''' - ret = pepper_cli('--timeout=60', '*', 'test.sleep', '30') + """Test a long call blocks until the return""" + ret = pepper_cli("--timeout=60", "*", "test.sleep", "30") assert ret[session_minion_id] is True diff --git a/tests/requirements.txt b/tests/requirements.txt index b6a15e2..ded9362 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -4,7 +4,7 @@ pytest-rerunfailures pytest-cov pytest-salt-factories==0.912.2 CherryPy -setuptools_scm +setuptools_scm==7.1.0 importlib-metadata<5.0.0 pyzmq<=20.0.0 ; python_version < "3.6" pyzmq>=17.0.0 ; python_version < "3.9" diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index 40a96af..e69de29 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/tests/unit/test_init.py b/tests/unit/test_init.py index 5f67864..dee3976 100644 --- a/tests/unit/test_init.py +++ b/tests/unit/test_init.py @@ -1,20 +1,20 @@ -# -*- coding: utf-8 -*- # Import Python Libraries -from __future__ import print_function, unicode_literals, absolute_import import imp import os import shutil + import setuptools_scm -# Import Pepper Libraries import pepper +# Import Pepper Libraries + def test_no_setup(): - setuppath = os.path.join(os.path.dirname(pepper.__file__), os.pardir, 'setup.py') - shutil.move(setuppath, setuppath + '.bak') - ptest = imp.load_source('ptest', os.path.join(os.path.dirname(pepper.__file__), '__init__.py')) - shutil.move(setuppath + '.bak', setuppath) + setuppath = os.path.join(os.path.dirname(pepper.__file__), os.pardir, "setup.py") + shutil.move(setuppath, setuppath + ".bak") + ptest = imp.load_source("ptest", os.path.join(os.path.dirname(pepper.__file__), "__init__.py")) + shutil.move(setuppath + ".bak", setuppath) assert ptest.version == setuptools_scm.get_version() assert ptest.sha is None diff --git a/tests/unit/test_login_details.py b/tests/unit/test_login_details.py index b1ffca2..64e645e 100644 --- a/tests/unit/test_login_details.py +++ b/tests/unit/test_login_details.py @@ -1,24 +1,18 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import import sys +from unittest.mock import MagicMock +from unittest.mock import patch -# Import Pepper Libs import pepper.cli # Import Testing libraries -from mock import MagicMock, patch def test_interactive_logins(): - sys.argv = ['pepper', '-c', 'tests/.pepperrc', '-p', 'noopts'] + sys.argv = ["pepper", "-c", "tests/.pepperrc", "-p", "noopts"] - with patch( - 'pepper.cli.input', - MagicMock(return_value='pepper') - ), patch( - 'pepper.cli.getpass.getpass', - MagicMock(return_value='pepper') - ): + with patch("pepper.cli.input", MagicMock(return_value="pepper")), patch( + "pepper.cli.getpass.getpass", MagicMock(return_value="pepper") + ): result = pepper.cli.PepperCli().get_login_details() - assert result['SALTAPI_USER'] == 'pepper' - assert result['SALTAPI_PASS'] == 'pepper' + assert result["SALTAPI_USER"] == "pepper" + assert result["SALTAPI_PASS"] == "pepper" diff --git a/tests/unit/test_retcodes_error.py b/tests/unit/test_retcodes_error.py index 16e1d0c..f76d61f 100644 --- a/tests/unit/test_retcodes_error.py +++ b/tests/unit/test_retcodes_error.py @@ -1,16 +1,15 @@ -# -*- coding: utf-8 -*- # Import Python Libraries -from __future__ import absolute_import import sys -# Import Pepper Libraries +import pytest + import pepper +# Import Pepper Libraries # Import Testing Libraries -import pytest def test_fail_any(): - sys.argv = ['pepper', '--fail-all', '--fail-any', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all", "--fail-any", "minion_id", "request"] with pytest.raises(SystemExit): pepper.script.Pepper()() diff --git a/tests/unit/test_retcodes_fail_fail.py b/tests/unit/test_retcodes_fail_fail.py index 9f52230..fd60b37 100644 --- a/tests/unit/test_retcodes_fail_fail.py +++ b/tests/unit/test_retcodes_fail_fail.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- # Import Python Libraries -from __future__ import print_function, unicode_literals, absolute_import import sys +from unittest.mock import MagicMock +from unittest.mock import patch -# Import Pepper Libraries import pepper -from mock import patch, MagicMock +# Import Pepper Libraries PAYLOAD = { "return": [ @@ -14,53 +13,53 @@ "ezh.msk.ru": { "jid": "20180414193904158892", "ret": "/bin/sh: 123: command not found", - "retcode": 127 + "retcode": 127, }, "saltstack.ezh.msk.ru": { "jid": "20180414193904158892", "ret": "/bin/sh: 1: 123: not found", - "retcode": 127 - } + "retcode": 127, + }, } ] } -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_default(): - sys.argv = ['pepper', 'minion_id', 'request'] + sys.argv = ["pepper", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any(): - sys.argv = ['pepper', '--fail-any', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any_none(): - sys.argv = ['pepper', '--fail-any-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all(): - sys.argv = ['pepper', '--fail-all', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all_none(): - sys.argv = ['pepper', '--fail-all-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 diff --git a/tests/unit/test_retcodes_fail_none.py b/tests/unit/test_retcodes_fail_none.py index 5e661e3..807640a 100644 --- a/tests/unit/test_retcodes_fail_none.py +++ b/tests/unit/test_retcodes_fail_none.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- # Import Python Libraries -from __future__ import print_function, unicode_literals, absolute_import import sys +from unittest.mock import MagicMock +from unittest.mock import patch -# Import Pepper Libraries import pepper -from mock import patch, MagicMock +# Import Pepper Libraries PAYLOAD = { "return": [ @@ -14,52 +13,52 @@ "saltstack.ezh.msk.ru": { "jid": "20180414193904158892", "ret": "/bin/sh: 123: command not found", - "retcode": 127 + "retcode": 127, }, "ezh.msk.ru": { "jid": "20180414193904158892", "ret": "Hello from SaltStack", - } + }, } ] } -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_default(): - sys.argv = ['pepper', 'minion_id', 'request'] + sys.argv = ["pepper", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any(): - sys.argv = ['pepper', '--fail-any', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any_none(): - sys.argv = ['pepper', '--fail-any-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all(): - sys.argv = ['pepper', '--fail-all', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all_none(): - sys.argv = ['pepper', '--fail-all-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 diff --git a/tests/unit/test_retcodes_fail_pass.py b/tests/unit/test_retcodes_fail_pass.py index d8dc164..3334e32 100644 --- a/tests/unit/test_retcodes_fail_pass.py +++ b/tests/unit/test_retcodes_fail_pass.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- # Import Python Libraries -from __future__ import print_function, unicode_literals, absolute_import import sys +from unittest.mock import MagicMock +from unittest.mock import patch -# Import Pepper Libraries import pepper -from mock import patch, MagicMock +# Import Pepper Libraries PAYLOAD = { "return": [ @@ -14,53 +13,49 @@ "ezh.msk.ru": { "jid": "20180414193904158892", "ret": "/bin/sh: 123: command not found", - "retcode": 127 + "retcode": 127, }, - "saltstack.ezh.msk.ru": { - "jid": "20180414193904158892", - "ret": "pass", - "retcode": 0 - } + "saltstack.ezh.msk.ru": {"jid": "20180414193904158892", "ret": "pass", "retcode": 0}, } ] } -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_default(): - sys.argv = ['pepper', 'minion_id', 'request'] + sys.argv = ["pepper", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any(): - sys.argv = ['pepper', '--fail-any', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any_none(): - sys.argv = ['pepper', '--fail-any-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all(): - sys.argv = ['pepper', '--fail-all', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all_none(): - sys.argv = ['pepper', '--fail-all-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 diff --git a/tests/unit/test_retcodes_none_none.py b/tests/unit/test_retcodes_none_none.py index 7e8a041..99077aa 100644 --- a/tests/unit/test_retcodes_none_none.py +++ b/tests/unit/test_retcodes_none_none.py @@ -1,12 +1,11 @@ -# -*- coding: utf-8 -*- # Import Python Libraries -from __future__ import print_function, unicode_literals, absolute_import import sys +from unittest.mock import MagicMock +from unittest.mock import patch -# Import Pepper Libraries import pepper -from mock import patch, MagicMock +# Import Pepper Libraries PAYLOAD = { "return": [ @@ -18,47 +17,47 @@ "ezh.msk.ru": { "jid": "20180414193904158892", "ret": "Hello from SaltStack", - } + }, } ] } -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_default(): - sys.argv = ['pepper', 'minion_id', 'request'] + sys.argv = ["pepper", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any(): - sys.argv = ['pepper', '--fail-any', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any_none(): - sys.argv = ['pepper', '--fail-any-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == -1 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all(): - sys.argv = ['pepper', '--fail-all', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all_none(): - sys.argv = ['pepper', '--fail-all-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == -1 diff --git a/tests/unit/test_retcodes_pass_fail.py b/tests/unit/test_retcodes_pass_fail.py index 4da3933..e148c0b 100644 --- a/tests/unit/test_retcodes_pass_fail.py +++ b/tests/unit/test_retcodes_pass_fail.py @@ -1,66 +1,61 @@ -# -*- coding: utf-8 -*- # Import Python Libraries -from __future__ import print_function, unicode_literals, absolute_import import sys +from unittest.mock import MagicMock +from unittest.mock import patch -# Import Pepper Libraries import pepper -from mock import patch, MagicMock +# Import Pepper Libraries PAYLOAD = { "return": [ { - "saltstack.ezh.msk.ru": { - "jid": "20180414193904158892", - "ret": "pass", - "retcode": 0 - }, + "saltstack.ezh.msk.ru": {"jid": "20180414193904158892", "ret": "pass", "retcode": 0}, "ezh.msk.ru": { "jid": "20180414193904158892", "ret": "/bin/sh: 123: command not found", - "retcode": 127 - } + "retcode": 127, + }, } ] } -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_default(): - sys.argv = ['pepper', 'minion_id', 'request'] + sys.argv = ["pepper", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any(): - sys.argv = ['pepper', '--fail-any', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any_none(): - sys.argv = ['pepper', '--fail-any-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 127 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all(): - sys.argv = ['pepper', '--fail-all', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all_none(): - sys.argv = ['pepper', '--fail-all-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 diff --git a/tests/unit/test_retcodes_pass_none.py b/tests/unit/test_retcodes_pass_none.py index 672f319..41d1436 100644 --- a/tests/unit/test_retcodes_pass_none.py +++ b/tests/unit/test_retcodes_pass_none.py @@ -1,65 +1,60 @@ -# -*- coding: utf-8 -*- # Import Python Libraries -from __future__ import print_function, unicode_literals, absolute_import import sys +from unittest.mock import MagicMock +from unittest.mock import patch -# Import Pepper Libraries import pepper -from mock import patch, MagicMock +# Import Pepper Libraries PAYLOAD = { "return": [ { - "saltstack.ezh.msk.ru": { - "jid": "20180414193904158892", - "ret": "pass", - "retcode": 0 - }, + "saltstack.ezh.msk.ru": {"jid": "20180414193904158892", "ret": "pass", "retcode": 0}, "ezh.msk.ru": { "jid": "20180414193904158892", "ret": "Hello from SaltStack", - } + }, } ] } -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_default(): - sys.argv = ['pepper', 'minion_id', 'request'] + sys.argv = ["pepper", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any(): - sys.argv = ['pepper', '--fail-any', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any_none(): - sys.argv = ['pepper', '--fail-any-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all(): - sys.argv = ['pepper', '--fail-all', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all_none(): - sys.argv = ['pepper', '--fail-all-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 diff --git a/tests/unit/test_retcodes_pass_pass.py b/tests/unit/test_retcodes_pass_pass.py index ed8519c..f84e5aa 100644 --- a/tests/unit/test_retcodes_pass_pass.py +++ b/tests/unit/test_retcodes_pass_pass.py @@ -1,66 +1,57 @@ -# -*- coding: utf-8 -*- # Import Python Libraries -from __future__ import print_function, unicode_literals, absolute_import import sys +from unittest.mock import MagicMock +from unittest.mock import patch -# Import Pepper Libraries import pepper -from mock import patch, MagicMock +# Import Pepper Libraries PAYLOAD = { "return": [ { - "ezh.msk.ru": { - "jid": "20180414193904158892", - "ret": "pass", - "retcode": 0 - }, - "saltstack.ezh.msk.ru": { - "jid": "20180414193904158892", - "ret": "pass", - "retcode": 0 - } + "ezh.msk.ru": {"jid": "20180414193904158892", "ret": "pass", "retcode": 0}, + "saltstack.ezh.msk.ru": {"jid": "20180414193904158892", "ret": "pass", "retcode": 0}, } ] } -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_default(): - sys.argv = ['pepper', 'minion_id', 'request'] + sys.argv = ["pepper", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any(): - sys.argv = ['pepper', '--fail-any', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_any_none(): - sys.argv = ['pepper', '--fail-any-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-any-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all(): - sys.argv = ['pepper', '--fail-all', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 -@patch('pepper.cli.PepperCli.login', MagicMock(side_effect=lambda arg: None)) -@patch('pepper.cli.PepperCli.low', MagicMock(side_effect=lambda api, load: PAYLOAD)) +@patch("pepper.cli.PepperCli.login", MagicMock(side_effect=lambda arg: None)) +@patch("pepper.cli.PepperCli.low", MagicMock(side_effect=lambda api, load: PAYLOAD)) def test_fail_all_none(): - sys.argv = ['pepper', '--fail-all-none', 'minion_id', 'request'] + sys.argv = ["pepper", "--fail-all-none", "minion_id", "request"] ret_code = pepper.script.Pepper()() assert ret_code == 0 diff --git a/tests/unit/test_token.py b/tests/unit/test_token.py index ec89946..579d1de 100644 --- a/tests/unit/test_token.py +++ b/tests/unit/test_token.py @@ -1,17 +1,17 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import import json import sys +from unittest.mock import MagicMock +from unittest.mock import mock_open +from unittest.mock import patch -# Import Pepper Libraries import pepper.cli +# Import Pepper Libraries # Import Testing Libraries -from mock import patch, mock_open, MagicMock def test_token(): - sys.argv = ['pepper', '*', 'test.ping'] + sys.argv = ["pepper", "*", "test.ping"] client = pepper.cli.PepperCli() client.options.mktoken = True mock_data = ( @@ -21,15 +21,17 @@ def test_token(): ) mock_api = MagicMock() mock_api.login = MagicMock(return_value=mock_data) - with patch('pepper.cli.open', mock_open(read_data=mock_data)), \ - patch('pepper.cli.PepperCli.get_login_details', MagicMock(return_value=mock_data)), \ - patch('pepper.cli.PepperCli.parse_login', MagicMock(return_value={})), \ - patch('os.remove', MagicMock(return_value=None)), \ - patch('json.dump', MagicMock(side_effect=Exception('Test Error'))): + with patch("pepper.cli.open", mock_open(read_data=mock_data)), patch( + "pepper.cli.PepperCli.get_login_details", MagicMock(return_value=mock_data) + ), patch("pepper.cli.PepperCli.parse_login", MagicMock(return_value={})), patch( + "os.remove", MagicMock(return_value=None) + ), patch( + "json.dump", MagicMock(side_effect=Exception("Test Error")) + ): ret1 = client.login(mock_api) - with patch('os.path.isfile', MagicMock(return_value=False)): + with patch("os.path.isfile", MagicMock(return_value=False)): ret2 = client.login(mock_api) - with patch('time.time', MagicMock(return_value=1529968044.133632)): + with patch("time.time", MagicMock(return_value=1529968044.133632)): ret3 = client.login(mock_api) assert json.loads(ret1) == json.loads(mock_data) assert json.loads(ret2) == json.loads(mock_data)