dcos-2440 add helpful error message if no http(s) in url

This commit is contained in:
Tamar Ben-Shachar
2015-08-03 16:48:28 -07:00
parent 99fe8a45f9
commit 469769c947
5 changed files with 148 additions and 68 deletions

View File

@@ -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"
}

View File

@@ -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"

View File

@@ -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(

View File

@@ -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

View File

@@ -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