mongo 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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}, :sock => socket)
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
- :sock => socket, :check_response => false)
236
+ :socket => socket, :check_response => false)
222
237
 
223
238
  if !Mongo::Support.ok?(config)
224
239
  raise ReplicaSetConnectionError, config['errmsg']
@@ -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
- return socket if socket
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
- # Otherwise, wait
116
- if @logger
117
- @logger.warn "MONGODB Waiting for available connection; " +
118
- "#{@checked_out.size} of #{@size} connections checked out."
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
@@ -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
@@ -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.6"
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