Skip to content

Commit

Permalink
host-appointer: prohibit invalid host names
Browse files Browse the repository at this point in the history
  • Loading branch information
oliver-sanders committed Nov 22, 2018
1 parent 7e472e2 commit 89241a0
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 12 deletions.
6 changes: 3 additions & 3 deletions lib/cylc/cfgspec/globalcfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,11 @@
},

'suite servers': {
'run hosts': [VDR.V_STRING_LIST],
'run hosts': [VDR.V_SPACELESS_STRING_LIST],
'run ports': [VDR.V_INTEGER_LIST, range(43001, 43101)],
'scan hosts': [VDR.V_STRING_LIST],
'scan hosts': [VDR.V_SPACELESS_STRING_LIST],
'scan ports': [VDR.V_INTEGER_LIST, range(43001, 43101)],
'condemned hosts': [VDR.V_STRING_LIST],
'condemned hosts': [VDR.V_SPACELESS_STRING_LIST],
'auto restart delay': [VDR.V_INTERVAL],
'run host select': {
'rank': [VDR.V_STRING, 'random', 'load:1', 'load:5', 'load:15',
Expand Down
29 changes: 29 additions & 0 deletions lib/parsec/tests/synonyms/07-string_list.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash
# THIS FILE IS PART OF THE CYLC SUITE ENGINE.
# Copyright (C) 2008-2018 NIWA & British Crown (Met Office) & Contributors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------------
# Test parsing of string list items
. $(dirname $0)/test_header

#-------------------------------------------------------------------------------
set_test_number 2

install_test $TEST_NAME_BASE
#-------------------------------------------------------------------------------
#synonyms.py spaceless_string_list >&2
run_ok "${TEST_NAME_BASE}-good" synonyms.py spaceless_string_list
run_ok "${TEST_NAME_BASE}-bad" python -m doctest \
"${CYLC_DIR}/lib/parsec/validate.py"
29 changes: 29 additions & 0 deletions lib/parsec/tests/synonyms/07-string_list/spaceless_string_list.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# All legals ways of defining string lists should parse to the same result.
# triple-quoted string values are currently illegal.
[spaceless_string_list]
[[the_quick_brown_fox]]
item 1 = the, quick, brown, fox # comment
item 2 = the, quick , brown, fox, # comment
item 3 = 'the', 'quick' , 'brown', 'fox', # comment
item 4 = "the", "quick" , "brown", "fox", # comment
item 5 = "the", "quick" , "brown",\
"fox", # comment
item 6 = the, quick, brown, fox # jumps, over # the lazy

[[the#c1_quick#c2_brown#c3_fox#c4]]
# internal comments should be preserved
item 1 = 'the#c1', 'quick#c2' , 'brown#c3', 'fox#c4', # comment
item 2 = "the#c1", "quick#c2" , "brown#c3", "fox#c4", # comment
item 3 = "the#c1", "quick#c2" , "brown#c3",\
"fox#c4", # comment

[[theCOMMAc1_quickCOMMAc2_brownCOMMAc3_foxCOMMAc4]]
# internal commas should be preserved
item 1 = 'the,c1', 'quick,c2' , 'brown,c3', 'fox,c4', # comment
item 2 = "the,c1", "quick,c2" , "brown,c3", "fox,c4", # comment
item 3 = "the,c1", "quick,c2" , "brown,c3",\
"fox,c4", # comment

[[NULL]]
item 1 =
item 2 = # comment
2 changes: 1 addition & 1 deletion lib/parsec/tests/synonyms/bin/synonyms.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
elif rcname == 'float_list':
expected = [float(i) for i in expected.split('_')]

elif rcname == 'string_list':
elif rcname in ['string_list', 'spaceless_string_list']:
if expected:
expected = expected.split('_')
else:
Expand Down
2 changes: 2 additions & 0 deletions lib/parsec/tests/synonyms/lib/python/cfgspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
'float': {'__MANY__': {'__MANY__': [VDR.V_FLOAT]}},
'string': {'__MANY__': {'__MANY__': [VDR.V_STRING]}},
'string_list': {'__MANY__': {'__MANY__': [VDR.V_STRING_LIST]}},
'spaceless_string_list': {'__MANY__': {'__MANY__': [
VDR.V_SPACELESS_STRING_LIST]}},
'float_list': {'__MANY__': {'__MANY__': [VDR.V_FLOAT_LIST]}},
'integer_list': {'__MANY__': {'__MANY__': [VDR.V_INTEGER_LIST]}},
}
35 changes: 28 additions & 7 deletions lib/parsec/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,10 @@ class ValidationError(ParsecError):

class ListValueError(ValidationError):
"""Bad setting value, for a comma separated list."""
def __init__(self, keys, value, exc=None):
def __init__(self, keys, value, msg='', exc=None):
ValidationError.__init__(self)
self.msg = (
"ERROR: names containing commas must be quoted"
" (e.g. 'foo<m,n>'):\n %s" % itemstr(
keys[:-1], keys[-1], value=value))
self.msg = '%s\n %s' % (
msg, itemstr(keys[:-1], keys[-1], value=value))
if exc:
self.msg += ": %s" % exc

Expand Down Expand Up @@ -110,6 +108,7 @@ class ParsecValidator(object):
V_INTEGER_LIST = 'V_INTEGER_LIST'
V_STRING = 'V_STRING'
V_STRING_LIST = 'V_STRING_LIST'
V_SPACELESS_STRING_LIST = 'V_SPACELESS_STRING_LIST'

def __init__(self):
self.coercers = {
Expand All @@ -120,6 +119,7 @@ def __init__(self):
self.V_INTEGER_LIST: self.coerce_int_list,
self.V_STRING: self.coerce_str,
self.V_STRING_LIST: self.coerce_str_list,
self.V_SPACELESS_STRING_LIST: self.coerce_spaceless_str_list,
}

def validate(self, cfg_root, spec_root):
Expand Down Expand Up @@ -250,6 +250,26 @@ def coerce_str_list(cls, value, keys):
"""Coerce value to a list of strings."""
return cls.strip_and_unquote_list(keys, value)

@classmethod
def coerce_spaceless_str_list(cls, value, keys):
"""Coerce value to a list of strings ensuring no values contain spaces.
Examples:
>>> ParsecValidator.coerce_spaceless_str_list(
... 'a, b c, d', ['foo'])
Traceback (most recent call last):
ListValueError: list item "b c" cannot contain a space character:
foo = a, b c, d
"""
lst = cls.strip_and_unquote_list(keys, value)
for item in lst:
if ' ' in item:
raise ListValueError(
keys, value,
msg='list item "%s" cannot contain a space character:' %
item)
return lst

@classmethod
def expand_list(cls, values, keys, type_):
"""Handle multiplier syntax N*VALUE in a list."""
Expand Down Expand Up @@ -373,8 +393,9 @@ def _unquoted_list_parse(cls, keys, value):

# First detect multi-parameter lists like <m,n>.
if cls._REC_MULTI_PARAM.search(value):
raise ListValueError(keys, value)

raise ListValueError(keys, value,
msg="names containing commas must be quoted"
" (e.g. 'foo<m,n>'):")
pos = 0
while True:
match = cls._REC_UQLP.search(value, pos)
Expand Down
16 changes: 15 additions & 1 deletion tests/cylc.host_appointer/00-host-appointer.t
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# Run unit tests to test HostAppointer class for selecting hosts.
. "$(dirname "$0")/test_header"
#-------------------------------------------------------------------------------
set_test_number 7
set_test_number 9

run_ok "${TEST_NAME_BASE}" python -m 'cylc.host_appointer'

Expand Down Expand Up @@ -53,6 +53,20 @@ for _ in range(10):
sys.exit(1)
'

# Invalid hostnames
create_test_globalrc '' "
[suite servers]
run hosts = foo bar
[suite servers]
condemned hosts = localhost
"
run_fail "${TEST_NAME_BASE}-invalid" python -c '
from cylc.host_appointer import HostAppointer
HostAppointer().appoint_host()
'
grep_ok 'list item "foo bar" cannot contain a space character' \
"${TEST_NAME_BASE}-invalid.stderr"

export CYLC_TEST_HOST=$( \
cylc get-global-config -i '[test battery]remote host with shared fs' \
2>'/dev/null')
Expand Down

0 comments on commit 89241a0

Please sign in to comment.