mongo 1.8.6 → 1.12.5
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/LICENSE +1 -1
- data/README.md +114 -282
- data/Rakefile +18 -4
- data/VERSION +1 -1
- data/bin/mongo_console +27 -5
- data/lib/mongo/bulk_write_collection_view.rb +387 -0
- data/lib/mongo/collection.rb +283 -222
- data/lib/mongo/collection_writer.rb +364 -0
- data/lib/mongo/{util → connection}/node.rb +58 -6
- data/lib/mongo/{util → connection}/pool.rb +61 -37
- data/lib/mongo/{util → connection}/pool_manager.rb +72 -22
- data/lib/mongo/{util → connection}/sharding_pool_manager.rb +13 -0
- data/lib/mongo/connection/socket/socket_util.rb +37 -0
- data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
- data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
- data/lib/mongo/connection/socket/unix_socket.rb +39 -0
- data/lib/mongo/connection/socket.rb +18 -0
- data/lib/mongo/connection.rb +19 -0
- data/lib/mongo/cursor.rb +183 -57
- data/lib/mongo/db.rb +302 -138
- data/lib/mongo/exception.rb +145 -0
- data/lib/mongo/functional/authentication.rb +455 -0
- data/lib/mongo/{util → functional}/logging.rb +23 -7
- data/lib/mongo/functional/read_preference.rb +183 -0
- data/lib/mongo/functional/scram.rb +556 -0
- data/lib/mongo/functional/uri_parser.rb +409 -0
- data/lib/mongo/{util → functional}/write_concern.rb +21 -9
- data/lib/mongo/functional.rb +20 -0
- data/lib/mongo/gridfs/grid.rb +19 -8
- data/lib/mongo/gridfs/grid_ext.rb +14 -0
- data/lib/mongo/gridfs/grid_file_system.rb +17 -4
- data/lib/mongo/gridfs/grid_io.rb +21 -9
- data/lib/mongo/gridfs.rb +18 -0
- data/lib/mongo/legacy.rb +76 -7
- data/lib/mongo/mongo_client.rb +246 -206
- data/lib/mongo/mongo_replica_set_client.rb +65 -15
- data/lib/mongo/mongo_sharded_client.rb +18 -3
- data/lib/mongo/networking.rb +47 -18
- data/lib/mongo/{util → utils}/conversions.rb +18 -3
- data/lib/mongo/{util → utils}/core_ext.rb +15 -32
- data/lib/mongo/{util → utils}/server_version.rb +15 -0
- data/lib/mongo/{util → utils}/support.rb +22 -55
- data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
- data/lib/mongo/utils.rb +19 -0
- data/lib/mongo.rb +44 -26
- data/mongo.gemspec +2 -2
- data/test/functional/authentication_test.rb +31 -10
- data/test/functional/bulk_api_stress_test.rb +133 -0
- data/test/functional/bulk_write_collection_view_test.rb +1198 -0
- data/test/functional/client_test.rb +627 -0
- data/test/functional/collection_test.rb +1419 -654
- data/test/functional/collection_writer_test.rb +83 -0
- data/test/functional/conversions_test.rb +46 -2
- data/test/functional/cursor_fail_test.rb +17 -9
- data/test/functional/cursor_message_test.rb +28 -15
- data/test/functional/cursor_test.rb +300 -165
- data/test/functional/db_api_test.rb +294 -264
- data/test/functional/db_connection_test.rb +15 -3
- data/test/functional/db_test.rb +165 -99
- data/test/functional/grid_file_system_test.rb +124 -112
- data/test/functional/grid_io_test.rb +17 -3
- data/test/functional/grid_test.rb +16 -2
- data/test/functional/pool_test.rb +99 -10
- data/test/functional/safe_test.rb +18 -4
- data/test/functional/ssl_test.rb +29 -0
- data/test/functional/support_test.rb +14 -0
- data/test/functional/timeout_test.rb +27 -27
- data/test/functional/uri_test.rb +268 -22
- data/test/functional/write_concern_test.rb +19 -5
- data/test/helpers/general.rb +50 -0
- data/test/helpers/test_unit.rb +476 -0
- data/test/replica_set/authentication_test.rb +28 -11
- data/test/replica_set/basic_test.rb +79 -23
- data/test/replica_set/client_test.rb +253 -124
- data/test/replica_set/connection_test.rb +59 -37
- data/test/replica_set/count_test.rb +18 -2
- data/test/replica_set/cursor_test.rb +30 -8
- data/test/replica_set/insert_test.rb +109 -2
- data/test/replica_set/max_values_test.rb +85 -10
- data/test/replica_set/pinning_test.rb +66 -2
- data/test/replica_set/query_test.rb +17 -3
- data/test/replica_set/read_preference_test.rb +115 -96
- data/test/replica_set/refresh_test.rb +59 -9
- data/test/replica_set/replication_ack_test.rb +32 -11
- data/test/replica_set/ssl_test.rb +32 -0
- data/test/sharded_cluster/basic_test.rb +73 -25
- data/test/shared/authentication/basic_auth_shared.rb +260 -0
- data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
- data/test/shared/authentication/gssapi_shared.rb +176 -0
- data/test/shared/authentication/sasl_plain_shared.rb +96 -0
- data/test/shared/authentication/scram_shared.rb +92 -0
- data/test/shared/ssl_shared.rb +235 -0
- data/test/test_helper.rb +47 -196
- data/test/threading/basic_test.rb +42 -2
- data/test/tools/mongo_config.rb +175 -35
- data/test/tools/mongo_config_test.rb +15 -1
- data/test/unit/client_test.rb +186 -57
- data/test/unit/collection_test.rb +44 -54
- data/test/unit/connection_test.rb +160 -71
- data/test/unit/cursor_test.rb +37 -3
- data/test/unit/db_test.rb +38 -14
- data/test/unit/grid_test.rb +15 -1
- data/test/unit/mongo_sharded_client_test.rb +30 -14
- data/test/unit/node_test.rb +16 -1
- data/test/unit/pool_manager_test.rb +21 -4
- data/test/unit/read_pref_test.rb +386 -1
- data/test/unit/read_test.rb +27 -13
- data/test/unit/safe_test.rb +22 -8
- data/test/unit/sharding_pool_manager_test.rb +25 -4
- data/test/unit/write_concern_test.rb +23 -9
- data.tar.gz.sig +0 -0
- metadata +80 -54
- metadata.gz.sig +0 -0
- data/lib/mongo/exceptions.rb +0 -65
- data/lib/mongo/util/read_preference.rb +0 -112
- data/lib/mongo/util/socket_util.rb +0 -20
- data/lib/mongo/util/ssl_socket.rb +0 -51
- data/lib/mongo/util/tcp_socket.rb +0 -62
- data/lib/mongo/util/thread_local_variable_manager.rb +0 -11
- data/lib/mongo/util/unix_socket.rb +0 -23
- data/lib/mongo/util/uri_parser.rb +0 -337
- data/test/functional/connection_test.rb +0 -449
- data/test/functional/threading_test.rb +0 -95
- data/test/replica_set/complex_connect_test.rb +0 -64
- data/test/shared/authentication.rb +0 -66
- data/test/unit/pool_test.rb +0 -9
- data/test/unit/util_test.rb +0 -55
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
# Copyright (C) 2009-2013 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
|
+
TEST_HOST = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost' unless defined? TEST_HOST
|
|
16
|
+
TEST_DATA = File.join(File.dirname(__FILE__), 'fixtures/data')
|
|
17
|
+
TEST_OP_TIMEOUT = 40
|
|
18
|
+
TEST_BASE = Test::Unit::TestCase
|
|
19
|
+
|
|
20
|
+
unless defined? TEST_PORT
|
|
21
|
+
TEST_PORT = if ENV['MONGO_RUBY_DRIVER_PORT']
|
|
22
|
+
ENV['MONGO_RUBY_DRIVER_PORT'].to_i
|
|
23
|
+
else
|
|
24
|
+
Mongo::MongoClient::DEFAULT_PORT
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Test::Unit::TestCase
|
|
29
|
+
include Mongo
|
|
30
|
+
include BSON
|
|
31
|
+
|
|
32
|
+
# Handles creating a pre-defined MongoDB cluster for integration testing.
|
|
33
|
+
#
|
|
34
|
+
# @param kind=nil [Symbol] Type of cluster (:rs or :sc).
|
|
35
|
+
# @param opts={} [Hash] Options to be passed through to the cluster manager.
|
|
36
|
+
#
|
|
37
|
+
# @return [ClusterManager] The cluster manager instance being used.
|
|
38
|
+
def ensure_cluster(kind=nil, opts={})
|
|
39
|
+
cluster_instance = nil
|
|
40
|
+
class_vars = TEST_BASE.class_eval { class_variables }
|
|
41
|
+
if class_vars.include?("@@cluster_#{kind}") || class_vars.include?("@@cluster_#{kind}".to_sym)
|
|
42
|
+
cluster_instance = TEST_BASE.class_eval { class_variable_get("@@cluster_#{kind}") }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
unless cluster_instance
|
|
46
|
+
if kind == :rs
|
|
47
|
+
cluster_opts = Config::DEFAULT_REPLICA_SET.dup
|
|
48
|
+
else
|
|
49
|
+
cluster_opts = Config::DEFAULT_SHARDED_SIMPLE.dup
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
cluster_opts.merge!(opts)
|
|
53
|
+
cluster_opts.merge!(:dbpath => ENV['MONGO_DBPATH'] || 'data')
|
|
54
|
+
config = Config.cluster(cluster_opts)
|
|
55
|
+
|
|
56
|
+
cluster_instance = Config::ClusterManager.new(config)
|
|
57
|
+
TEST_BASE.class_eval { class_variable_set("@@cluster_#{kind}", cluster_instance) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
cluster_instance.start
|
|
61
|
+
instance_variable_set("@#{kind}", cluster_instance)
|
|
62
|
+
|
|
63
|
+
uri = "mongodb://#{TEST_USER}:#{TEST_USER_PWD}@" +
|
|
64
|
+
"#{cluster_instance.members_uri}"
|
|
65
|
+
uri += "?replicaset=#{@rs.repl_set_name}&sockettimeoutms=60000" if cluster_instance.replica_set?
|
|
66
|
+
instance_variable_set("@uri", uri)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Generic helper to rescue and retry from a connection failure.
|
|
70
|
+
#
|
|
71
|
+
# @param max_retries=30 [Integer] The number of times to attempt a retry.
|
|
72
|
+
#
|
|
73
|
+
# @return [Object] The block result.
|
|
74
|
+
def rescue_connection_failure(max_retries=30)
|
|
75
|
+
retries = 0
|
|
76
|
+
begin
|
|
77
|
+
yield
|
|
78
|
+
rescue Mongo::ConnectionFailure => ex
|
|
79
|
+
retries += 1
|
|
80
|
+
raise ex if retries > max_retries
|
|
81
|
+
sleep(2)
|
|
82
|
+
retry
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Creates and connects a standard, pre-defined MongoClient instance.
|
|
87
|
+
#
|
|
88
|
+
# @param options={} [Hash] Options to be passed to the client instance.
|
|
89
|
+
# @param legacy=false [Boolean] When true, uses deprecated Mongo::Connection.
|
|
90
|
+
#
|
|
91
|
+
# @return [MongoClient] The client instance.
|
|
92
|
+
def self.standard_connection(options={}, legacy=false)
|
|
93
|
+
opts = options[:op_timeout] ? options : options.merge(:op_timeout => TEST_OP_TIMEOUT)
|
|
94
|
+
if legacy
|
|
95
|
+
silently do
|
|
96
|
+
# We have to create the Connection object directly here instead of using TEST_URI
|
|
97
|
+
# because Connection#from_uri ends up creating a MongoClient object.
|
|
98
|
+
conn = Connection.new(TEST_HOST, TEST_PORT, opts)
|
|
99
|
+
conn[TEST_DB].authenticate(TEST_USER, TEST_USER_PWD)
|
|
100
|
+
conn
|
|
101
|
+
end
|
|
102
|
+
else
|
|
103
|
+
MongoClient.from_uri(TEST_URI, opts)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Creates and connects a standard, pre-defined MongoClient instance.
|
|
108
|
+
#
|
|
109
|
+
# @param options={} [Hash] Options to be passed to the client instance.
|
|
110
|
+
# @param legacy=false [Boolean] When true, uses deprecated Mongo::Connection.
|
|
111
|
+
#
|
|
112
|
+
# @return [MongoClient] The client instance.
|
|
113
|
+
def standard_connection(options={}, legacy=false)
|
|
114
|
+
self.class.standard_connection(options, legacy)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self.host_port
|
|
118
|
+
"#{mongo_host}:#{mongo_port}"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def self.mongo_host
|
|
122
|
+
TEST_HOST
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.mongo_port
|
|
126
|
+
TEST_PORT
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def host_port
|
|
130
|
+
self.class.host_port
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def mongo_host
|
|
134
|
+
self.class.mongo_host
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def mongo_port
|
|
138
|
+
self.class.mongo_port
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def method_name
|
|
142
|
+
caller[0]=~/`(.*?)'/
|
|
143
|
+
$1
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def perform_step_down(member)
|
|
147
|
+
start = Time.now
|
|
148
|
+
timeout = 20 # seconds
|
|
149
|
+
begin
|
|
150
|
+
step_down_command = BSON::OrderedHash.new
|
|
151
|
+
step_down_command[:replSetStepDown] = 30
|
|
152
|
+
step_down_command[:force] = true
|
|
153
|
+
member['admin'].command(step_down_command)
|
|
154
|
+
rescue Mongo::OperationFailure => e
|
|
155
|
+
retry unless (Time.now - start) > timeout
|
|
156
|
+
raise e
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def new_mock_socket(host='localhost', port=27017)
|
|
161
|
+
socket = Object.new
|
|
162
|
+
socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
163
|
+
socket.stubs(:close)
|
|
164
|
+
socket.stubs(:closed?)
|
|
165
|
+
socket.stubs(:checkin)
|
|
166
|
+
socket.stubs(:pool)
|
|
167
|
+
socket.stubs(:pid)
|
|
168
|
+
socket
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def new_mock_unix_socket(sockfile='/tmp/mongod.sock')
|
|
172
|
+
socket = Object.new
|
|
173
|
+
socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP)
|
|
174
|
+
socket.stubs(:close)
|
|
175
|
+
socket.stubs(:closed?)
|
|
176
|
+
socket
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def new_mock_db
|
|
180
|
+
Object.new
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def mock_pool(tags={}, ping_time=15)
|
|
184
|
+
mock('pool').tap do |pool|
|
|
185
|
+
pool.stubs(:tags).returns(tags)
|
|
186
|
+
pool.stubs(:ping_time).returns(ping_time)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def assert_raise_error(klass, message=nil)
|
|
191
|
+
begin
|
|
192
|
+
yield
|
|
193
|
+
rescue => e
|
|
194
|
+
if klass.to_s != e.class.to_s
|
|
195
|
+
flunk "Expected exception class #{klass} but got #{e.class}.\n #{e.backtrace}"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
if message && !e.message.include?(message)
|
|
199
|
+
p e.backtrace
|
|
200
|
+
flunk "#{e.message} does not include #{message}.\n#{e.backtrace}"
|
|
201
|
+
end
|
|
202
|
+
else
|
|
203
|
+
flunk "Expected assertion #{klass} but none was raised."
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def match_document(key, expected, actual) # special cases for Regexp match, BSON::ObjectId, Range
|
|
208
|
+
if expected.is_a?(Hash) && actual.is_a?(Hash)
|
|
209
|
+
expected_keys = expected.keys.sort
|
|
210
|
+
actual_keys = actual.keys.sort
|
|
211
|
+
#currently allow extra fields in actual as the following check for equality of keys is commented out
|
|
212
|
+
#raise "field:#{key.inspect} - Hash keys expected:#{expected_keys.inspect} actual:#{actual_keys.inspect}" if expected_keys != actual_keys
|
|
213
|
+
expected_keys.each{|k| match_document(k, expected[k], actual[k])}
|
|
214
|
+
elsif expected.is_a?(Array) && actual.is_a?(Array)
|
|
215
|
+
raise "field:#{key.inspect} - Array size expected:#{expected.size} actual:#{actual.size}" if expected.size != actual.size
|
|
216
|
+
(0...expected.size).each{|i| match_document(i, expected[i], actual[i])}
|
|
217
|
+
elsif expected.is_a?(Regexp) && actual.is_a?(String)
|
|
218
|
+
raise "field:#{key.inspect} - Regexp expected:#{expected.inspect} actual:#{actual.inspect}" if expected !~ actual
|
|
219
|
+
elsif expected.is_a?(BSON::ObjectId) && actual.is_a?(BSON::ObjectId)
|
|
220
|
+
# match type but not value
|
|
221
|
+
elsif expected.is_a?(Range)
|
|
222
|
+
raise "field:#{key.inspect} - Range expected:#{expected.inspect} actual:#{actual.inspect}" if !expected.include?(actual)
|
|
223
|
+
elsif expected.is_a?(Set)
|
|
224
|
+
raise "field:#{key.inspect} - Set expected:#{expected.inspect} actual:#{actual.inspect}" if !expected.include?(actual)
|
|
225
|
+
else
|
|
226
|
+
raise "field:#{key.inspect} - expected:#{expected.inspect} actual:#{actual.inspect}" if expected != actual
|
|
227
|
+
end
|
|
228
|
+
true
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def assert_match_document(expected, actual, message = '')
|
|
232
|
+
match = begin
|
|
233
|
+
match_document('', expected, actual)
|
|
234
|
+
rescue => ex
|
|
235
|
+
message = ex.message + ' - ' + message
|
|
236
|
+
false
|
|
237
|
+
end
|
|
238
|
+
assert(match, message)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def with_forced_timeout(client, &block)
|
|
242
|
+
authenticate_client(client)
|
|
243
|
+
cmd_line_args = client['admin'].command({ :getCmdLineOpts => 1 })['argv']
|
|
244
|
+
if cmd_line_args.include?('enableTestCommands=1') && client.server_version >= "2.5.3"
|
|
245
|
+
begin
|
|
246
|
+
#Force any query or command with valid non-zero max time to fail (SERVER-10650)
|
|
247
|
+
fail_point_cmd = OrderedHash.new
|
|
248
|
+
fail_point_cmd[:configureFailPoint] = 'maxTimeAlwaysTimeOut'
|
|
249
|
+
fail_point_cmd[:mode] = 'alwaysOn'
|
|
250
|
+
client['admin'].command(fail_point_cmd)
|
|
251
|
+
yield
|
|
252
|
+
fail_point_cmd[:mode] = 'off'
|
|
253
|
+
client['admin'].command(fail_point_cmd)
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def with_default_journaling(client, &block)
|
|
259
|
+
authenticate_client(client)
|
|
260
|
+
cmd_line_args = client['admin'].command({ :getCmdLineOpts => 1 })['parsed']
|
|
261
|
+
unless client.server_version < "2.0" || cmd_line_args.include?('nojournal') ||
|
|
262
|
+
using_heap1_storage_engine?(cmd_line_args)
|
|
263
|
+
yield
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def using_heap1_storage_engine?(cmd_line_args)
|
|
268
|
+
cmd_line_args.include?('storage') &&
|
|
269
|
+
cmd_line_args['storage']['engine'] == 'heap1'
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def with_no_replication(client, &block)
|
|
273
|
+
if client.class == MongoClient
|
|
274
|
+
yield
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def with_no_journaling(client, &block)
|
|
279
|
+
authenticate_client(client)
|
|
280
|
+
cmd_line_args = client['admin'].command({ :getCmdLineOpts => 1 })['parsed']
|
|
281
|
+
unless client.server_version < "2.0" || !cmd_line_args.include?('nojournal')
|
|
282
|
+
yield
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def with_ipv6_enabled(client, &block)
|
|
287
|
+
authenticate_client(client)
|
|
288
|
+
cmd_line_args = client['admin'].command({ :getCmdLineOpts => 1 })['parsed']
|
|
289
|
+
if cmd_line_args.include?('ipv6')
|
|
290
|
+
yield
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
def with_write_commands(client, &block)
|
|
295
|
+
wire_version = Mongo::MongoClient::BATCH_COMMANDS
|
|
296
|
+
if client.primary_wire_version_feature?(wire_version)
|
|
297
|
+
yield wire_version
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def with_preserved_env_uri(new_uri=nil, &block)
|
|
302
|
+
old_mongodb_uri = ENV['MONGODB_URI']
|
|
303
|
+
begin
|
|
304
|
+
ENV['MONGODB_URI'] = new_uri
|
|
305
|
+
yield
|
|
306
|
+
ensure
|
|
307
|
+
ENV['MONGODB_URI'] = old_mongodb_uri
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def with_write_operations(client, &block)
|
|
312
|
+
wire_version = Mongo::MongoClient::RELEASE_2_4_AND_BEFORE
|
|
313
|
+
if client.primary_wire_version_feature?(wire_version)
|
|
314
|
+
client.class.class_eval(%Q{
|
|
315
|
+
alias :old_use_write_command? :use_write_command?
|
|
316
|
+
def use_write_command?(write_concern)
|
|
317
|
+
false
|
|
318
|
+
end
|
|
319
|
+
})
|
|
320
|
+
yield wire_version
|
|
321
|
+
client.class.class_eval(%Q{
|
|
322
|
+
alias :use_write_command? :old_use_write_command?
|
|
323
|
+
})
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def with_write_commands_and_operations(client, &block)
|
|
328
|
+
with_write_commands(client, &block)
|
|
329
|
+
with_write_operations(client, &block)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def batch_commands?(wire_version)
|
|
333
|
+
wire_version >= Mongo::MongoClient::BATCH_COMMANDS
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def subject_to_server_4754?(client)
|
|
337
|
+
# Until SERVER-4754 is resolved, profiling info is not collected
|
|
338
|
+
# when mongod is started with --auth in versions < 2.2
|
|
339
|
+
authenticate_client(client)
|
|
340
|
+
cmd_line_args = client['admin'].command({ :getCmdLineOpts => 1 })['parsed']
|
|
341
|
+
client.server_version < '2.2' && cmd_line_args.include?('auth')
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# When testing under narrowed localhost exception, the admin user must have
|
|
345
|
+
# special permissions to run the db_eval command.
|
|
346
|
+
def grant_admin_user_eval_role(client)
|
|
347
|
+
if auth_enabled?(client) && client.server_version >= "2.6"
|
|
348
|
+
# we need to have anyAction on anyResource to run db_eval()
|
|
349
|
+
admin = client['admin']
|
|
350
|
+
any_priv = BSON::OrderedHash.new
|
|
351
|
+
any_priv[:resource] = { :anyResource => true }
|
|
352
|
+
any_priv[:actions] = ['anyAction']
|
|
353
|
+
|
|
354
|
+
create_role = BSON::OrderedHash.new
|
|
355
|
+
create_role[:createRole] = 'db_eval'
|
|
356
|
+
create_role[:privileges] = [any_priv]
|
|
357
|
+
create_role[:roles] = []
|
|
358
|
+
|
|
359
|
+
begin
|
|
360
|
+
admin.command(create_role)
|
|
361
|
+
rescue Mongo::OperationFailure => ex
|
|
362
|
+
# role already exists
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
grant_role = BSON::OrderedHash.new
|
|
366
|
+
grant_role[:grantRolesToUser] = TEST_USER
|
|
367
|
+
grant_role[:roles] = ['db_eval']
|
|
368
|
+
admin.command(grant_role)
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Return true if auth is enabled, false otherwise.
|
|
373
|
+
def auth_enabled?(client)
|
|
374
|
+
begin
|
|
375
|
+
cmd_line_args = client['admin'].command({ :getCmdLineOpts => 1 })['parsed']
|
|
376
|
+
return true if cmd_line_args.include?('auth') || cmd_line_args.include?('keyFile')
|
|
377
|
+
if security = cmd_line_args["security"]
|
|
378
|
+
return true if security["authorization"] == "enabled"
|
|
379
|
+
end
|
|
380
|
+
rescue OperationFailure => ex
|
|
381
|
+
# under narrowed localhost exception in > 2.7.1, getCmdLineOpts is not allowed
|
|
382
|
+
# unless you're authenticated.
|
|
383
|
+
return true if ex.message.include?("authorized") ||
|
|
384
|
+
(client.server_version >= "2.7.1" &&
|
|
385
|
+
ex.error_code == Mongo::ErrorCode::UNAUTHORIZED)
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def with_auth(client, &block)
|
|
390
|
+
yield if auth_enabled?(client)
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def authenticate_client(client)
|
|
394
|
+
client[TEST_DB].authenticate(TEST_USER, TEST_USER_PWD) unless client.auths.any? {|a| a[:source] == TEST_DB}
|
|
395
|
+
client
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def self.ensure_admin_user
|
|
399
|
+
10.times do
|
|
400
|
+
begin
|
|
401
|
+
client = Mongo::MongoClient.new(TEST_HOST, TEST_PORT)
|
|
402
|
+
db = client[TEST_DB]
|
|
403
|
+
begin
|
|
404
|
+
db.authenticate(TEST_USER, TEST_USER_PWD, nil, 'admin')
|
|
405
|
+
rescue Mongo::AuthenticationError => ex
|
|
406
|
+
roles = [ 'dbAdminAnyDatabase',
|
|
407
|
+
'userAdminAnyDatabase',
|
|
408
|
+
'readWriteAnyDatabase',
|
|
409
|
+
'clusterAdmin' ]
|
|
410
|
+
db.add_user(TEST_USER, TEST_USER_PWD, nil, :roles => roles)
|
|
411
|
+
end
|
|
412
|
+
TEST_BASE.class_eval { class_variable_set("@@connected_single_mongod", true) }
|
|
413
|
+
break
|
|
414
|
+
rescue Mongo::ConnectionFailure
|
|
415
|
+
# mongod not available yet, wait a second and try again
|
|
416
|
+
sleep(1)
|
|
417
|
+
end
|
|
418
|
+
#puts "Not connected to a MongoD" unless client.connected?
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def self.cleanup_users_and_dbs
|
|
423
|
+
not_cluster = TEST_BASE.class_eval { class_variables }.none? { |v| v =~ /@@cluster_/ }
|
|
424
|
+
|
|
425
|
+
if @@connected_single_mongod && not_cluster
|
|
426
|
+
client = Mongo::MongoClient.from_uri(TEST_URI, :op_timeout => TEST_OP_TIMEOUT)
|
|
427
|
+
db = client[TEST_DB]
|
|
428
|
+
begin
|
|
429
|
+
begin
|
|
430
|
+
db.authenticate(TEST_USER, TEST_USER_PWD)
|
|
431
|
+
|
|
432
|
+
rescue Mongo::ConnectionFailure, Mongo::MongoArgumentError
|
|
433
|
+
rescue Mongo::AuthenticationError
|
|
434
|
+
Test::Unit::TestCase.ensure_admin_user
|
|
435
|
+
db.authenticate(TEST_USER, TEST_USER_PWD)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
client.database_names.each do |db_name|
|
|
439
|
+
if db_name =~ /^ruby_test*/
|
|
440
|
+
puts "[CLEAN-UP] Dropping '#{db_name}'..."
|
|
441
|
+
client.drop_database(db_name)
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
if client.server_version < '2.5'
|
|
446
|
+
db['system.users'].remove
|
|
447
|
+
else
|
|
448
|
+
db.command(:dropAllUsersFromDatabase => 1)
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
rescue Mongo::ConnectionFailure
|
|
452
|
+
# Nothing we can do about the mongod not being available
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
Test::Unit.at_start do
|
|
460
|
+
TEST_DB = ENV['TEST_DB'] || 'admin'
|
|
461
|
+
TEST_USER = ENV['TEST_USER'] || 'admin_user'
|
|
462
|
+
TEST_USER_PWD = ENV['TEST_USER_PWD'] || 'password'
|
|
463
|
+
TEST_URI = ENV['TEST_URI'] ||
|
|
464
|
+
"mongodb://#{TEST_USER}:#{TEST_USER_PWD}@#{TEST_HOST}:#{TEST_PORT}/#{TEST_DB}"
|
|
465
|
+
TEST_BASE.class_eval { class_variable_set("@@connected_single_mongod", false) }
|
|
466
|
+
Test::Unit::TestCase.ensure_admin_user
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# Before and after hooks for the entire test run
|
|
470
|
+
# handles mop up after the cluster manager is done.
|
|
471
|
+
Test::Unit.at_exit do
|
|
472
|
+
Test::Unit::TestCase.cleanup_users_and_dbs
|
|
473
|
+
TEST_BASE.class_eval { class_variables }.select { |v| v =~ /@@cluster_/ }.each do |cluster|
|
|
474
|
+
TEST_BASE.class_eval { class_variable_get(cluster) }.stop
|
|
475
|
+
end
|
|
476
|
+
end
|
|
@@ -1,20 +1,37 @@
|
|
|
1
|
+
# Copyright (C) 2009-2013 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
|
+
|
|
1
15
|
require 'test_helper'
|
|
2
|
-
require 'shared/authentication'
|
|
16
|
+
require 'shared/authentication/basic_auth_shared'
|
|
17
|
+
require 'shared/authentication/sasl_plain_shared'
|
|
18
|
+
require 'shared/authentication/bulk_api_auth_shared'
|
|
19
|
+
require 'shared/authentication/gssapi_shared'
|
|
3
20
|
|
|
4
21
|
class ReplicaSetAuthenticationTest < Test::Unit::TestCase
|
|
5
22
|
include Mongo
|
|
6
|
-
|
|
23
|
+
|
|
24
|
+
include BasicAuthTests
|
|
25
|
+
include SASLPlainTests
|
|
26
|
+
include BulkAPIAuthTests
|
|
27
|
+
include GSSAPITests
|
|
7
28
|
|
|
8
29
|
def setup
|
|
9
30
|
ensure_cluster(:rs)
|
|
10
|
-
@client
|
|
11
|
-
@
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_authenticate_with_connection_uri
|
|
16
|
-
@db.add_user('eunice', 'uritest')
|
|
17
|
-
assert MongoReplicaSetClient.from_uri(
|
|
18
|
-
"mongodb://eunice:uritest@#{@rs.repl_set_seeds.join(',')}/#{@db.name}?replicaSet=#{@rs.repl_set_name}")
|
|
31
|
+
@client = MongoReplicaSetClient.from_uri(@uri, :op_timeout => TEST_OP_TIMEOUT)
|
|
32
|
+
@admin = @client['admin']
|
|
33
|
+
@version = @client.server_version
|
|
34
|
+
@db = @client['ruby-test']
|
|
35
|
+
@host_info = @rs.repl_set_seeds.join(',')
|
|
19
36
|
end
|
|
20
37
|
end
|
|
@@ -1,13 +1,27 @@
|
|
|
1
|
+
# Copyright (C) 2009-2013 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
|
+
|
|
1
15
|
require 'test_helper'
|
|
2
16
|
|
|
3
|
-
class
|
|
17
|
+
class ReplicaSetBasicTest < Test::Unit::TestCase
|
|
4
18
|
|
|
5
19
|
def setup
|
|
6
20
|
ensure_cluster(:rs)
|
|
7
21
|
end
|
|
8
22
|
|
|
9
23
|
def test_connect
|
|
10
|
-
client = MongoReplicaSetClient.new(@rs.repl_set_seeds, :name => @rs.repl_set_name)
|
|
24
|
+
client = MongoReplicaSetClient.new(@rs.repl_set_seeds, :name => @rs.repl_set_name, :op_timeout => TEST_OP_TIMEOUT)
|
|
11
25
|
assert client.connected?
|
|
12
26
|
assert_equal @rs.primary_name, client.primary.join(':')
|
|
13
27
|
assert_equal @rs.secondary_names.sort, client.secondaries.collect{|s| s.join(':')}.sort
|
|
@@ -59,9 +73,7 @@ class BasicTest < Test::Unit::TestCase
|
|
|
59
73
|
end
|
|
60
74
|
|
|
61
75
|
def test_accessors
|
|
62
|
-
|
|
63
|
-
args = {:name => @rs.repl_set_name}
|
|
64
|
-
client = MongoReplicaSetClient.new(seeds, args)
|
|
76
|
+
client = MongoReplicaSetClient.from_uri(@uri)
|
|
65
77
|
assert_equal @rs.primary_name, [client.host, client.port].join(':')
|
|
66
78
|
assert_equal client.host, client.primary_pool.host
|
|
67
79
|
assert_equal client.port, client.primary_pool.port
|
|
@@ -74,30 +86,76 @@ class BasicTest < Test::Unit::TestCase
|
|
|
74
86
|
client.close
|
|
75
87
|
end
|
|
76
88
|
|
|
89
|
+
def test_write_commands_and_operations
|
|
90
|
+
@client = MongoReplicaSetClient.from_uri(@uri)
|
|
91
|
+
@coll = @client[TEST_DB]['test-write-commands-and-operations']
|
|
92
|
+
with_write_commands_and_operations(@client) do
|
|
93
|
+
@coll.remove
|
|
94
|
+
@coll.insert({:foo => "bar"})
|
|
95
|
+
assert_equal(1, @coll.count)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def test_wnote_does_not_raise_exception_with_err_nil
|
|
100
|
+
@client = MongoReplicaSetClient.from_uri(@uri)
|
|
101
|
+
if @client.server_version < '2.5.5'
|
|
102
|
+
@coll = @client[TEST_DB]['test-wnote']
|
|
103
|
+
begin
|
|
104
|
+
result = @coll.remove({:foo => 1}, :w => 2)
|
|
105
|
+
rescue => ex
|
|
106
|
+
assert(false, "should not raise an exception for a wnote response field from a remove that does not match any documents")
|
|
107
|
+
end
|
|
108
|
+
assert_nil result["err"]
|
|
109
|
+
assert_true result.has_key?("wnote")
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def test_sockets_used_by_forked_process
|
|
114
|
+
@client = MongoReplicaSetClient.from_uri(@uri)
|
|
115
|
+
primary_node = @client.manager.primary_pool.node
|
|
116
|
+
primary_socket = primary_node.socket
|
|
117
|
+
primary_socket.instance_variable_set(:@pid, primary_socket.pid + 1)
|
|
118
|
+
primary_node.set_config
|
|
119
|
+
assert primary_socket != primary_node.socket
|
|
120
|
+
end
|
|
121
|
+
|
|
77
122
|
context "Socket pools" do
|
|
78
123
|
context "checking out writers" do
|
|
79
124
|
setup do
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
@client = MongoReplicaSetClient.new(seeds, args)
|
|
83
|
-
@coll = @client[MONGO_TEST_DB]['test-connection-exceptions']
|
|
125
|
+
@client = MongoReplicaSetClient.from_uri(@uri)
|
|
126
|
+
@coll = @client[TEST_DB]['test-connection-exceptions']
|
|
84
127
|
end
|
|
85
128
|
|
|
86
129
|
should "close the connection on send_message for major exceptions" do
|
|
87
|
-
@client
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
130
|
+
with_write_operations(@client) do # explicit even if w 0 maps to write operations
|
|
131
|
+
@client.expects(:checkout_writer).raises(SystemStackError)
|
|
132
|
+
@client.expects(:close)
|
|
133
|
+
begin
|
|
134
|
+
@coll.insert({:foo => "bar"}, :w => 0)
|
|
135
|
+
rescue SystemStackError
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
should "close the connection on send_write_command for major exceptions" do
|
|
141
|
+
with_write_commands(@client) do
|
|
142
|
+
@client.expects(:checkout_reader).raises(SystemStackError)
|
|
143
|
+
@client.expects(:close)
|
|
144
|
+
begin
|
|
145
|
+
@coll.insert({:foo => "bar"})
|
|
146
|
+
rescue SystemStackError
|
|
147
|
+
end
|
|
92
148
|
end
|
|
93
149
|
end
|
|
94
150
|
|
|
95
151
|
should "close the connection on send_message_with_gle for major exceptions" do
|
|
96
|
-
@client
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
152
|
+
with_write_operations(@client) do
|
|
153
|
+
@client.expects(:checkout_writer).raises(SystemStackError)
|
|
154
|
+
@client.expects(:close)
|
|
155
|
+
begin
|
|
156
|
+
@coll.insert({:foo => "bar"})
|
|
157
|
+
rescue SystemStackError
|
|
158
|
+
end
|
|
101
159
|
end
|
|
102
160
|
end
|
|
103
161
|
|
|
@@ -113,10 +171,8 @@ class BasicTest < Test::Unit::TestCase
|
|
|
113
171
|
|
|
114
172
|
context "checking out readers" do
|
|
115
173
|
setup do
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
@client = MongoReplicaSetClient.new(seeds, args)
|
|
119
|
-
@coll = @client[MONGO_TEST_DB]['test-connection-exceptions']
|
|
174
|
+
@client = MongoReplicaSetClient.from_uri(@uri)
|
|
175
|
+
@coll = @client[TEST_DB]['test-connection-exceptions']
|
|
120
176
|
end
|
|
121
177
|
|
|
122
178
|
should "close the connection on receive_message for major exceptions" do
|