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.
@@ -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