mongo 1.11.1 → 1.12.0.rc0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/VERSION +1 -1
- data/lib/mongo/collection.rb +8 -3
- data/lib/mongo/collection_writer.rb +1 -1
- data/lib/mongo/connection/socket/unix_socket.rb +1 -1
- data/lib/mongo/cursor.rb +8 -1
- data/lib/mongo/db.rb +61 -33
- data/lib/mongo/exception.rb +57 -0
- data/lib/mongo/functional.rb +1 -0
- data/lib/mongo/functional/authentication.rb +138 -11
- data/lib/mongo/functional/read_preference.rb +31 -22
- data/lib/mongo/functional/scram.rb +555 -0
- data/lib/mongo/functional/uri_parser.rb +107 -79
- data/lib/mongo/mongo_client.rb +19 -24
- data/lib/mongo/mongo_replica_set_client.rb +2 -1
- data/test/functional/authentication_test.rb +3 -0
- data/test/functional/client_test.rb +33 -0
- data/test/functional/collection_test.rb +29 -19
- data/test/functional/db_api_test.rb +16 -1
- data/test/functional/pool_test.rb +7 -6
- data/test/functional/uri_test.rb +111 -7
- data/test/helpers/test_unit.rb +17 -3
- data/test/replica_set/client_test.rb +31 -0
- data/test/replica_set/insert_test.rb +49 -32
- data/test/replica_set/pinning_test.rb +50 -0
- data/test/replica_set/query_test.rb +1 -1
- data/test/replica_set/replication_ack_test.rb +3 -3
- data/test/shared/authentication/basic_auth_shared.rb +14 -1
- data/test/shared/authentication/gssapi_shared.rb +13 -8
- data/test/shared/authentication/scram_shared.rb +92 -0
- data/test/tools/mongo_config.rb +18 -6
- data/test/unit/client_test.rb +40 -6
- data/test/unit/connection_test.rb +15 -5
- data/test/unit/db_test.rb +1 -1
- data/test/unit/read_pref_test.rb +291 -0
- metadata +9 -6
- metadata.gz.sig +0 -0
@@ -37,13 +37,13 @@ class ReplicaSetAckTest < Test::Unit::TestCase
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def test_safe_mode_with_w_failure
|
40
|
-
assert_raise_error WriteConcernError
|
40
|
+
assert_raise_error WriteConcernError do
|
41
41
|
@col.insert({:foo => 1}, :w => 4, :wtimeout => 1, :fsync => true)
|
42
42
|
end
|
43
|
-
assert_raise_error WriteConcernError
|
43
|
+
assert_raise_error WriteConcernError do
|
44
44
|
@col.update({:foo => 1}, {:foo => 2}, :w => 4, :wtimeout => 1, :fsync => true)
|
45
45
|
end
|
46
|
-
assert_raise_error WriteConcernError
|
46
|
+
assert_raise_error WriteConcernError do
|
47
47
|
@col.remove({:foo => 2}, :w => 4, :wtimeout => 1, :fsync => true)
|
48
48
|
end
|
49
49
|
if @client.server_version >= '2.5.4'
|
@@ -23,7 +23,10 @@ module BasicAuthTests
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def teardown
|
26
|
-
@client
|
26
|
+
return unless @client && @db
|
27
|
+
unless has_auth?(TEST_DB, TEST_USER)
|
28
|
+
@client[TEST_DB].authenticate(TEST_USER, TEST_USER_PWD)
|
29
|
+
end
|
27
30
|
|
28
31
|
if @client.server_version < '2.5'
|
29
32
|
@db['system.users'].remove
|
@@ -42,6 +45,16 @@ module BasicAuthTests
|
|
42
45
|
@client.auths.any? { |a| a[:source] == db_name && a[:username] == username }
|
43
46
|
end
|
44
47
|
|
48
|
+
def test_descriptive_mech_error
|
49
|
+
assert_raise_error Mongo::MongoArgumentError, Mongo::Authentication::MECHANISM_ERROR do
|
50
|
+
@db.authenticate('emily', nil, nil, nil, 'FAKE_MECHANISM')
|
51
|
+
end
|
52
|
+
assert_raise_error Mongo::MongoArgumentError, Mongo::Authentication::MECHANISM_ERROR do
|
53
|
+
uri = "mongodb://user:pwd@host:port/example?authSource=$external&authMechanism=FAKE_MECHANISM"
|
54
|
+
Mongo::MongoClient.from_uri(uri)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
45
58
|
def test_add_remove_user
|
46
59
|
init_auth_basic
|
47
60
|
|
@@ -75,7 +75,7 @@ module GSSAPITests
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def test_wrong_service_name_fails
|
78
|
-
extra_opts = { :
|
78
|
+
extra_opts = { :service_name => 'example' }
|
79
79
|
client = Mongo::MongoClient.new(MONGODB_GSSAPI_HOST, MONGODB_GSSAPI_PORT)
|
80
80
|
if client['admin'].command(:isMaster => 1)['setName']
|
81
81
|
client = Mongo::MongoReplicaSetClient.new(["#{MONGODB_GSSAPI_HOST}:#{MONGODB_GSSAPI_PORT}"])
|
@@ -93,7 +93,7 @@ module GSSAPITests
|
|
93
93
|
require 'cgi'
|
94
94
|
username = CGI::escape(ENV['MONGODB_GSSAPI_USER'])
|
95
95
|
uri = "mongodb://#{username}@#{ENV['MONGODB_GSSAPI_HOST']}:#{ENV['MONGODB_GSSAPI_PORT']}/?" +
|
96
|
-
"authMechanism=GSSAPI&
|
96
|
+
"authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:example"
|
97
97
|
client = @client.class.from_uri(uri)
|
98
98
|
assert_raise_error Mongo::AuthenticationError do
|
99
99
|
client[MONGODB_GSSAPI_DB].command(:dbstats => 1)
|
@@ -101,32 +101,37 @@ module GSSAPITests
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def test_extra_opts
|
104
|
-
extra_opts = { :
|
105
|
-
|
104
|
+
extra_opts = { :service_name => 'example', :canonicalize_host_name => true,
|
105
|
+
:service_realm => 'dumdum' }
|
106
|
+
client = Mongo::MongoClient.new(TEST_HOST, MONGODB_GSSAPI_PORT)
|
106
107
|
set_system_properties
|
107
108
|
|
108
109
|
Mongo::Sasl::GSSAPI.expects(:authenticate).with do |username, client, socket, opts|
|
109
|
-
assert_equal opts[:
|
110
|
+
assert_equal opts[:service_name], extra_opts[:service_name]
|
110
111
|
assert_equal opts[:canonicalize_host_name], extra_opts[:canonicalize_host_name]
|
112
|
+
assert_equal opts[:service_realm], extra_opts[:service_realm]
|
111
113
|
[ username, client, socket, opts ]
|
112
114
|
end.returns('ok' => true )
|
113
115
|
client[MONGODB_GSSAPI_DB].authenticate(MONGODB_GSSAPI_USER, nil, nil, nil, 'GSSAPI', extra_opts)
|
114
116
|
end
|
115
117
|
|
116
118
|
def test_extra_opts_uri
|
117
|
-
extra_opts = { :
|
119
|
+
extra_opts = { :service_name => 'example', :canonicalize_host_name => true,
|
120
|
+
:service_realm => 'dumdum' }
|
118
121
|
set_system_properties
|
119
122
|
|
120
123
|
Mongo::Sasl::GSSAPI.expects(:authenticate).with do |username, client, socket, opts|
|
121
|
-
assert_equal opts[:
|
124
|
+
assert_equal opts[:service_name], extra_opts[:service_name]
|
122
125
|
assert_equal opts[:canonicalize_host_name], extra_opts[:canonicalize_host_name]
|
126
|
+
assert_equal opts[:service_realm], extra_opts[:service_realm]
|
123
127
|
[ username, client, socket, opts ]
|
124
128
|
end.returns('ok' => true)
|
125
129
|
|
126
130
|
require 'cgi'
|
127
131
|
username = CGI::escape(ENV['MONGODB_GSSAPI_USER'])
|
128
132
|
uri = "mongodb://#{username}@#{ENV['MONGODB_GSSAPI_HOST']}:#{ENV['MONGODB_GSSAPI_PORT']}/?" +
|
129
|
-
"authMechanism=GSSAPI&
|
133
|
+
"authMechanism=GSSAPI&authmechanismproperties=SERVICE_NAME:example," +
|
134
|
+
"CANONICALIZE_HOST_NAME:true,SERVICE_REALM:dumdum"
|
130
135
|
client = @client.class.from_uri(uri)
|
131
136
|
client.expects(:receive_message).returns([[{ 'ok' => 1 }], 1, 1])
|
132
137
|
client[MONGODB_GSSAPI_DB].command(:dbstats => 1)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# Copyright (C) 2014 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module SCRAMTests
|
16
|
+
|
17
|
+
def setup_conversation
|
18
|
+
SecureRandom.expects(:base64).returns('NDA2NzU3MDY3MDYwMTgy')
|
19
|
+
@password = Digest::MD5.hexdigest("user:mongo:pencil")
|
20
|
+
@scram = Mongo::Authentication::SCRAM.new({ :username => 'user' }, @password)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_scram_authenticate
|
24
|
+
if @version.to_s > '2.7'
|
25
|
+
@client.clear_auths
|
26
|
+
assert @db.authenticate(TEST_USER, TEST_USER_PWD, nil, 'admin', 'SCRAM-SHA-1')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_scram_conversation_start
|
31
|
+
setup_conversation
|
32
|
+
command = @scram.start
|
33
|
+
assert_equal 1, command['saslStart']
|
34
|
+
assert_equal 'SCRAM-SHA-1', command['mechanism']
|
35
|
+
assert_equal 'n,,n=user,r=NDA2NzU3MDY3MDYwMTgy', command['payload'].to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_scram_conversation_continue
|
39
|
+
setup_conversation
|
40
|
+
payload = BSON::Binary.new(
|
41
|
+
'r=NDA2NzU3MDY3MDYwMTgyt7/+IWaw1HaZZ5NmPJUTWapLpH2Gg+d8,s=AVvQXzAbxweH2RYDICaplw==,i=10000'
|
42
|
+
)
|
43
|
+
reply = { 'conversationId' => 1, 'done' => false, 'payload' => payload, 'ok' => 1.0 }
|
44
|
+
command = @scram.continue(reply)
|
45
|
+
assert_equal 1, command['saslContinue']
|
46
|
+
assert_equal 1, command['conversationId']
|
47
|
+
assert_equal(
|
48
|
+
'c=biws,r=NDA2NzU3MDY3MDYwMTgyt7/+IWaw1HaZZ5NmPJUTWapLpH2Gg+d8,p=qYUYNy6SQ9Jucq9rFA9nVgXQdbM=',
|
49
|
+
command['payload'].to_s
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_scram_conversation_continue_with_invalid_nonce
|
54
|
+
setup_conversation
|
55
|
+
payload = BSON::Binary.new(
|
56
|
+
'r=NDA2NzU4MDY3MDYwMTgyt7/+IWaw1HaZZ5NmPJUTWapLpH2Gg+d8,s=AVvQXzAbxweH2RYDICaplw==,i=10000'
|
57
|
+
)
|
58
|
+
reply = { 'conversationId' => 1, 'done' => false, 'payload' => payload, 'ok' => 1.0 }
|
59
|
+
assert_raise_error Mongo::InvalidNonce do
|
60
|
+
@scram.continue(reply)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_scram_conversation_finalize
|
65
|
+
setup_conversation
|
66
|
+
continue_payload = BSON::Binary.new(
|
67
|
+
'r=NDA2NzU3MDY3MDYwMTgyt7/+IWaw1HaZZ5NmPJUTWapLpH2Gg+d8,s=AVvQXzAbxweH2RYDICaplw==,i=10000'
|
68
|
+
)
|
69
|
+
continue_reply = { 'conversationId' => 1, 'done' => false, 'payload' => continue_payload, 'ok' => 1.0 }
|
70
|
+
@scram.continue(continue_reply)
|
71
|
+
payload = BSON::Binary.new('v=gwo9E8+uifshm7ixj441GvIfuUY=')
|
72
|
+
reply = { 'conversationId' => 1, 'done' => false, 'payload' => payload, 'ok' => 1.0 }
|
73
|
+
command = @scram.finalize(reply)
|
74
|
+
assert_equal 1, command['saslContinue']
|
75
|
+
assert_equal 1, command['conversationId']
|
76
|
+
assert_equal '', command['payload'].to_s
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_scram_conversation_finalize_with_invalid_server_signature
|
80
|
+
setup_conversation
|
81
|
+
continue_payload = BSON::Binary.new(
|
82
|
+
'r=NDA2NzU3MDY3MDYwMTgyt7/+IWaw1HaZZ5NmPJUTWapLpH2Gg+d8,s=AVvQXzAbxweH2RYDICaplw==,i=10000'
|
83
|
+
)
|
84
|
+
continue_reply = { 'conversationId' => 1, 'done' => false, 'payload' => continue_payload, 'ok' => 1.0 }
|
85
|
+
@scram.continue(continue_reply)
|
86
|
+
payload = BSON::Binary.new('v=LQ+8yhQeVL2a3Dh+TDJ7xHz4Srk=')
|
87
|
+
reply = { 'conversationId' => 1, 'done' => false, 'payload' => payload, 'ok' => 1.0 }
|
88
|
+
assert_raise_error Mongo::InvalidSignature do
|
89
|
+
@scram.finalize(reply)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/test/tools/mongo_config.rb
CHANGED
@@ -121,6 +121,7 @@ module Mongo
|
|
121
121
|
fast_sync = opts[:fastsync] || false
|
122
122
|
auth = opts[:auth] || true
|
123
123
|
ipv6 = opts[:ipv6].nil? ? true : opts[:ipv6]
|
124
|
+
setParameter = opts[:setParameter] || 'enableTestCommands=1'
|
124
125
|
|
125
126
|
params.merge(:command => mongod,
|
126
127
|
:dbpath => path,
|
@@ -129,7 +130,8 @@ module Mongo
|
|
129
130
|
:quiet => quiet,
|
130
131
|
:fastsync => fast_sync,
|
131
132
|
:auth => auth,
|
132
|
-
:ipv6 => ipv6
|
133
|
+
:ipv6 => ipv6,
|
134
|
+
:setParameter => setParameter)
|
133
135
|
end
|
134
136
|
|
135
137
|
def self.key_file(opts)
|
@@ -301,6 +303,11 @@ module Mongo
|
|
301
303
|
|
302
304
|
def initialize(config)
|
303
305
|
@config = config
|
306
|
+
cmd = init_config!
|
307
|
+
super(cmd, @config[:host], @config[:port])
|
308
|
+
end
|
309
|
+
|
310
|
+
def init_config!
|
304
311
|
dbpath = @config[:dbpath]
|
305
312
|
[dbpath, File.dirname(@config[:logpath])].compact.each{|dir| FileUtils.mkdir_p(dir) unless File.directory?(dir) }
|
306
313
|
command = @config[:command] || 'mongod'
|
@@ -314,7 +321,6 @@ module Mongo
|
|
314
321
|
end
|
315
322
|
end
|
316
323
|
cmd = [command, arguments].flatten.compact
|
317
|
-
super(cmd, @config[:host], @config[:port])
|
318
324
|
end
|
319
325
|
|
320
326
|
def start(verifies = DEFAULT_VERIFIES)
|
@@ -334,8 +340,13 @@ module Mongo
|
|
334
340
|
sleep 1
|
335
341
|
end
|
336
342
|
end
|
337
|
-
|
338
|
-
|
343
|
+
if @config.delete(:setParameter)
|
344
|
+
@cmd = init_config!
|
345
|
+
start(verifies)
|
346
|
+
else
|
347
|
+
system "ps -fp #{@pid}; cat #{@config[:logpath]}"
|
348
|
+
raise Mongo::ConnectionFailure, "DbServer.start verify via connection probe failed - port:#{@port.inspect} @pid:#{@pid.inspect} kill:#{Process.kill(0, @pid).inspect} running?:#{running?.inspect} cmd:#{cmd.inspect}"
|
349
|
+
end
|
339
350
|
end
|
340
351
|
|
341
352
|
end
|
@@ -346,7 +357,7 @@ module Mongo
|
|
346
357
|
@config = config
|
347
358
|
@servers = {}
|
348
359
|
Mongo::Config::CLUSTER_OPT_KEYS.each do |key|
|
349
|
-
@servers[key] = @config[key].collect{|conf| DbServer.new(conf)} if @config[key]
|
360
|
+
@servers[key] = @config[key].collect{|conf| p conf; DbServer.new(conf)} if @config[key]
|
350
361
|
end
|
351
362
|
end
|
352
363
|
|
@@ -646,7 +657,8 @@ module Mongo
|
|
646
657
|
servers.each{|server| server.start}
|
647
658
|
# TODO - sharded replica sets - pending
|
648
659
|
if @config[:replicas]
|
649
|
-
repl_set_initiate if repl_set_get_status.first['
|
660
|
+
repl_set_initiate if repl_set_get_status.first['code'] == 94 ||
|
661
|
+
(repl_set_get_status.first['startupStatus'] && repl_set_get_status.first['startupStatus'] == 3)
|
650
662
|
repl_set_startup
|
651
663
|
end
|
652
664
|
if @config[:routers]
|
data/test/unit/client_test.rb
CHANGED
@@ -41,6 +41,10 @@ class ClientUnitTest < Test::Unit::TestCase
|
|
41
41
|
assert_equal 1, @client.primary_pool.size
|
42
42
|
end
|
43
43
|
|
44
|
+
should "set op timeout to default" do
|
45
|
+
assert_equal Mongo::MongoClient::DEFAULT_OP_TIMEOUT, @client.op_timeout
|
46
|
+
end
|
47
|
+
|
44
48
|
should "default slave_ok to false" do
|
45
49
|
assert !@client.slave_ok?
|
46
50
|
end
|
@@ -67,6 +71,26 @@ class ClientUnitTest < Test::Unit::TestCase
|
|
67
71
|
client.send(:initialize, *args)
|
68
72
|
end
|
69
73
|
|
74
|
+
context 'specifying nil op timeout explicitly' do
|
75
|
+
setup do
|
76
|
+
@client = MongoClient.new('localhost', 27017, :connect => false, :op_timeout => nil)
|
77
|
+
end
|
78
|
+
|
79
|
+
should 'set op timeout to nil' do
|
80
|
+
assert_equal nil, @client.op_timeout
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'specifying a different op timeout than default' do
|
85
|
+
setup do
|
86
|
+
@client = MongoClient.new('localhost', 27017, :connect => false, :op_timeout => 50)
|
87
|
+
end
|
88
|
+
|
89
|
+
should 'set op timeout to the specified value' do
|
90
|
+
assert_equal 50, @client.op_timeout
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
70
94
|
context "given a replica set" do
|
71
95
|
|
72
96
|
should "warn if invalid options are specified" do
|
@@ -92,11 +116,21 @@ class ClientUnitTest < Test::Unit::TestCase
|
|
92
116
|
|
93
117
|
context "initializing with a unix socket" do
|
94
118
|
setup do
|
95
|
-
|
119
|
+
@client = MongoClient.new('/tmp/mongod.sock', :connect => false)
|
120
|
+
UNIXSocket.stubs(:new).returns(new_mock_unix_socket)
|
121
|
+
end
|
122
|
+
should "parse a unix socket" do
|
123
|
+
assert_equal "/tmp/mongod.sock", @client.host_port.first
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "initializing with a unix socket in uri" do
|
128
|
+
setup do
|
129
|
+
@client = MongoClient.from_uri("mongodb:///tmp/mongod.sock", :connect => false)
|
96
130
|
UNIXSocket.stubs(:new).returns(new_mock_unix_socket)
|
97
131
|
end
|
98
132
|
should "parse a unix socket" do
|
99
|
-
assert_equal "/tmp/mongod.sock", @
|
133
|
+
assert_equal "/tmp/mongod.sock", @client.host_port.first
|
100
134
|
end
|
101
135
|
end
|
102
136
|
|
@@ -153,11 +187,11 @@ class ClientUnitTest < Test::Unit::TestCase
|
|
153
187
|
|
154
188
|
auth_hash = {
|
155
189
|
:db_name => 'db',
|
190
|
+
:extra=>{},
|
156
191
|
:username => 'hyphen-user_name',
|
157
192
|
:password => 'p-s_s',
|
158
193
|
:source => 'db',
|
159
|
-
:mechanism =>
|
160
|
-
:extra => {}
|
194
|
+
:mechanism => nil
|
161
195
|
}
|
162
196
|
assert_equal auth_hash, @client.auths.first
|
163
197
|
end
|
@@ -272,11 +306,11 @@ class ClientUnitTest < Test::Unit::TestCase
|
|
272
306
|
|
273
307
|
auth_hash = {
|
274
308
|
:db_name => 'db',
|
309
|
+
:extra=>{},
|
275
310
|
:username => 'hyphen-user_name',
|
276
311
|
:password => 'p-s_s',
|
277
312
|
:source => 'db',
|
278
|
-
:mechanism =>
|
279
|
-
:extra => {}
|
313
|
+
:mechanism => nil
|
280
314
|
}
|
281
315
|
assert_equal auth_hash, @client.auths.first
|
282
316
|
end
|
@@ -70,7 +70,17 @@ class ConnectionUnitTest < Test::Unit::TestCase
|
|
70
70
|
|
71
71
|
context "initializing with a unix socket" do
|
72
72
|
setup do
|
73
|
-
|
73
|
+
@connection = Mongo::Connection.new('/tmp/mongod.sock', :safe => true, :connect => false)
|
74
|
+
UNIXSocket.stubs(:new).returns(new_mock_unix_socket)
|
75
|
+
end
|
76
|
+
should "parse a unix socket" do
|
77
|
+
assert_equal "/tmp/mongod.sock", @connection.host_port.first
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "initializing with a unix socket in uri" do
|
82
|
+
setup do
|
83
|
+
@connection = Mongo::Connection.from_uri("mongodb:///tmp/mongod.sock", :connect => false)
|
74
84
|
UNIXSocket.stubs(:new).returns(new_mock_unix_socket)
|
75
85
|
end
|
76
86
|
should "parse a unix socket" do
|
@@ -131,11 +141,11 @@ class ConnectionUnitTest < Test::Unit::TestCase
|
|
131
141
|
|
132
142
|
auth_hash = {
|
133
143
|
:db_name => 'db',
|
144
|
+
:extra=>{},
|
134
145
|
:username => 'hyphen-user_name',
|
135
146
|
:password => 'p-s_s',
|
136
147
|
:source => 'db',
|
137
|
-
:mechanism =>
|
138
|
-
:extra => {}
|
148
|
+
:mechanism => nil
|
139
149
|
}
|
140
150
|
assert_equal auth_hash, @connection.auths.first
|
141
151
|
end
|
@@ -250,11 +260,11 @@ class ConnectionUnitTest < Test::Unit::TestCase
|
|
250
260
|
|
251
261
|
auth_hash = {
|
252
262
|
:db_name => 'db',
|
263
|
+
:extra=>{},
|
253
264
|
:username => 'hyphen-user_name',
|
254
265
|
:password => 'p-s_s',
|
255
266
|
:source => 'db',
|
256
|
-
:mechanism =>
|
257
|
-
:extra => {}
|
267
|
+
:mechanism => nil
|
258
268
|
}
|
259
269
|
assert_equal auth_hash, @connection.auths.first
|
260
270
|
end
|
data/test/unit/db_test.rb
CHANGED
@@ -127,7 +127,7 @@ class DBUnitTest < Test::Unit::TestCase
|
|
127
127
|
end
|
128
128
|
|
129
129
|
should "allow extra authentication options" do
|
130
|
-
extra_opts = { :
|
130
|
+
extra_opts = { :service_name => 'example', :canonicalize_host_name => true }
|
131
131
|
assert @client.expects(:add_auth).with(@db.name, 'emily', nil, nil, 'GSSAPI', extra_opts)
|
132
132
|
@db.authenticate('emily', nil, nil, nil, 'GSSAPI', extra_opts)
|
133
133
|
end
|
data/test/unit/read_pref_test.rb
CHANGED
@@ -74,6 +74,11 @@ class ReadPreferenceUnitTest < Test::Unit::TestCase
|
|
74
74
|
assert_equal false, ReadPreference::secondary_ok?(command)
|
75
75
|
end
|
76
76
|
|
77
|
+
def test_sok_text_returns_true
|
78
|
+
command = BSON::OrderedHash['text', BSON::OrderedHash['search', 'coffee']]
|
79
|
+
assert_equal true, ReadPreference::secondary_ok?(command)
|
80
|
+
end
|
81
|
+
|
77
82
|
def test_cmd_reroute_with_secondary
|
78
83
|
ReadPreference::expects(:warn).with(regexp_matches(/rerouted to primary/))
|
79
84
|
command = BSON::OrderedHash['mapreduce', 'test-collection',
|
@@ -112,4 +117,290 @@ class ReadPreferenceUnitTest < Test::Unit::TestCase
|
|
112
117
|
assert_equal true, ReadPreference::secondary_ok?(command)
|
113
118
|
end
|
114
119
|
|
120
|
+
def test_primary_with_tags_raises_error
|
121
|
+
# Confirm that an error is raised if you provide tags and read pref is primary
|
122
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
123
|
+
client.stubs(:primary_pool).returns(mock_pool)
|
124
|
+
read_pref_tags = {'dc' => 'nyc'}
|
125
|
+
read_pref = client.read_preference.merge(:mode => :primary,
|
126
|
+
:tags => [read_pref_tags],
|
127
|
+
:latency => 6)
|
128
|
+
assert_raise Mongo::MongoArgumentError do
|
129
|
+
client.select_pool(read_pref)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_secondary_pref
|
134
|
+
# Confirm that a primary is not selected
|
135
|
+
primary_pool = mock('pool')
|
136
|
+
|
137
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
138
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 5)
|
139
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
140
|
+
|
141
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
142
|
+
client.stubs(:primary_pool).returns(primary_pool)
|
143
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
144
|
+
|
145
|
+
read_pref = client.read_preference.merge(:mode => :secondary)
|
146
|
+
assert_not_equal Hash.new, client.select_pool(read_pref).tags
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_secondary_tags_pref
|
150
|
+
# Confirm that a secondary with matching tags is selected
|
151
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
152
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 5)
|
153
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
154
|
+
|
155
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
156
|
+
client.stubs(:primary_pool).returns(mock_pool)
|
157
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
158
|
+
|
159
|
+
read_pref_tags = {'dc' => 'nyc'}
|
160
|
+
read_pref = client.read_preference.merge(:mode => :secondary,
|
161
|
+
:tags => [read_pref_tags])
|
162
|
+
assert_equal read_pref_tags, client.select_pool(read_pref).tags
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_secondary_tags_with_latency
|
166
|
+
# Confirm that between more than 1 secondary matching tags, only the one within
|
167
|
+
# max acceptable latency is selected
|
168
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
169
|
+
secondary_nyc2 = mock_pool({'dc' => 'nyc'}, 25)
|
170
|
+
secondary_pools = [secondary_nyc, secondary_nyc2]
|
171
|
+
|
172
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
173
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
174
|
+
|
175
|
+
read_pref_tags = {'dc' => 'nyc'}
|
176
|
+
read_pref = client.read_preference.merge(:mode => :secondary,
|
177
|
+
:tags => [read_pref_tags])
|
178
|
+
assert_equal 5, client.select_pool(read_pref).ping_time
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_secondary_latency_pref
|
182
|
+
# Confirm that only the latency of pools matching tags is considered
|
183
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 10)
|
184
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 5)
|
185
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
186
|
+
|
187
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
188
|
+
client.stubs(:primary_pool).returns(mock_pool)
|
189
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
190
|
+
|
191
|
+
read_pref_tags = {'dc' => 'nyc'}
|
192
|
+
read_pref = client.read_preference.merge(:mode => :secondary,
|
193
|
+
:tags => [read_pref_tags],
|
194
|
+
:latency => 3)
|
195
|
+
assert_equal read_pref_tags, client.select_pool(read_pref).tags
|
196
|
+
end
|
197
|
+
|
198
|
+
def test_primary_preferred_primary_available
|
199
|
+
# Confirm that the primary is always selected if it's available
|
200
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
201
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
|
202
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
203
|
+
primary_pool = mock_pool
|
204
|
+
|
205
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
206
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
207
|
+
client.stubs(:primary_pool).returns(primary_pool)
|
208
|
+
|
209
|
+
read_pref_tags = {'dc' => 'chicago'}
|
210
|
+
read_pref = client.read_preference.merge(:mode => :primary_preferred,
|
211
|
+
:tags => [read_pref_tags],
|
212
|
+
:latency => 6)
|
213
|
+
assert_equal primary_pool, client.select_pool(read_pref)
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_primary_preferred_primary_not_available
|
217
|
+
# Confirm that a secondary with matching tags is selected if primary is
|
218
|
+
# not available
|
219
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
220
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
|
221
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
222
|
+
|
223
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
224
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
225
|
+
|
226
|
+
read_pref_tags = {'dc' => 'chicago'}
|
227
|
+
read_pref = client.read_preference.merge(:mode => :primary_preferred,
|
228
|
+
:tags => [read_pref_tags],
|
229
|
+
:latency => 6)
|
230
|
+
assert_equal read_pref_tags, client.select_pool(read_pref).tags
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_primary_preferred_primary_not_available_and_no_matching_tags
|
234
|
+
# Confirm that tags are taken into account if primary is not available and
|
235
|
+
# secondaries are considered for selection.
|
236
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
237
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
|
238
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
239
|
+
|
240
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
241
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
242
|
+
|
243
|
+
read_pref_tags = {'dc' => 'other_city'}
|
244
|
+
read_pref = client.read_preference.merge(:mode => :primary_preferred,
|
245
|
+
:tags => [read_pref_tags],
|
246
|
+
:latency => 6)
|
247
|
+
assert_equal nil, client.select_pool(read_pref)
|
248
|
+
end
|
249
|
+
|
250
|
+
def test_secondary_preferred_with_tags
|
251
|
+
# Confirm that tags are taken into account
|
252
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
253
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
|
254
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
255
|
+
|
256
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
257
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
258
|
+
|
259
|
+
read_pref_tags = {'dc' => 'chicago'}
|
260
|
+
read_pref = client.read_preference.merge(:mode => :secondary_preferred,
|
261
|
+
:tags => [read_pref_tags],
|
262
|
+
:latency => 6)
|
263
|
+
assert_equal read_pref_tags, client.select_pool(read_pref).tags
|
264
|
+
end
|
265
|
+
|
266
|
+
def test_secondary_preferred_with_no_matching_tags
|
267
|
+
# Confirm that the primary is selected if no secondaries with matching tags
|
268
|
+
# are found
|
269
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
270
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
|
271
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
272
|
+
|
273
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
274
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
275
|
+
client.stubs(:primary_pool).returns(mock_pool)
|
276
|
+
|
277
|
+
read_pref_tags = {'dc' => 'other_city'}
|
278
|
+
read_pref = client.read_preference.merge(:mode => :secondary_preferred,
|
279
|
+
:tags => [read_pref_tags],
|
280
|
+
:latency => 6)
|
281
|
+
assert_equal Hash.new, client.select_pool(read_pref).tags
|
282
|
+
end
|
283
|
+
|
284
|
+
def test_nearest_with_tags
|
285
|
+
# Confirm that tags are taken into account when selecting nearest
|
286
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
287
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
|
288
|
+
primary_pool = mock_pool
|
289
|
+
pools = [secondary_nyc, secondary_chi, primary_pool]
|
290
|
+
|
291
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
292
|
+
client.stubs(:pools).returns(pools)
|
293
|
+
|
294
|
+
read_pref_tags = {'dc' => 'nyc'}
|
295
|
+
read_pref = client.read_preference.merge(:mode => :nearest,
|
296
|
+
:tags => [read_pref_tags],
|
297
|
+
:latency => 3)
|
298
|
+
assert_equal read_pref_tags, client.select_pool(read_pref).tags
|
299
|
+
end
|
300
|
+
|
301
|
+
def test_nearest
|
302
|
+
# Confirm that the nearest is selected when tags aren't specified
|
303
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
304
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
|
305
|
+
primary_pool = mock_pool({}, 1)
|
306
|
+
pools = [secondary_nyc, secondary_chi, primary_pool]
|
307
|
+
|
308
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
309
|
+
client.stubs(:pools).returns(pools)
|
310
|
+
|
311
|
+
read_pref = client.read_preference.merge(:mode => :nearest,
|
312
|
+
:latency => 3)
|
313
|
+
assert_equal Hash.new, client.select_pool(read_pref).tags
|
314
|
+
end
|
315
|
+
|
316
|
+
def test_nearest_primary_matching
|
317
|
+
# Confirm that a primary matching tags is included in nearest candidates
|
318
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
319
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
|
320
|
+
primary_pool = mock_pool({'dc' => 'boston'}, 1)
|
321
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
322
|
+
|
323
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
324
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
325
|
+
client.stubs(:primary_pool).returns(primary_pool)
|
326
|
+
client.stubs(:pools).returns(secondary_pools << primary_pool)
|
327
|
+
|
328
|
+
read_pref_tags = {'dc' => 'boston'}
|
329
|
+
read_pref = client.read_preference.merge(:mode => :nearest,
|
330
|
+
:tags => [read_pref_tags])
|
331
|
+
assert_equal primary_pool, client.select_pool(read_pref)
|
332
|
+
end
|
333
|
+
|
334
|
+
def test_nearest_primary_not_matching
|
335
|
+
# Confirm that a primary not matching tags is not included in nearest candidates
|
336
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 25)
|
337
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 25)
|
338
|
+
primary_pool = mock_pool({'dc' => 'boston'}, 1)
|
339
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
340
|
+
|
341
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
342
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
343
|
+
client.stubs(:primary_pool).returns(mock_pool)
|
344
|
+
client.stubs(:pools).returns(secondary_pools << primary_pool)
|
345
|
+
|
346
|
+
read_pref_tags = {'dc' => 'SF'}
|
347
|
+
read_pref = client.read_preference.merge(:mode => :nearest,
|
348
|
+
:tags => [read_pref_tags])
|
349
|
+
assert_equal nil, client.select_pool(read_pref)
|
350
|
+
end
|
351
|
+
|
352
|
+
def test_nearest_primary_not_matching_excluded_from_latency_calculations
|
353
|
+
# Confirm that a primary not matching tags is not included in the latency calculations
|
354
|
+
secondary1 = mock_pool({'dc' => 'nyc'}, 10)
|
355
|
+
secondary2 = mock_pool({'dc' => 'nyc'}, 10)
|
356
|
+
primary_pool = mock_pool({'dc' => 'boston'}, 1)
|
357
|
+
secondary_pools = [secondary1, secondary2]
|
358
|
+
|
359
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
360
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
361
|
+
client.stubs(:primary_pool).returns(mock_pool)
|
362
|
+
client.stubs(:pools).returns(secondary_pools << primary_pool)
|
363
|
+
|
364
|
+
read_pref_tags = {'dc' => 'nyc'}
|
365
|
+
read_pref = client.read_preference.merge(:mode => :nearest,
|
366
|
+
:tags => [read_pref_tags],
|
367
|
+
:latency => 5)
|
368
|
+
assert_equal 'nyc', client.select_pool(read_pref).tags['dc']
|
369
|
+
end
|
370
|
+
|
371
|
+
def test_nearest_matching_tags_but_not_available
|
372
|
+
# Confirm that even if a server matches a tag, it's not selected if it's down
|
373
|
+
secondary_nyc = mock_pool({'dc' => 'nyc'}, 5)
|
374
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
|
375
|
+
primary_pool = mock_pool({'dc' => 'chicago'}, nil)
|
376
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
377
|
+
|
378
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
379
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
380
|
+
client.stubs(:primary_pool).returns(primary_pool)
|
381
|
+
client.stubs(:pools).returns(secondary_pools << primary_pool)
|
382
|
+
|
383
|
+
tags = [{'dc' => 'nyc'}, {'dc' => 'chicago'}, {}]
|
384
|
+
read_pref = client.read_preference.merge(:mode => :nearest,
|
385
|
+
:tags => tags)
|
386
|
+
assert_equal secondary_nyc, client.select_pool(read_pref)
|
387
|
+
end
|
388
|
+
|
389
|
+
def test_nearest_multiple_tags
|
390
|
+
# Confirm that with multiple tags in the read preference, servers are still selected
|
391
|
+
secondary_nyc = mock_pool({}, 5)
|
392
|
+
secondary_chi = mock_pool({'dc' => 'chicago'}, 10)
|
393
|
+
primary_pool = mock_pool({}, 1)
|
394
|
+
secondary_pools = [secondary_nyc, secondary_chi]
|
395
|
+
|
396
|
+
client = MongoReplicaSetClient.new(["#{TEST_HOST}:#{TEST_PORT}"], :connect => false)
|
397
|
+
client.stubs(:secondary_pools).returns(secondary_pools)
|
398
|
+
client.stubs(:primary_pool).returns(mock_pool)
|
399
|
+
client.stubs(:pools).returns(secondary_pools << primary_pool)
|
400
|
+
|
401
|
+
tags = [{'dc' => 'nyc'}, {'dc' => 'chicago'}, {}]
|
402
|
+
read_pref = client.read_preference.merge(:mode => :nearest,
|
403
|
+
:tags => tags)
|
404
|
+
assert_equal secondary_chi, client.select_pool(read_pref)
|
405
|
+
end
|
115
406
|
end
|