mongo 1.11.1 → 1.12.0.rc0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|