mongo 1.3.0 → 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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +122 -271
- data/Rakefile +25 -209
- data/VERSION +1 -0
- data/bin/mongo_console +31 -9
- data/lib/mongo/bulk_write_collection_view.rb +387 -0
- data/lib/mongo/collection.rb +576 -269
- data/lib/mongo/collection_writer.rb +364 -0
- data/lib/mongo/connection/node.rb +249 -0
- data/lib/mongo/connection/pool.rb +340 -0
- data/lib/mongo/connection/pool_manager.rb +320 -0
- data/lib/mongo/connection/sharding_pool_manager.rb +67 -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 +7 -875
- data/lib/mongo/cursor.rb +403 -117
- data/lib/mongo/db.rb +444 -243
- data/lib/mongo/exception.rb +145 -0
- data/lib/mongo/functional/authentication.rb +455 -0
- data/lib/mongo/functional/logging.rb +85 -0
- 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/functional/write_concern.rb +66 -0
- data/lib/mongo/functional.rb +20 -0
- data/lib/mongo/gridfs/grid.rb +30 -24
- data/lib/mongo/gridfs/grid_ext.rb +6 -10
- data/lib/mongo/gridfs/grid_file_system.rb +38 -20
- data/lib/mongo/gridfs/grid_io.rb +84 -75
- data/lib/mongo/gridfs.rb +18 -0
- data/lib/mongo/legacy.rb +140 -0
- data/lib/mongo/mongo_client.rb +697 -0
- data/lib/mongo/mongo_replica_set_client.rb +535 -0
- data/lib/mongo/mongo_sharded_client.rb +159 -0
- data/lib/mongo/networking.rb +372 -0
- data/lib/mongo/{util → utils}/conversions.rb +29 -8
- data/lib/mongo/{util → utils}/core_ext.rb +28 -18
- data/lib/mongo/{util → utils}/server_version.rb +4 -6
- data/lib/mongo/{util → utils}/support.rb +29 -31
- data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
- data/lib/mongo/utils.rb +19 -0
- data/lib/mongo.rb +51 -50
- data/mongo.gemspec +29 -32
- data/test/functional/authentication_test.rb +39 -0
- 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 +2175 -0
- data/test/functional/collection_writer_test.rb +83 -0
- data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
- data/test/functional/cursor_fail_test.rb +57 -0
- data/test/functional/cursor_message_test.rb +56 -0
- data/test/functional/cursor_test.rb +683 -0
- data/test/functional/db_api_test.rb +835 -0
- data/test/functional/db_connection_test.rb +25 -0
- data/test/functional/db_test.rb +348 -0
- data/test/functional/grid_file_system_test.rb +285 -0
- data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
- data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
- data/test/functional/pool_test.rb +136 -0
- data/test/functional/safe_test.rb +98 -0
- data/test/functional/ssl_test.rb +29 -0
- data/test/functional/support_test.rb +62 -0
- data/test/functional/timeout_test.rb +60 -0
- data/test/functional/uri_test.rb +446 -0
- data/test/functional/write_concern_test.rb +118 -0
- data/test/helpers/general.rb +50 -0
- data/test/helpers/test_unit.rb +476 -0
- data/test/replica_set/authentication_test.rb +37 -0
- data/test/replica_set/basic_test.rb +189 -0
- data/test/replica_set/client_test.rb +393 -0
- data/test/replica_set/connection_test.rb +138 -0
- data/test/replica_set/count_test.rb +66 -0
- data/test/replica_set/cursor_test.rb +220 -0
- data/test/replica_set/insert_test.rb +157 -0
- data/test/replica_set/max_values_test.rb +151 -0
- data/test/replica_set/pinning_test.rb +105 -0
- data/test/replica_set/query_test.rb +73 -0
- data/test/replica_set/read_preference_test.rb +219 -0
- data/test/replica_set/refresh_test.rb +211 -0
- data/test/replica_set/replication_ack_test.rb +95 -0
- data/test/replica_set/ssl_test.rb +32 -0
- data/test/sharded_cluster/basic_test.rb +203 -0
- 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 +53 -94
- data/test/threading/basic_test.rb +120 -0
- data/test/tools/mongo_config.rb +708 -0
- data/test/tools/mongo_config_test.rb +160 -0
- data/test/unit/client_test.rb +381 -0
- data/test/unit/collection_test.rb +89 -53
- data/test/unit/connection_test.rb +282 -32
- data/test/unit/cursor_test.rb +206 -8
- data/test/unit/db_test.rb +55 -13
- data/test/unit/grid_test.rb +43 -16
- data/test/unit/mongo_sharded_client_test.rb +48 -0
- data/test/unit/node_test.rb +93 -0
- data/test/unit/pool_manager_test.rb +111 -0
- data/test/unit/read_pref_test.rb +406 -0
- data/test/unit/read_test.rb +159 -0
- data/test/unit/safe_test.rb +69 -36
- data/test/unit/sharding_pool_manager_test.rb +84 -0
- data/test/unit/write_concern_test.rb +175 -0
- data.tar.gz.sig +3 -0
- metadata +227 -216
- metadata.gz.sig +0 -0
- data/docs/CREDITS.md +0 -123
- data/docs/FAQ.md +0 -116
- data/docs/GridFS.md +0 -158
- data/docs/HISTORY.md +0 -244
- data/docs/RELEASES.md +0 -33
- data/docs/REPLICA_SETS.md +0 -72
- data/docs/TUTORIAL.md +0 -247
- data/docs/WRITE_CONCERN.md +0 -28
- data/lib/mongo/exceptions.rb +0 -71
- data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
- data/lib/mongo/repl_set_connection.rb +0 -342
- data/lib/mongo/test.rb +0 -20
- data/lib/mongo/util/pool.rb +0 -177
- data/lib/mongo/util/uri_parser.rb +0 -185
- data/test/async/collection_test.rb +0 -224
- data/test/async/connection_test.rb +0 -24
- data/test/async/cursor_test.rb +0 -162
- data/test/async/worker_pool_test.rb +0 -99
- data/test/auxillary/1.4_features.rb +0 -166
- data/test/auxillary/authentication_test.rb +0 -68
- data/test/auxillary/autoreconnect_test.rb +0 -41
- data/test/auxillary/fork_test.rb +0 -30
- data/test/auxillary/repl_set_auth_test.rb +0 -58
- data/test/auxillary/slave_connection_test.rb +0 -36
- data/test/auxillary/threaded_authentication_test.rb +0 -101
- data/test/bson/binary_test.rb +0 -15
- data/test/bson/bson_test.rb +0 -649
- data/test/bson/byte_buffer_test.rb +0 -208
- data/test/bson/hash_with_indifferent_access_test.rb +0 -38
- data/test/bson/json_test.rb +0 -17
- data/test/bson/object_id_test.rb +0 -154
- data/test/bson/ordered_hash_test.rb +0 -204
- data/test/bson/timestamp_test.rb +0 -24
- data/test/collection_test.rb +0 -910
- data/test/connection_test.rb +0 -309
- data/test/cursor_fail_test.rb +0 -75
- data/test/cursor_message_test.rb +0 -43
- data/test/cursor_test.rb +0 -483
- data/test/db_api_test.rb +0 -726
- data/test/db_connection_test.rb +0 -15
- data/test/db_test.rb +0 -287
- data/test/grid_file_system_test.rb +0 -243
- data/test/load/resque/load.rb +0 -21
- data/test/load/resque/processor.rb +0 -26
- data/test/load/thin/load.rb +0 -24
- data/test/load/unicorn/load.rb +0 -23
- data/test/load/unicorn/unicorn.rb +0 -29
- data/test/replica_sets/connect_test.rb +0 -94
- data/test/replica_sets/connection_string_test.rb +0 -32
- data/test/replica_sets/count_test.rb +0 -35
- data/test/replica_sets/insert_test.rb +0 -53
- data/test/replica_sets/pooled_insert_test.rb +0 -55
- data/test/replica_sets/query_secondaries.rb +0 -96
- data/test/replica_sets/query_test.rb +0 -51
- data/test/replica_sets/replication_ack_test.rb +0 -66
- data/test/replica_sets/rs_test_helper.rb +0 -27
- data/test/safe_test.rb +0 -68
- data/test/support/hash_with_indifferent_access.rb +0 -186
- data/test/support/keys.rb +0 -45
- data/test/support_test.rb +0 -18
- data/test/threading/threading_with_large_pool_test.rb +0 -90
- data/test/threading_test.rb +0 -87
- data/test/tools/auth_repl_set_manager.rb +0 -14
- data/test/tools/load.rb +0 -58
- data/test/tools/repl_set_manager.rb +0 -266
- data/test/tools/sharding_manager.rb +0 -202
- data/test/tools/test.rb +0 -4
- data/test/unit/pool_test.rb +0 -9
- data/test/unit/repl_set_connection_test.rb +0 -59
- data/test/uri_test.rb +0 -91
@@ -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
|
@@ -0,0 +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
|
+
|
15
|
+
require 'test_helper'
|
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'
|
20
|
+
|
21
|
+
class ReplicaSetAuthenticationTest < Test::Unit::TestCase
|
22
|
+
include Mongo
|
23
|
+
|
24
|
+
include BasicAuthTests
|
25
|
+
include SASLPlainTests
|
26
|
+
include BulkAPIAuthTests
|
27
|
+
include GSSAPITests
|
28
|
+
|
29
|
+
def setup
|
30
|
+
ensure_cluster(:rs)
|
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(',')
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,189 @@
|
|
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
|
+
require 'test_helper'
|
16
|
+
|
17
|
+
class ReplicaSetBasicTest < Test::Unit::TestCase
|
18
|
+
|
19
|
+
def setup
|
20
|
+
ensure_cluster(:rs)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_connect
|
24
|
+
client = MongoReplicaSetClient.new(@rs.repl_set_seeds, :name => @rs.repl_set_name, :op_timeout => TEST_OP_TIMEOUT)
|
25
|
+
assert client.connected?
|
26
|
+
assert_equal @rs.primary_name, client.primary.join(':')
|
27
|
+
assert_equal @rs.secondary_names.sort, client.secondaries.collect{|s| s.join(':')}.sort
|
28
|
+
assert_equal @rs.arbiter_names.sort, client.arbiters.collect{|s| s.join(':')}.sort
|
29
|
+
client.close
|
30
|
+
|
31
|
+
silently do
|
32
|
+
client = MongoReplicaSetClient.new(@rs.repl_set_seeds_old, :name => @rs.repl_set_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
assert client.connected?
|
36
|
+
client.close
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_safe_option
|
40
|
+
client = MongoReplicaSetClient.new(@rs.repl_set_seeds, :name => @rs.repl_set_name)
|
41
|
+
assert client.connected?
|
42
|
+
assert client.write_concern[:w] > 0
|
43
|
+
client.close
|
44
|
+
client = MongoReplicaSetClient.new(@rs.repl_set_seeds, :name => @rs.repl_set_name, :w => 0)
|
45
|
+
assert client.connected?
|
46
|
+
assert client.write_concern[:w] < 1
|
47
|
+
client.close
|
48
|
+
client = MongoReplicaSetClient.new(@rs.repl_set_seeds, :name => @rs.repl_set_name, :w => 2)
|
49
|
+
assert client.connected?
|
50
|
+
assert client.write_concern[:w] > 0
|
51
|
+
client.close
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_multiple_concurrent_replica_set_connection
|
55
|
+
client1 = MongoReplicaSetClient.new(@rs.repl_set_seeds, :name => @rs.repl_set_name)
|
56
|
+
client2 = MongoReplicaSetClient.new(@rs.repl_set_seeds, :name => @rs.repl_set_name)
|
57
|
+
assert client1.connected?
|
58
|
+
assert client2.connected?
|
59
|
+
assert client1.manager != client2.manager
|
60
|
+
assert client1.local_manager != client2.local_manager
|
61
|
+
client1.close
|
62
|
+
client2.close
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_cache_original_seed_nodes
|
66
|
+
host = @rs.servers.first.host
|
67
|
+
seeds = @rs.repl_set_seeds << "#{host}:19356"
|
68
|
+
client = MongoReplicaSetClient.new(seeds, :name => @rs.repl_set_name)
|
69
|
+
assert client.connected?
|
70
|
+
assert client.seeds.include?([host, 19356]), "Original seed nodes not cached!"
|
71
|
+
assert_equal [host, 19356], client.seeds.last, "Original seed nodes not cached!"
|
72
|
+
client.close
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_accessors
|
76
|
+
client = MongoReplicaSetClient.from_uri(@uri)
|
77
|
+
assert_equal @rs.primary_name, [client.host, client.port].join(':')
|
78
|
+
assert_equal client.host, client.primary_pool.host
|
79
|
+
assert_equal client.port, client.primary_pool.port
|
80
|
+
assert_equal 2, client.secondaries.length
|
81
|
+
assert_equal 2, client.secondary_pools.length
|
82
|
+
assert_equal @rs.repl_set_name, client.replica_set_name
|
83
|
+
assert client.secondary_pools.include?(client.read_pool({:mode => :secondary}))
|
84
|
+
assert_equal 90, client.refresh_interval
|
85
|
+
assert_equal client.refresh_mode, false
|
86
|
+
client.close
|
87
|
+
end
|
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
|
+
|
122
|
+
context "Socket pools" do
|
123
|
+
context "checking out writers" do
|
124
|
+
setup do
|
125
|
+
@client = MongoReplicaSetClient.from_uri(@uri)
|
126
|
+
@coll = @client[TEST_DB]['test-connection-exceptions']
|
127
|
+
end
|
128
|
+
|
129
|
+
should "close the connection on send_message for major exceptions" do
|
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
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
should "close the connection on send_message_with_gle for major exceptions" do
|
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
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
should "close the connection on receive_message for major exceptions" do
|
163
|
+
@client.expects(:checkout_reader).raises(SystemStackError)
|
164
|
+
@client.expects(:close)
|
165
|
+
begin
|
166
|
+
@coll.find({}, :read => :primary).next
|
167
|
+
rescue SystemStackError
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "checking out readers" do
|
173
|
+
setup do
|
174
|
+
@client = MongoReplicaSetClient.from_uri(@uri)
|
175
|
+
@coll = @client[TEST_DB]['test-connection-exceptions']
|
176
|
+
end
|
177
|
+
|
178
|
+
should "close the connection on receive_message for major exceptions" do
|
179
|
+
@client.expects(:checkout_reader).raises(SystemStackError)
|
180
|
+
@client.expects(:close)
|
181
|
+
begin
|
182
|
+
@coll.find({}, :read => :secondary).next
|
183
|
+
rescue SystemStackError
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|