Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support regex status types in http.query state #50150

Merged
merged 3 commits into from
Oct 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions salt/modules/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ def query(url, **kwargs):
params='key1=val1&key2=val2'
salt '*' http.query http://somelink.com/ method=POST \
data='<xml>somecontent</xml>'

For more information about the ``http.query`` module, refer to the
:ref:`HTTP Tutorial <tutorial-http>`.
'''
opts = __opts__.copy()
if 'opts' in kwargs:
Expand Down
47 changes: 36 additions & 11 deletions salt/states/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
log = logging.getLogger(__name__)


def query(name, match=None, match_type='string', status=None, wait_for=None, **kwargs):
def query(name, match=None, match_type='string', status=None, status_type='string', wait_for=None, **kwargs):
'''
Perform an HTTP query and statefully return the result

Expand All @@ -36,7 +36,7 @@ def query(name, match=None, match_type='string', status=None, wait_for=None, **k
text.

match_type
Specifies the type of pattern matching to use. Default is ``string``, but
Specifies the type of pattern matching to use on match. Default is ``string``, but
can also be set to ``pcre`` to use regular expression matching if a more
complex pattern matching is required.

Expand All @@ -50,6 +50,19 @@ def query(name, match=None, match_type='string', status=None, wait_for=None, **k
The status code for a URL for which to be checked. Can be used instead of
or in addition to the ``match`` setting.

status_type
Specifies the type of pattern matching to use for status. Default is ``string``, but
can also be set to ``pcre`` to use regular expression matching if a more
complex pattern matching is required.

.. versionadded:: Neon

.. note::

Despite the name of ``match_type`` for this argument, this setting
actually uses Python's ``re.search()`` function rather than Python's
``re.match()`` function.

If both ``match`` and ``status`` options are set, both settings will be checked.
However, note that if only one option is ``True`` and the other is ``False``,
then ``False`` will be returned. If this case is reached, the comments in the
Expand Down Expand Up @@ -94,28 +107,40 @@ def query(name, match=None, match_type='string', status=None, wait_for=None, **k

if match is not None:
if match_type == 'string':
if match in data.get('text', ''):
if str(match) in data.get('text', ''):
ret['result'] = True
ret['comment'] += ' Match text "{0}" was found.'.format(match)
else:
ret['result'] = False
ret['comment'] += ' Match text "{0}" was not found.'.format(match)
elif match_type == 'pcre':
if re.search(match, data.get('text', '')):
if re.search(str(match), str(data.get('text', ''))):
ret['result'] = True
ret['comment'] += ' Match pattern "{0}" was found.'.format(match)
else:
ret['result'] = False
ret['comment'] += ' Match pattern "{0}" was not found.'.format(match)

if status is not None:
if data.get('status', '') == status:
ret['comment'] += 'Status {0} was found, as specified.'.format(status)
if ret['result'] is None:
ret['result'] = True
else:
ret['comment'] += 'Status {0} was not found, as specified.'.format(status)
ret['result'] = False
if status_type == 'string':
if data.get('status', '') == str(status):
ret['comment'] += ' Status {0} was found.'.format(status)
if ret['result'] is None:
ret['result'] = True
else:
ret['comment'] += ' Status {0} was not found.'.format(status)
ret['result'] = False
elif status_type == 'pcre':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we combine this with the check for the string status type? It looks like the blocks are basically identical.

if re.search(str(status), str(data.get('status', ''))):
ret['comment'] += ' Status pattern "{0}" was found.'.format(status)
if ret['result'] is None:
ret['result'] = True
else:
ret['comment'] += ' Status pattern "{0}" was not found.'.format(status)
ret['result'] = False

# cleanup spaces in comment
ret['comment'] = ret['comment'].strip()

if __opts__['test'] is True:
ret['result'] = None
Expand Down
2 changes: 1 addition & 1 deletion salt/utils/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ def update_ca_bundle(
source=None,
opts=None,
merge_files=None,
):
):
'''
Attempt to update the CA bundle file from a URL

Expand Down
24 changes: 24 additions & 0 deletions tests/unit/states/test_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,27 @@ def test_query(self):
with patch.dict(http.__salt__, {'http.query': mock}):
self.assertDictEqual(http.query("salt", "Dude", "stack"),
ret[1])

def test_query_statustype(self):
'''
Test to perform an HTTP query and statefully return the result
'''
testurl = "salturl"
http_result = {
"text": "This page returned a 201 status code",
"status": "201"
}
state_return = {'changes': {},
'comment': 'Match text "This page returned" was found. Status pattern "200|201" was found.',
'data': {'status': '201', 'text': 'This page returned a 201 status code'},
'name': testurl,
'result': True}

with patch.dict(http.__opts__, {'test': False}):
mock = MagicMock(return_value=http_result)
with patch.dict(http.__salt__, {'http.query': mock}):
self.assertDictEqual(http.query(testurl,
match="This page returned",
status="200|201",
status_type='pcre'
), state_return)