PYTHON-1418 - More spec updates

This commit is contained in:
Bernie Hackett 2017-11-29 10:52:05 -08:00
parent e554d6116c
commit e42897e79e
17 changed files with 79 additions and 62 deletions

View File

@ -133,12 +133,14 @@ class MongoClient(common.BaseObject):
resolved to one or more DNS `SRV records
<https://en.wikipedia.org/wiki/SRV_record>`_ which will be used
as the seed list for connecting to the MongoDB deployment. When using
SRV support configuration options can be specified using `TXT records
SRV URIs, the `authSource` and `replicaSet` configuration options can
be specified using `TXT records
<https://en.wikipedia.org/wiki/TXT_record>`_. See the
`Initial DNS Seedlist Discovery spec
<https://github.com/mongodb/specifications/blob/master/source/
initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.rst>`_
for more details.
for more details. Note that the use of SRV URIs implicitly enables
TLS support. Pass ssl=false in the URI to override.
.. note:: MongoClient creation will block waiting for answers from
DNS when mongodb+srv:// URIs are used.

View File

@ -278,6 +278,10 @@ else:
return text
_ALLOWED_TXT_OPTS = frozenset(
['authsource', 'authSource', 'replicaset', 'replicaSet'])
def _get_dns_srv_hosts(hostname):
try:
results = resolver.query('_mongodb._tcp.' + hostname, 'SRV')
@ -295,6 +299,8 @@ def _get_dns_txt_options(hostname):
return None
except Exception as exc:
raise ConfigurationError(str(exc))
if len(results) > 1:
raise ConfigurationError('Only one TXT record is supported')
return (
b'&'.join([b''.join(res.strings) for res in results])).decode('utf-8')
@ -410,6 +416,10 @@ def parse_uri(uri, default_port=DEFAULT_PORT, validate=True, warn=False):
dns_options = _get_dns_txt_options(fqdn)
if dns_options:
options = split_options(dns_options, validate, warn)
if set(options) - _ALLOWED_TXT_OPTS:
raise ConfigurationError(
"Only authSource and replicaSet are supported from DNS")
options["ssl"] = True if validate else 'true'
else:
nodes = split_hosts(hosts, default_port=default_port)

View File

@ -8,5 +8,9 @@
"localhost:27018",
"localhost:27019"
],
"options": {
"replicaSet": "repl0",
"ssl": true
},
"comment": "Is correct, as returned host name shared the URI root \"test.build.10gen.cc\"."
}

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test8.test.build.10gen.cc/",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because the options in the TXT record are incorrectly formatted (misses value)."
}

View File

@ -7,5 +7,9 @@
"localhost:27017",
"localhost:27018",
"localhost:27019"
]
],
"options": {
"replicaSet": "repl0",
"ssl": true
}
}

View File

@ -1,5 +1,5 @@
{
"uri": "mongodb+srv://test11.test.build.10gen.cc/?replicaSet=repl0",
"uri": "mongodb+srv://test11.test.build.10gen.cc/",
"seeds": [
"localhost.test.build.10gen.cc:27017"
],
@ -9,8 +9,7 @@
"localhost:27019"
],
"options": {
"connectTimeoutMS": 150000,
"replicaSet": "repl0",
"socketTimeoutMS": 250000
"ssl": true
}
}

View File

@ -1,5 +1,5 @@
{
"uri": "mongodb+srv://test5.test.build.10gen.cc/?replicaSet=repl0",
"uri": "mongodb+srv://test5.test.build.10gen.cc/",
"seeds": [
"localhost.test.build.10gen.cc:27017"
],
@ -9,8 +9,8 @@
"localhost:27019"
],
"options": {
"connectTimeoutMS": 300000,
"replicaSet": "repl0",
"socketTimeoutMS": 300000
"authSource": "thisDB",
"ssl": true
}
}

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test19.test.build.10gen.cc/",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because one of the returned host names' domain name parts \"evil\" mismatches \"test\"."
}

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test12.test.build.10gen.cc/",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because returned host name is too short and mismatches a parent."
}

View File

@ -8,5 +8,9 @@
"localhost:27017",
"localhost:27018",
"localhost:27019"
]
],
"options": {
"replicaSet": "repl0",
"ssl": true
}
}

View File

@ -8,5 +8,9 @@
"localhost:27017",
"localhost:27018",
"localhost:27019"
]
],
"options": {
"replicaSet": "repl0",
"ssl": true
}
}

View File

@ -1,16 +1,7 @@
{
"uri": "mongodb+srv://test6.test.build.10gen.cc/?replicaSet=repl0",
"seeds": [
"localhost.test.build.10gen.cc:27017"
],
"hosts": [
"localhost:27017",
"localhost:27018",
"localhost:27019"
],
"options": {
"connectTimeoutMS": 200000,
"replicaSet": "repl0",
"socketTimeoutMS": 200000
}
"uri": "mongodb+srv://test6.test.build.10gen.cc/",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because there are two TXT records."
}

View File

@ -1,25 +0,0 @@
{
"uri": "mongodb+srv://test7.test.build.10gen.cc/?replicaSet=repl0&readPreferenceTags=dc:fr,item:cheese&readPreferenceTags=dc:de,item:hotdog",
"seeds": [
"localhost.test.build.10gen.cc:27017"
],
"hosts": [
"localhost:27017",
"localhost:27018",
"localhost:27019"
],
"options": {
"replicaSet": "repl0",
"readPreference": "secondaryPreferred",
"readPreferenceTags": [
{
"dc": "fr",
"item": "cheese"
},
{
"dc": "de",
"item": "hotdog"
}
]
}
}

View File

@ -1,5 +1,5 @@
{
"uri": "mongodb+srv://test6.test.build.10gen.cc/?replicaSet=repl0&connectTimeoutMS=250000",
"uri": "mongodb+srv://test5.test.build.10gen.cc/?authSource=otherDB",
"seeds": [
"localhost.test.build.10gen.cc:27017"
],
@ -9,8 +9,8 @@
"localhost:27019"
],
"options": {
"connectTimeoutMS": 250000,
"replicaSet": "repl0",
"socketTimeoutMS": 200000
"authSource": "otherDB",
"ssl": true
}
}

View File

@ -0,0 +1,7 @@
{
"uri": "mongodb+srv://test7.test.build.10gen.cc/",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because \"ssl\" is not an allowed option."
}

View File

@ -1,7 +0,0 @@
{
"uri": "mongodb+srv://test10.test.build.10gen.cc/?replicaSet=repl0",
"seeds": [],
"hosts": [],
"error": true,
"comment": "Should fail because the value of socketTimeoutMS is not an integer."
}

View File

@ -44,6 +44,7 @@ class TestDNS(unittest.TestCase):
def create_test(test_case):
@client_context.require_replica_set
@client_context.require_ssl
def run_test(self):
if not _HAVE_DNSPYTHON:
raise unittest.SkipTest("DNS tests require the dnspython module")
@ -57,8 +58,10 @@ def create_test(test_case):
hosts = frozenset(split_hosts(','.join(hosts)))
if options:
for key, value in options.items():
# Convert numbers to strings for comparison
if isinstance(value, (int, float)):
# Convert numbers / booleans to strings for comparison
if isinstance(value, bool):
options[key] = 'true' if value else 'false'
elif isinstance(value, (int, float)):
options[key] = str(value)
if seeds: