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.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +122 -271
  5. data/Rakefile +25 -209
  6. data/VERSION +1 -0
  7. data/bin/mongo_console +31 -9
  8. data/lib/mongo/bulk_write_collection_view.rb +387 -0
  9. data/lib/mongo/collection.rb +576 -269
  10. data/lib/mongo/collection_writer.rb +364 -0
  11. data/lib/mongo/connection/node.rb +249 -0
  12. data/lib/mongo/connection/pool.rb +340 -0
  13. data/lib/mongo/connection/pool_manager.rb +320 -0
  14. data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
  15. data/lib/mongo/connection/socket/socket_util.rb +37 -0
  16. data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
  17. data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
  18. data/lib/mongo/connection/socket/unix_socket.rb +39 -0
  19. data/lib/mongo/connection/socket.rb +18 -0
  20. data/lib/mongo/connection.rb +7 -875
  21. data/lib/mongo/cursor.rb +403 -117
  22. data/lib/mongo/db.rb +444 -243
  23. data/lib/mongo/exception.rb +145 -0
  24. data/lib/mongo/functional/authentication.rb +455 -0
  25. data/lib/mongo/functional/logging.rb +85 -0
  26. data/lib/mongo/functional/read_preference.rb +183 -0
  27. data/lib/mongo/functional/scram.rb +556 -0
  28. data/lib/mongo/functional/uri_parser.rb +409 -0
  29. data/lib/mongo/functional/write_concern.rb +66 -0
  30. data/lib/mongo/functional.rb +20 -0
  31. data/lib/mongo/gridfs/grid.rb +30 -24
  32. data/lib/mongo/gridfs/grid_ext.rb +6 -10
  33. data/lib/mongo/gridfs/grid_file_system.rb +38 -20
  34. data/lib/mongo/gridfs/grid_io.rb +84 -75
  35. data/lib/mongo/gridfs.rb +18 -0
  36. data/lib/mongo/legacy.rb +140 -0
  37. data/lib/mongo/mongo_client.rb +697 -0
  38. data/lib/mongo/mongo_replica_set_client.rb +535 -0
  39. data/lib/mongo/mongo_sharded_client.rb +159 -0
  40. data/lib/mongo/networking.rb +372 -0
  41. data/lib/mongo/{util → utils}/conversions.rb +29 -8
  42. data/lib/mongo/{util → utils}/core_ext.rb +28 -18
  43. data/lib/mongo/{util → utils}/server_version.rb +4 -6
  44. data/lib/mongo/{util → utils}/support.rb +29 -31
  45. data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
  46. data/lib/mongo/utils.rb +19 -0
  47. data/lib/mongo.rb +51 -50
  48. data/mongo.gemspec +29 -32
  49. data/test/functional/authentication_test.rb +39 -0
  50. data/test/functional/bulk_api_stress_test.rb +133 -0
  51. data/test/functional/bulk_write_collection_view_test.rb +1198 -0
  52. data/test/functional/client_test.rb +627 -0
  53. data/test/functional/collection_test.rb +2175 -0
  54. data/test/functional/collection_writer_test.rb +83 -0
  55. data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
  56. data/test/functional/cursor_fail_test.rb +57 -0
  57. data/test/functional/cursor_message_test.rb +56 -0
  58. data/test/functional/cursor_test.rb +683 -0
  59. data/test/functional/db_api_test.rb +835 -0
  60. data/test/functional/db_connection_test.rb +25 -0
  61. data/test/functional/db_test.rb +348 -0
  62. data/test/functional/grid_file_system_test.rb +285 -0
  63. data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
  64. data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
  65. data/test/functional/pool_test.rb +136 -0
  66. data/test/functional/safe_test.rb +98 -0
  67. data/test/functional/ssl_test.rb +29 -0
  68. data/test/functional/support_test.rb +62 -0
  69. data/test/functional/timeout_test.rb +60 -0
  70. data/test/functional/uri_test.rb +446 -0
  71. data/test/functional/write_concern_test.rb +118 -0
  72. data/test/helpers/general.rb +50 -0
  73. data/test/helpers/test_unit.rb +476 -0
  74. data/test/replica_set/authentication_test.rb +37 -0
  75. data/test/replica_set/basic_test.rb +189 -0
  76. data/test/replica_set/client_test.rb +393 -0
  77. data/test/replica_set/connection_test.rb +138 -0
  78. data/test/replica_set/count_test.rb +66 -0
  79. data/test/replica_set/cursor_test.rb +220 -0
  80. data/test/replica_set/insert_test.rb +157 -0
  81. data/test/replica_set/max_values_test.rb +151 -0
  82. data/test/replica_set/pinning_test.rb +105 -0
  83. data/test/replica_set/query_test.rb +73 -0
  84. data/test/replica_set/read_preference_test.rb +219 -0
  85. data/test/replica_set/refresh_test.rb +211 -0
  86. data/test/replica_set/replication_ack_test.rb +95 -0
  87. data/test/replica_set/ssl_test.rb +32 -0
  88. data/test/sharded_cluster/basic_test.rb +203 -0
  89. data/test/shared/authentication/basic_auth_shared.rb +260 -0
  90. data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
  91. data/test/shared/authentication/gssapi_shared.rb +176 -0
  92. data/test/shared/authentication/sasl_plain_shared.rb +96 -0
  93. data/test/shared/authentication/scram_shared.rb +92 -0
  94. data/test/shared/ssl_shared.rb +235 -0
  95. data/test/test_helper.rb +53 -94
  96. data/test/threading/basic_test.rb +120 -0
  97. data/test/tools/mongo_config.rb +708 -0
  98. data/test/tools/mongo_config_test.rb +160 -0
  99. data/test/unit/client_test.rb +381 -0
  100. data/test/unit/collection_test.rb +89 -53
  101. data/test/unit/connection_test.rb +282 -32
  102. data/test/unit/cursor_test.rb +206 -8
  103. data/test/unit/db_test.rb +55 -13
  104. data/test/unit/grid_test.rb +43 -16
  105. data/test/unit/mongo_sharded_client_test.rb +48 -0
  106. data/test/unit/node_test.rb +93 -0
  107. data/test/unit/pool_manager_test.rb +111 -0
  108. data/test/unit/read_pref_test.rb +406 -0
  109. data/test/unit/read_test.rb +159 -0
  110. data/test/unit/safe_test.rb +69 -36
  111. data/test/unit/sharding_pool_manager_test.rb +84 -0
  112. data/test/unit/write_concern_test.rb +175 -0
  113. data.tar.gz.sig +3 -0
  114. metadata +227 -216
  115. metadata.gz.sig +0 -0
  116. data/docs/CREDITS.md +0 -123
  117. data/docs/FAQ.md +0 -116
  118. data/docs/GridFS.md +0 -158
  119. data/docs/HISTORY.md +0 -244
  120. data/docs/RELEASES.md +0 -33
  121. data/docs/REPLICA_SETS.md +0 -72
  122. data/docs/TUTORIAL.md +0 -247
  123. data/docs/WRITE_CONCERN.md +0 -28
  124. data/lib/mongo/exceptions.rb +0 -71
  125. data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
  126. data/lib/mongo/repl_set_connection.rb +0 -342
  127. data/lib/mongo/test.rb +0 -20
  128. data/lib/mongo/util/pool.rb +0 -177
  129. data/lib/mongo/util/uri_parser.rb +0 -185
  130. data/test/async/collection_test.rb +0 -224
  131. data/test/async/connection_test.rb +0 -24
  132. data/test/async/cursor_test.rb +0 -162
  133. data/test/async/worker_pool_test.rb +0 -99
  134. data/test/auxillary/1.4_features.rb +0 -166
  135. data/test/auxillary/authentication_test.rb +0 -68
  136. data/test/auxillary/autoreconnect_test.rb +0 -41
  137. data/test/auxillary/fork_test.rb +0 -30
  138. data/test/auxillary/repl_set_auth_test.rb +0 -58
  139. data/test/auxillary/slave_connection_test.rb +0 -36
  140. data/test/auxillary/threaded_authentication_test.rb +0 -101
  141. data/test/bson/binary_test.rb +0 -15
  142. data/test/bson/bson_test.rb +0 -649
  143. data/test/bson/byte_buffer_test.rb +0 -208
  144. data/test/bson/hash_with_indifferent_access_test.rb +0 -38
  145. data/test/bson/json_test.rb +0 -17
  146. data/test/bson/object_id_test.rb +0 -154
  147. data/test/bson/ordered_hash_test.rb +0 -204
  148. data/test/bson/timestamp_test.rb +0 -24
  149. data/test/collection_test.rb +0 -910
  150. data/test/connection_test.rb +0 -309
  151. data/test/cursor_fail_test.rb +0 -75
  152. data/test/cursor_message_test.rb +0 -43
  153. data/test/cursor_test.rb +0 -483
  154. data/test/db_api_test.rb +0 -726
  155. data/test/db_connection_test.rb +0 -15
  156. data/test/db_test.rb +0 -287
  157. data/test/grid_file_system_test.rb +0 -243
  158. data/test/load/resque/load.rb +0 -21
  159. data/test/load/resque/processor.rb +0 -26
  160. data/test/load/thin/load.rb +0 -24
  161. data/test/load/unicorn/load.rb +0 -23
  162. data/test/load/unicorn/unicorn.rb +0 -29
  163. data/test/replica_sets/connect_test.rb +0 -94
  164. data/test/replica_sets/connection_string_test.rb +0 -32
  165. data/test/replica_sets/count_test.rb +0 -35
  166. data/test/replica_sets/insert_test.rb +0 -53
  167. data/test/replica_sets/pooled_insert_test.rb +0 -55
  168. data/test/replica_sets/query_secondaries.rb +0 -96
  169. data/test/replica_sets/query_test.rb +0 -51
  170. data/test/replica_sets/replication_ack_test.rb +0 -66
  171. data/test/replica_sets/rs_test_helper.rb +0 -27
  172. data/test/safe_test.rb +0 -68
  173. data/test/support/hash_with_indifferent_access.rb +0 -186
  174. data/test/support/keys.rb +0 -45
  175. data/test/support_test.rb +0 -18
  176. data/test/threading/threading_with_large_pool_test.rb +0 -90
  177. data/test/threading_test.rb +0 -87
  178. data/test/tools/auth_repl_set_manager.rb +0 -14
  179. data/test/tools/load.rb +0 -58
  180. data/test/tools/repl_set_manager.rb +0 -266
  181. data/test/tools/sharding_manager.rb +0 -202
  182. data/test/tools/test.rb +0 -4
  183. data/test/unit/pool_test.rb +0 -9
  184. data/test/unit/repl_set_connection_test.rb +0 -59
  185. 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