mongo 1.2.0 → 1.2.1
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.
- data/README.md +15 -3
- data/docs/FAQ.md +4 -0
- data/docs/HISTORY.md +11 -4
- data/lib/mongo.rb +1 -1
- data/lib/mongo/collection.rb +28 -25
- data/lib/mongo/connection.rb +54 -12
- data/lib/mongo/cursor.rb +17 -11
- data/lib/mongo/db.rb +31 -11
- data/lib/mongo/repl_set_connection.rb +17 -2
- data/lib/mongo/util/pool.rb +50 -6
- data/test/auxillary/repl_set_auth_test.rb +58 -0
- data/test/auxillary/threaded_authentication_test.rb +101 -0
- data/test/bson/bson_test.rb +43 -0
- data/test/connection_test.rb +1 -1
- data/test/db_api_test.rb +0 -37
- data/test/load/thin/load.rb +24 -0
- data/test/load/unicorn/load.rb +23 -0
- data/test/load/unicorn/unicorn.rb +29 -0
- data/test/tools/auth_repl_set_manager.rb +14 -0
- data/test/tools/load.rb +58 -0
- data/test/tools/repl_set_manager.rb +34 -9
- data/test/tools/sharding_manager.rb +202 -0
- data/test/tools/test.rb +3 -12
- data/test/unit/collection_test.rb +19 -22
- data/test/unit/connection_test.rb +0 -1
- data/test/unit/db_test.rb +1 -0
- metadata +23 -11
@@ -125,6 +125,7 @@ module Mongo
|
|
125
125
|
end
|
126
126
|
end
|
127
127
|
end
|
128
|
+
alias :reconnect :connect
|
128
129
|
|
129
130
|
def connecting?
|
130
131
|
@nodes_to_try.length > 0
|
@@ -167,6 +168,20 @@ module Mongo
|
|
167
168
|
@read_secondary || @slave_ok
|
168
169
|
end
|
169
170
|
|
171
|
+
def authenticate_pools
|
172
|
+
super
|
173
|
+
@secondary_pools.each do |pool|
|
174
|
+
pool.authenticate_existing
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def logout_pools(db)
|
179
|
+
super
|
180
|
+
@secondary_pools.each do |pool|
|
181
|
+
pool.logout_existing(db)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
170
185
|
private
|
171
186
|
|
172
187
|
def check_is_master(node)
|
@@ -175,7 +190,7 @@ module Mongo
|
|
175
190
|
socket = TCPSocket.new(host, port)
|
176
191
|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
177
192
|
|
178
|
-
config = self['admin'].command({:ismaster => 1}, :
|
193
|
+
config = self['admin'].command({:ismaster => 1}, :socket => socket)
|
179
194
|
|
180
195
|
check_set_name(config, socket)
|
181
196
|
rescue OperationFailure, SocketError, SystemCallError, IOError => ex
|
@@ -218,7 +233,7 @@ module Mongo
|
|
218
233
|
def check_set_name(config, socket)
|
219
234
|
if @replica_set
|
220
235
|
config = self['admin'].command({:replSetGetStatus => 1},
|
221
|
-
:
|
236
|
+
:socket => socket, :check_response => false)
|
222
237
|
|
223
238
|
if !Mongo::Support.ok?(config)
|
224
239
|
raise ReplicaSetConnectionError, config['errmsg']
|
data/lib/mongo/util/pool.rb
CHANGED
@@ -37,6 +37,9 @@ module Mongo
|
|
37
37
|
# Condition variable for signal and wait
|
38
38
|
@queue = ConditionVariable.new
|
39
39
|
|
40
|
+
# Operations to perform on a socket
|
41
|
+
@socket_ops = Hash.new { |h, k| h[k] = [] }
|
42
|
+
|
40
43
|
@sockets = []
|
41
44
|
@checked_out = []
|
42
45
|
end
|
@@ -75,11 +78,42 @@ module Mongo
|
|
75
78
|
rescue => ex
|
76
79
|
raise ConnectionFailure, "Failed to connect socket: #{ex}"
|
77
80
|
end
|
81
|
+
|
82
|
+
# If any saved authentications exist, we want to apply those
|
83
|
+
# when creating new sockets.
|
84
|
+
@connection.apply_saved_authentication(:socket => socket)
|
85
|
+
|
78
86
|
@sockets << socket
|
79
87
|
@checked_out << socket
|
80
88
|
socket
|
81
89
|
end
|
82
90
|
|
91
|
+
# If a user calls DB#authenticate, and several sockets exist,
|
92
|
+
# then we need a way to apply the authentication on each socket.
|
93
|
+
# So we store the apply_authentication method, and this will be
|
94
|
+
# applied right before the next use of each socket.
|
95
|
+
def authenticate_existing
|
96
|
+
@connection_mutex.synchronize do
|
97
|
+
@sockets.each do |socket|
|
98
|
+
@socket_ops[socket] << Proc.new do
|
99
|
+
@connection.apply_saved_authentication(:socket => socket)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Store the logout op for each existing socket to be applied before
|
106
|
+
# the next use of each socket.
|
107
|
+
def logout_existing(db)
|
108
|
+
@connection_mutex.synchronize do
|
109
|
+
@sockets.each do |socket|
|
110
|
+
@socket_ops[socket] << Proc.new do
|
111
|
+
@connection.db(db).issue_logout(:socket => socket)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
83
117
|
# Checks out the first available socket from the pool.
|
84
118
|
#
|
85
119
|
# This method is called exclusively from #checkout;
|
@@ -110,14 +144,24 @@ module Mongo
|
|
110
144
|
checkout_new_socket
|
111
145
|
end
|
112
146
|
|
113
|
-
|
147
|
+
if socket
|
148
|
+
|
149
|
+
# This call all procs, in order, scoped to existing sockets.
|
150
|
+
# At the moment, we use this to lazily authenticate and
|
151
|
+
# logout existing socket connections.
|
152
|
+
@socket_ops[socket].reject! do |op|
|
153
|
+
op.call
|
154
|
+
end
|
114
155
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
156
|
+
return socket
|
157
|
+
else
|
158
|
+
# Otherwise, wait
|
159
|
+
if @logger
|
160
|
+
@logger.warn "MONGODB Waiting for available connection; " +
|
161
|
+
"#{@checked_out.size} of #{@size} connections checked out."
|
162
|
+
end
|
163
|
+
@queue.wait(@connection_mutex)
|
119
164
|
end
|
120
|
-
@queue.wait(@connection_mutex)
|
121
165
|
end
|
122
166
|
end
|
123
167
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require './test/test_helper'
|
3
|
+
require './test/tools/auth_repl_set_manager'
|
4
|
+
|
5
|
+
class AuthTest < Test::Unit::TestCase
|
6
|
+
include Mongo
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@manager = AuthReplSetManager.new(:start_port => 40000)
|
10
|
+
@manager.start_set
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
@manager.cleanup_set
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_repl_set_auth
|
18
|
+
@conn = ReplSetConnection.new([@manager.host, @manager.ports[0]], [@manager.host, @manager.ports[1]],
|
19
|
+
[@manager.host, @manager.ports[2]], :name => @manager.name)
|
20
|
+
|
21
|
+
# Add an admin user
|
22
|
+
@conn['admin'].add_user("me", "secret")
|
23
|
+
|
24
|
+
# Ensure that insert fails
|
25
|
+
assert_raise_error Mongo::OperationFailure, "unauthorized" do
|
26
|
+
@conn['foo']['stuff'].insert({:a => 2}, :safe => {:w => 3})
|
27
|
+
end
|
28
|
+
|
29
|
+
# Then authenticate
|
30
|
+
assert @conn['admin'].authenticate("me", "secret")
|
31
|
+
|
32
|
+
# Insert should succeed now
|
33
|
+
assert @conn['foo']['stuff'].insert({:a => 2}, :safe => {:w => 3})
|
34
|
+
|
35
|
+
# So should a query
|
36
|
+
assert @conn['foo']['stuff'].find_one
|
37
|
+
|
38
|
+
# But not when we logout
|
39
|
+
@conn['admin'].logout
|
40
|
+
|
41
|
+
assert_raise_error Mongo::OperationFailure, "unauthorized" do
|
42
|
+
@conn['foo']['stuff'].find_one
|
43
|
+
end
|
44
|
+
|
45
|
+
# Same should apply to a random secondary
|
46
|
+
@slave1 = Connection.new(@conn.secondary_pools[0].host,
|
47
|
+
@conn.secondary_pools[0].port, :slave_ok => true)
|
48
|
+
|
49
|
+
# Find should fail
|
50
|
+
assert_raise_error Mongo::OperationFailure, "unauthorized" do
|
51
|
+
@slave1['foo']['stuff'].find_one
|
52
|
+
end
|
53
|
+
|
54
|
+
# But not when authenticated
|
55
|
+
@slave1['admin'].authenticate("me", "secret")
|
56
|
+
assert @slave1['foo']['stuff'].find_one
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'mongo'
|
3
|
+
require 'thread'
|
4
|
+
require 'test/unit'
|
5
|
+
require './test/test_helper'
|
6
|
+
|
7
|
+
# NOTE: This test requires bouncing the server.
|
8
|
+
# It also requires that a user exists on the admin database.
|
9
|
+
class AuthenticationTest < Test::Unit::TestCase
|
10
|
+
include Mongo
|
11
|
+
|
12
|
+
def setup
|
13
|
+
@conn = standard_connection(:pool_size => 10)
|
14
|
+
@db1 = @conn.db('mongo-ruby-test-auth1')
|
15
|
+
@db2 = @conn.db('mongo-ruby-test-auth2')
|
16
|
+
@admin = @conn.db('admin')
|
17
|
+
end
|
18
|
+
|
19
|
+
def teardown
|
20
|
+
@db1.authenticate('user1', 'secret')
|
21
|
+
@db2.authenticate('user2', 'secret')
|
22
|
+
@conn.drop_database('mongo-ruby-test-auth1')
|
23
|
+
@conn.drop_database('mongo-ruby-test-auth2')
|
24
|
+
end
|
25
|
+
|
26
|
+
def threaded_exec
|
27
|
+
threads = []
|
28
|
+
|
29
|
+
100.times do
|
30
|
+
threads << Thread.new do
|
31
|
+
yield
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
100.times do |n|
|
36
|
+
threads[n].join
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_authenticate
|
41
|
+
@admin.authenticate('bob', 'secret')
|
42
|
+
@db1.add_user('user1', 'secret')
|
43
|
+
@db2.add_user('user2', 'secret')
|
44
|
+
@admin.logout
|
45
|
+
|
46
|
+
threaded_exec do
|
47
|
+
assert_raise Mongo::OperationFailure do
|
48
|
+
@db1['stuff'].insert({:a => 2}, :safe => true)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
threaded_exec do
|
53
|
+
assert_raise Mongo::OperationFailure do
|
54
|
+
@db2['stuff'].insert({:a => 2}, :safe => true)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
@db1.authenticate('user1', 'secret')
|
59
|
+
@db2.authenticate('user2', 'secret')
|
60
|
+
|
61
|
+
threaded_exec do
|
62
|
+
assert @db1['stuff'].insert({:a => 2}, :safe => true)
|
63
|
+
end
|
64
|
+
|
65
|
+
threaded_exec do
|
66
|
+
assert @db2['stuff'].insert({:a => 2}, :safe => true)
|
67
|
+
end
|
68
|
+
|
69
|
+
puts "Please bounce the server."
|
70
|
+
gets
|
71
|
+
|
72
|
+
# Here we reconnect.
|
73
|
+
begin
|
74
|
+
@db1['stuff'].find.to_a
|
75
|
+
rescue Mongo::ConnectionFailure
|
76
|
+
end
|
77
|
+
|
78
|
+
threaded_exec do
|
79
|
+
assert @db1['stuff'].insert({:a => 2}, :safe => true)
|
80
|
+
end
|
81
|
+
|
82
|
+
threaded_exec do
|
83
|
+
assert @db2['stuff'].insert({:a => 2}, :safe => true)
|
84
|
+
end
|
85
|
+
|
86
|
+
@db1.logout
|
87
|
+
threaded_exec do
|
88
|
+
assert_raise Mongo::OperationFailure do
|
89
|
+
@db1['stuff'].insert({:a => 2}, :safe => true)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
@db2.logout
|
94
|
+
threaded_exec do
|
95
|
+
assert_raise Mongo::OperationFailure do
|
96
|
+
assert @db2['stuff'].insert({:a => 2}, :safe => true)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
data/test/bson/bson_test.rb
CHANGED
@@ -562,4 +562,47 @@ class BSONTest < Test::Unit::TestCase
|
|
562
562
|
end
|
563
563
|
end
|
564
564
|
|
565
|
+
def test_invalid_key_names
|
566
|
+
assert @encoder.serialize({"hello" => "world"}, true)
|
567
|
+
assert @encoder.serialize({"hello" => {"hello" => "world"}}, true)
|
568
|
+
|
569
|
+
assert @encoder.serialize({"he$llo" => "world"}, true)
|
570
|
+
assert @encoder.serialize({"hello" => {"hell$o" => "world"}}, true)
|
571
|
+
|
572
|
+
assert_raise BSON::InvalidDocument do
|
573
|
+
@encoder.serialize({"he\0llo" => "world"}, true)
|
574
|
+
end
|
575
|
+
|
576
|
+
assert_raise_error BSON::InvalidKeyName, "$hello" do
|
577
|
+
@encoder.serialize({"$hello" => "world"}, true)
|
578
|
+
end
|
579
|
+
|
580
|
+
assert_raise BSON::InvalidKeyName do
|
581
|
+
@encoder.serialize({"hello" => {"$hello" => "world"}}, true)
|
582
|
+
end
|
583
|
+
|
584
|
+
assert_raise_error BSON::InvalidKeyName, ".hello" do
|
585
|
+
@encoder.serialize({".hello" => "world"}, true)
|
586
|
+
end
|
587
|
+
|
588
|
+
assert_raise BSON::InvalidKeyName do
|
589
|
+
@encoder.serialize({"hello" => {".hello" => "world"}}, true)
|
590
|
+
end
|
591
|
+
|
592
|
+
assert_raise BSON::InvalidKeyName do
|
593
|
+
@encoder.serialize({"hello." => "world"}, true)
|
594
|
+
end
|
595
|
+
|
596
|
+
assert_raise BSON::InvalidKeyName do
|
597
|
+
@encoder.serialize({"hello" => {"hello." => "world"}}, true)
|
598
|
+
end
|
599
|
+
|
600
|
+
assert_raise BSON::InvalidKeyName do
|
601
|
+
@encoder.serialize({"hel.lo" => "world"}, true)
|
602
|
+
end
|
603
|
+
|
604
|
+
assert_raise BSON::InvalidKeyName do
|
605
|
+
@encoder.serialize({"hello" => {"hel.lo" => "world"}}, true)
|
606
|
+
end
|
607
|
+
end
|
565
608
|
end
|
data/test/connection_test.rb
CHANGED
@@ -167,7 +167,7 @@ class TestConnection < Test::Unit::TestCase
|
|
167
167
|
|
168
168
|
def test_max_bson_size_value
|
169
169
|
conn = standard_connection
|
170
|
-
if conn.server_version > "1.
|
170
|
+
if conn.server_version > "1.7.2"
|
171
171
|
assert_equal conn['admin'].command({:ismaster => 1})['maxBsonObjectSize'], conn.max_bson_size
|
172
172
|
end
|
173
173
|
|
data/test/db_api_test.rb
CHANGED
@@ -621,43 +621,6 @@ class DBAPITest < Test::Unit::TestCase
|
|
621
621
|
assert_equal("mike", @@coll.find_one()["hello"])
|
622
622
|
end
|
623
623
|
|
624
|
-
def test_invalid_key_names
|
625
|
-
@@coll.remove
|
626
|
-
|
627
|
-
@@coll.insert({"hello" => "world"})
|
628
|
-
@@coll.insert({"hello" => {"hello" => "world"}})
|
629
|
-
|
630
|
-
assert_raise BSON::InvalidKeyName do
|
631
|
-
@@coll.insert({"$hello" => "world"})
|
632
|
-
end
|
633
|
-
|
634
|
-
assert_raise BSON::InvalidKeyName do
|
635
|
-
@@coll.insert({"hello" => {"$hello" => "world"}})
|
636
|
-
end
|
637
|
-
|
638
|
-
@@coll.insert({"he$llo" => "world"})
|
639
|
-
@@coll.insert({"hello" => {"hell$o" => "world"}})
|
640
|
-
|
641
|
-
assert_raise BSON::InvalidKeyName do
|
642
|
-
@@coll.insert({".hello" => "world"})
|
643
|
-
end
|
644
|
-
assert_raise BSON::InvalidKeyName do
|
645
|
-
@@coll.insert({"hello" => {".hello" => "world"}})
|
646
|
-
end
|
647
|
-
assert_raise BSON::InvalidKeyName do
|
648
|
-
@@coll.insert({"hello." => "world"})
|
649
|
-
end
|
650
|
-
assert_raise BSON::InvalidKeyName do
|
651
|
-
@@coll.insert({"hello" => {"hello." => "world"}})
|
652
|
-
end
|
653
|
-
assert_raise BSON::InvalidKeyName do
|
654
|
-
@@coll.insert({"hel.lo" => "world"})
|
655
|
-
end
|
656
|
-
assert_raise BSON::InvalidKeyName do
|
657
|
-
@@coll.insert({"hello" => {"hel.lo" => "world"}})
|
658
|
-
end
|
659
|
-
end
|
660
|
-
|
661
624
|
def test_collection_names
|
662
625
|
assert_raise TypeError do
|
663
626
|
@@db.collection(5)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '..', '..', 'lib', 'mongo')
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
$con = Mongo::Connection.new
|
5
|
+
$db = $con['foo']
|
6
|
+
|
7
|
+
class Load < Sinatra::Base
|
8
|
+
|
9
|
+
configure do
|
10
|
+
LOGGER = Logger.new("sinatra.log")
|
11
|
+
enable :logging, :dump_errors
|
12
|
+
set :raise_errors, true
|
13
|
+
end
|
14
|
+
|
15
|
+
get '/' do
|
16
|
+
3.times do |n|
|
17
|
+
if (v=$db.eval("1 + #{n}")) != 1 + n
|
18
|
+
STDERR << "#{1 + n} expected but got #{v}"
|
19
|
+
raise StandardError, "#{1 + n} expected but got #{v}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'mongo')
|
2
|
+
|
3
|
+
$con = Mongo::Connection.new
|
4
|
+
$db = $con['foo']
|
5
|
+
|
6
|
+
class Load < Sinatra::Base
|
7
|
+
|
8
|
+
configure do
|
9
|
+
LOGGER = Logger.new("sinatra.log")
|
10
|
+
enable :logging, :dump_errors
|
11
|
+
set :raise_errors, true
|
12
|
+
end
|
13
|
+
|
14
|
+
get '/' do
|
15
|
+
3.times do |n|
|
16
|
+
if (v=$db.eval("1 + #{n}")) != 1 + n
|
17
|
+
STDERR << "#{1 + n} expected but got #{v}"
|
18
|
+
raise StandardError, "#{1 + n} expected but got #{v}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# set path to app that will be used to configure unicorn,
|
2
|
+
# # note the trailing slash in this example
|
3
|
+
@dir = "/home/kyle/work/10gen/ruby-driver/test/load/"
|
4
|
+
|
5
|
+
worker_processes 10
|
6
|
+
working_directory @dir
|
7
|
+
|
8
|
+
preload_app true
|
9
|
+
|
10
|
+
timeout 30
|
11
|
+
|
12
|
+
# Specify path to socket unicorn listens to,
|
13
|
+
# we will use this in our nginx.conf later
|
14
|
+
listen "#{@dir}tmp/sockets/unicorn.sock", :backlog => 64
|
15
|
+
|
16
|
+
# Set process id path
|
17
|
+
pid "#{@dir}tmp/pids/unicorn.pid"
|
18
|
+
|
19
|
+
# # Set log file paths
|
20
|
+
stderr_path "#{@dir}log/unicorn.stderr.log"
|
21
|
+
stdout_path "#{@dir}log/unicorn.stdout.log"
|
22
|
+
|
23
|
+
# NOTE: You need this when using forking web servers!
|
24
|
+
after_fork do |server, worker|
|
25
|
+
$con.close if $con
|
26
|
+
$con = Mongo::Connection.new
|
27
|
+
$db = $con['foo']
|
28
|
+
STDERR << "FORKED #{server} #{worker}"
|
29
|
+
end
|