dcos-2440 add helpful error message if no http(s) in url
This commit is contained in:
@@ -1,48 +1,47 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"title": "Your email address",
|
||||
"description": "Your email address"
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"dcos_url": {
|
||||
"description": "The URL to the location of the DCOS",
|
||||
"format": "uri",
|
||||
"title": "DCOS URL",
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"description": "Your email address",
|
||||
"title": "Your email address",
|
||||
"type": "string"
|
||||
},
|
||||
"mesos_master_url": {
|
||||
"description": "Mesos Master URL. Must be of the format: \"http://host:port\"",
|
||||
"format": "uri",
|
||||
"title": "Mesos Master URL",
|
||||
"type": "string"
|
||||
},
|
||||
"refresh_token": {
|
||||
"description": "Your OAuth refresh token",
|
||||
"title": "Your OAuth refresh token",
|
||||
"type": "string"
|
||||
},
|
||||
"reporting": {
|
||||
"default": true,
|
||||
"description": "Whether to report usage events to Mesosphere",
|
||||
"title": "Usage Reporting",
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeout": {
|
||||
"default": 5,
|
||||
"description": "Request timeout in seconds",
|
||||
"minimum": 1,
|
||||
"title": "Request timeout in seconds",
|
||||
"type": "integer"
|
||||
},
|
||||
"token": {
|
||||
"description": "Your OAuth access token",
|
||||
"title": "Your OAuth access token",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"dcos_url": {
|
||||
"type": "string",
|
||||
"pattern": "^(?:(?:https?)://)(([^:])+(:[^:]+)?@){0,1}(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.?)+(?:[a-zA-Z]{2,6}\\.?|[a-zA-Z0-9-]{2,}\\.?)?|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(?::\\d+)?(?:/?|[/?]\\S+)$",
|
||||
"title": "DCOS URL",
|
||||
"description": "The URL to the location of the DCOS"
|
||||
},
|
||||
"refresh_token": {
|
||||
"type": "string",
|
||||
"title": "Your OAuth refresh token",
|
||||
"description": "Your OAuth refresh token"
|
||||
},
|
||||
"reporting": {
|
||||
"type": "boolean",
|
||||
"title": "Usage Reporting",
|
||||
"description": "Whether to report usage events to Mesosphere",
|
||||
"default": true
|
||||
},
|
||||
"token": {
|
||||
"type": "string",
|
||||
"title": "Your OAuth access token",
|
||||
"description": "Your OAuth access token"
|
||||
},
|
||||
"mesos_master_url": {
|
||||
"type": "string",
|
||||
"pattern": "^(?:(?:https?)://)(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.?)+(?:[a-zA-Z]{2,6}\\.?|[a-zA-Z0-9-]{2,}\\.?)?|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(?::\\d+)?(?:/?|[/?]\\S+)$",
|
||||
"title": "Mesos Master URL",
|
||||
"description":
|
||||
"Mesos Master URL. Must be of the format: \"http://host:port\""
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer",
|
||||
"default": 5,
|
||||
"minimum": 1,
|
||||
"title": "Request timeout in seconds",
|
||||
"description": "Request timeout in seconds"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
"type": "object"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"pattern": "^(?:(?:https?)://)(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.?)+(?:[a-zA-Z]{2,6}\\.?|[a-zA-Z0-9-]{2,}\\.?)?|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(?::\\d+)?(?:/?|[/?]\\S+)$",
|
||||
"format": "uri",
|
||||
"title": "Marathon base URL",
|
||||
"description": "Base URL for talking to Marathon. It overwrites the value specified in core.dcos_url",
|
||||
"default": "http://localhost:8080"
|
||||
|
||||
@@ -101,6 +101,13 @@ def test_get_missing_property(env):
|
||||
_get_missing_value('missing.property', env)
|
||||
|
||||
|
||||
def test_invalid_dcos_url(env):
|
||||
stderr = b'Please check url \'abc.com\'. Missing http(s)://\n'
|
||||
assert_command(['dcos', 'config', 'set', 'core.dcos_url', 'abc.com'],
|
||||
stderr=stderr,
|
||||
returncode=1)
|
||||
|
||||
|
||||
def test_get_top_property(env):
|
||||
stderr = (
|
||||
b"Property 'package' doesn't fully specify a value - "
|
||||
@@ -473,12 +480,12 @@ def test_bad_port_fail_url_validation(env):
|
||||
'http://localhost:bad_port/', env)
|
||||
|
||||
|
||||
def test_append_fail_url_validation(env):
|
||||
_fail_url_validation('append', 'package.sources', 'bad_url', env)
|
||||
def test_append_fail_validation(env):
|
||||
_fail_validation('append', 'package.sources', 'bad_url', env)
|
||||
|
||||
|
||||
def test_prepend_fail_url_validation(env):
|
||||
_fail_url_validation('prepend', 'package.sources', 'bad_url', env)
|
||||
def test_prepend_fail_validation(env):
|
||||
_fail_validation('prepend', 'package.sources', 'bad_url', env)
|
||||
|
||||
|
||||
def test_timeout(missing_env):
|
||||
@@ -513,6 +520,16 @@ def _fail_url_validation(command, key, value, env):
|
||||
returncode_, stdout_, stderr_ = exec_command(
|
||||
['dcos', 'config', command, key, value], env=env)
|
||||
|
||||
assert returncode_ == 1
|
||||
assert stdout_ == b''
|
||||
assert stderr_.startswith(str(
|
||||
'Unable to parse {!r} as a url'.format(value)).encode('utf-8'))
|
||||
|
||||
|
||||
def _fail_validation(command, key, value, env):
|
||||
returncode_, stdout_, stderr_ = exec_command(
|
||||
['dcos', 'config', command, key, value], env=env)
|
||||
|
||||
assert returncode_ == 1
|
||||
assert stdout_ == b''
|
||||
assert stderr_.startswith(str(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import collections
|
||||
import json
|
||||
import re
|
||||
|
||||
from dcos import util
|
||||
from dcos.errors import DCOSException
|
||||
@@ -86,7 +87,10 @@ class ValueTypeParser(object):
|
||||
value = clean_value(value)
|
||||
|
||||
if self.schema['type'] == 'string':
|
||||
return _parse_string(value)
|
||||
if self.schema.get('format') == 'uri':
|
||||
return _parse_url(value)
|
||||
else:
|
||||
return _parse_string(value)
|
||||
elif self.schema['type'] == 'object':
|
||||
return _parse_object(value)
|
||||
elif self.schema['type'] == 'number':
|
||||
@@ -226,3 +230,37 @@ def _parse_array(value):
|
||||
|
||||
msg = 'Unable to parse {!r} as an array: {}'.format(value, error)
|
||||
raise DCOSException(msg)
|
||||
|
||||
|
||||
def _parse_url(value):
|
||||
"""
|
||||
:param value: The url to parse
|
||||
:type url: str
|
||||
:returns: The parsed value
|
||||
:rtype: str
|
||||
"""
|
||||
|
||||
scheme_pattern = r'^(?P<scheme>(?:(?:https?)://))'
|
||||
domain_pattern = (
|
||||
r'(?P<hostname>(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.?)+'
|
||||
'(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)?|') # domain,
|
||||
|
||||
value_regex = re.match(
|
||||
scheme_pattern + # http:// or https://
|
||||
r'(([^:])+(:[^:]+)?@){0,1}' + # auth credentials
|
||||
domain_pattern +
|
||||
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))' # or ip
|
||||
r'(?P<port>(?::\d+))?' # port
|
||||
r'(?P<path>(?:/?|[/?]\S+))$', # resource path
|
||||
value, re.IGNORECASE)
|
||||
|
||||
if value_regex is None:
|
||||
scheme_match = re.match(scheme_pattern, value, re.IGNORECASE)
|
||||
if scheme_match is None:
|
||||
msg = 'Please check url {!r}. Missing http(s)://'.format(value)
|
||||
raise DCOSException(msg)
|
||||
else:
|
||||
raise DCOSException(
|
||||
'Unable to parse {!r} as a url'.format(value))
|
||||
else:
|
||||
return value
|
||||
|
||||
@@ -60,22 +60,33 @@ def bad_array(request):
|
||||
][request.param]
|
||||
|
||||
|
||||
@pytest.fixture(params=range(12))
|
||||
@pytest.fixture(params=[
|
||||
'www.test.com',
|
||||
'http:/hi.com',
|
||||
'https//www.hi.com',
|
||||
'http://bad.port:here'
|
||||
])
|
||||
def bad_url(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=[
|
||||
('string', 'this is a string', 'this is a string'),
|
||||
('string', 'null', None),
|
||||
('object', '{"key":"value"}', {'key': 'value'}),
|
||||
('object', 'null', None),
|
||||
('number', '4.2', 4.2),
|
||||
('number', 'null', None),
|
||||
('integer', '42', 42),
|
||||
('integer', 'null', None),
|
||||
('boolean', 'true', True),
|
||||
('boolean', 'null', None),
|
||||
('array', '[1,2,3]', [1, 2, 3]),
|
||||
('array', 'null', None),
|
||||
('url', 'http://test.com', 'http://test.com')
|
||||
])
|
||||
def jsonitem_tuple(request):
|
||||
return [
|
||||
('string', 'this is a string', 'this is a string'),
|
||||
('string', 'null', None),
|
||||
('object', '{"key":"value"}', {'key': 'value'}),
|
||||
('object', 'null', None),
|
||||
('number', '4.2', 4.2),
|
||||
('number', 'null', None),
|
||||
('integer', '42', 42),
|
||||
('integer', 'null', None),
|
||||
('boolean', 'true', True),
|
||||
('boolean', 'null', None),
|
||||
('array', '[1,2,3]', [1, 2, 3]),
|
||||
('array', 'null', None),
|
||||
][request.param]
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=range(13))
|
||||
@@ -123,16 +134,22 @@ def schema():
|
||||
'type': 'number'
|
||||
},
|
||||
'string': {
|
||||
'type': 'string'
|
||||
'type': 'string',
|
||||
},
|
||||
'object': {
|
||||
'type': 'object'
|
||||
},
|
||||
'array': {
|
||||
'type': 'array'
|
||||
|
||||
},
|
||||
'boolean': {
|
||||
'type': 'boolean'
|
||||
'type': 'boolean',
|
||||
},
|
||||
'url': {
|
||||
'type': 'string',
|
||||
'format': 'url',
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,6 +207,15 @@ def test_parse_invalid_arrays(bad_array):
|
||||
jsonitem._parse_array(bad_array)
|
||||
|
||||
|
||||
def test_parse_url():
|
||||
assert jsonitem._parse_url('http://test.com:12') == 'http://test.com:12'
|
||||
|
||||
|
||||
def test_parse_invalid_url(bad_url):
|
||||
with pytest.raises(DCOSException):
|
||||
jsonitem._parse_url(bad_url)
|
||||
|
||||
|
||||
def test_find_parser(schema, jsonitem_tuple):
|
||||
key, string_value, value = jsonitem_tuple
|
||||
assert jsonitem.find_parser(key, schema)(string_value) == value
|
||||
|
||||
Reference in New Issue
Block a user