diff --git a/pepper/cli.py b/pepper/cli.py index 07685f6..2a96f67 100644 --- a/pepper/cli.py +++ b/pepper/cli.py @@ -468,7 +468,7 @@ def parse_login(self): return ret - def parse_cmd(self): + def parse_cmd(self, api): ''' Extract the low data for a command from the passed CLI params ''' @@ -505,26 +505,37 @@ def parse_cmd(self): low['arg'] = args elif client.startswith('runner'): low['fun'] = args.pop(0) - for arg in args: - 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) + # 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 + else: + for arg in args: + 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) - for arg in args: - 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) + # see above comment in runner arg handling + if api.salt_version: + low['arg'] = args + else: + for arg in args: + 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'): if len(args) < 2: self.parser.error("Command or target not specified") @@ -636,12 +647,6 @@ def run(self): logger.addHandler(logging.StreamHandler()) logger.setLevel(max(logging.ERROR - (self.options.verbose * 10), 1)) - load = self.parse_cmd() - - for entry in load: - if entry.get('client', '').startswith('local'): - entry['full_return'] = True - api = pepper.Pepper( self.parse_url(), debug_http=self.options.debug_http, @@ -649,6 +654,12 @@ def run(self): self.login(api) + load = self.parse_cmd(api) + + for entry in load: + if entry.get('client', '').startswith('local'): + 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 yield exit_code, json.dumps(ret, sort_keys=True, indent=4) diff --git a/pepper/libpepper.py b/pepper/libpepper.py index 7b4e606..9dfe05c 100644 --- a/pepper/libpepper.py +++ b/pepper/libpepper.py @@ -6,6 +6,7 @@ ''' import json import logging +import re import ssl from pepper.exceptions import PepperException @@ -79,6 +80,7 @@ def __init__(self, api_url='https://localhost:8000', debug_http=False, ignore_ss self.debug_http = int(debug_http) self._ssl_verify = not ignore_ssl_errors self.auth = {} + self.salt_version = None def req_stream(self, path): ''' @@ -231,6 +233,10 @@ def req(self, path, data=None): 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']) + except (HTTPError, URLError) as exc: logger.debug('Error with request', exc_info=True) status = getattr(exc, 'code', None) @@ -285,6 +291,10 @@ def req_requests(self, path, data=None): if resp.status_code == 500: # TODO should be resp.raise_from_status 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']) + return resp.json() def low(self, lowstate, path='/'): @@ -479,3 +489,17 @@ def _construct_url(self, path): 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}))?' + ) + match = git_describe_regex.match(version) + if match: + self.salt_version = match.groups()