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.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/LICENSE +1 -1
  4. data/README.md +114 -282
  5. data/Rakefile +18 -4
  6. data/VERSION +1 -1
  7. data/bin/mongo_console +27 -5
  8. data/lib/mongo/bulk_write_collection_view.rb +387 -0
  9. data/lib/mongo/collection.rb +283 -222
  10. data/lib/mongo/collection_writer.rb +364 -0
  11. data/lib/mongo/{util → connection}/node.rb +58 -6
  12. data/lib/mongo/{util → connection}/pool.rb +61 -37
  13. data/lib/mongo/{util → connection}/pool_manager.rb +72 -22
  14. data/lib/mongo/{util → connection}/sharding_pool_manager.rb +13 -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 +19 -0
  21. data/lib/mongo/cursor.rb +183 -57
  22. data/lib/mongo/db.rb +302 -138
  23. data/lib/mongo/exception.rb +145 -0
  24. data/lib/mongo/functional/authentication.rb +455 -0
  25. data/lib/mongo/{util → functional}/logging.rb +23 -7
  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/{util → functional}/write_concern.rb +21 -9
  30. data/lib/mongo/functional.rb +20 -0
  31. data/lib/mongo/gridfs/grid.rb +19 -8
  32. data/lib/mongo/gridfs/grid_ext.rb +14 -0
  33. data/lib/mongo/gridfs/grid_file_system.rb +17 -4
  34. data/lib/mongo/gridfs/grid_io.rb +21 -9
  35. data/lib/mongo/gridfs.rb +18 -0
  36. data/lib/mongo/legacy.rb +76 -7
  37. data/lib/mongo/mongo_client.rb +246 -206
  38. data/lib/mongo/mongo_replica_set_client.rb +65 -15
  39. data/lib/mongo/mongo_sharded_client.rb +18 -3
  40. data/lib/mongo/networking.rb +47 -18
  41. data/lib/mongo/{util → utils}/conversions.rb +18 -3
  42. data/lib/mongo/{util → utils}/core_ext.rb +15 -32
  43. data/lib/mongo/{util → utils}/server_version.rb +15 -0
  44. data/lib/mongo/{util → utils}/support.rb +22 -55
  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 +44 -26
  48. data/mongo.gemspec +2 -2
  49. data/test/functional/authentication_test.rb +31 -10
  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 +1419 -654
  54. data/test/functional/collection_writer_test.rb +83 -0
  55. data/test/functional/conversions_test.rb +46 -2
  56. data/test/functional/cursor_fail_test.rb +17 -9
  57. data/test/functional/cursor_message_test.rb +28 -15
  58. data/test/functional/cursor_test.rb +300 -165
  59. data/test/functional/db_api_test.rb +294 -264
  60. data/test/functional/db_connection_test.rb +15 -3
  61. data/test/functional/db_test.rb +165 -99
  62. data/test/functional/grid_file_system_test.rb +124 -112
  63. data/test/functional/grid_io_test.rb +17 -3
  64. data/test/functional/grid_test.rb +16 -2
  65. data/test/functional/pool_test.rb +99 -10
  66. data/test/functional/safe_test.rb +18 -4
  67. data/test/functional/ssl_test.rb +29 -0
  68. data/test/functional/support_test.rb +14 -0
  69. data/test/functional/timeout_test.rb +27 -27
  70. data/test/functional/uri_test.rb +268 -22
  71. data/test/functional/write_concern_test.rb +19 -5
  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 +28 -11
  75. data/test/replica_set/basic_test.rb +79 -23
  76. data/test/replica_set/client_test.rb +253 -124
  77. data/test/replica_set/connection_test.rb +59 -37
  78. data/test/replica_set/count_test.rb +18 -2
  79. data/test/replica_set/cursor_test.rb +30 -8
  80. data/test/replica_set/insert_test.rb +109 -2
  81. data/test/replica_set/max_values_test.rb +85 -10
  82. data/test/replica_set/pinning_test.rb +66 -2
  83. data/test/replica_set/query_test.rb +17 -3
  84. data/test/replica_set/read_preference_test.rb +115 -96
  85. data/test/replica_set/refresh_test.rb +59 -9
  86. data/test/replica_set/replication_ack_test.rb +32 -11
  87. data/test/replica_set/ssl_test.rb +32 -0
  88. data/test/sharded_cluster/basic_test.rb +73 -25
  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 +47 -196
  96. data/test/threading/basic_test.rb +42 -2
  97. data/test/tools/mongo_config.rb +175 -35
  98. data/test/tools/mongo_config_test.rb +15 -1
  99. data/test/unit/client_test.rb +186 -57
  100. data/test/unit/collection_test.rb +44 -54
  101. data/test/unit/connection_test.rb +160 -71
  102. data/test/unit/cursor_test.rb +37 -3
  103. data/test/unit/db_test.rb +38 -14
  104. data/test/unit/grid_test.rb +15 -1
  105. data/test/unit/mongo_sharded_client_test.rb +30 -14
  106. data/test/unit/node_test.rb +16 -1
  107. data/test/unit/pool_manager_test.rb +21 -4
  108. data/test/unit/read_pref_test.rb +386 -1
  109. data/test/unit/read_test.rb +27 -13
  110. data/test/unit/safe_test.rb +22 -8
  111. data/test/unit/sharding_pool_manager_test.rb +25 -4
  112. data/test/unit/write_concern_test.rb +23 -9
  113. data.tar.gz.sig +0 -0
  114. metadata +80 -54
  115. metadata.gz.sig +0 -0
  116. data/lib/mongo/exceptions.rb +0 -65
  117. data/lib/mongo/util/read_preference.rb +0 -112
  118. data/lib/mongo/util/socket_util.rb +0 -20
  119. data/lib/mongo/util/ssl_socket.rb +0 -51
  120. data/lib/mongo/util/tcp_socket.rb +0 -62
  121. data/lib/mongo/util/thread_local_variable_manager.rb +0 -11
  122. data/lib/mongo/util/unix_socket.rb +0 -23
  123. data/lib/mongo/util/uri_parser.rb +0 -337
  124. data/test/functional/connection_test.rb +0 -449
  125. data/test/functional/threading_test.rb +0 -95
  126. data/test/replica_set/complex_connect_test.rb +0 -64
  127. data/test/shared/authentication.rb +0 -66
  128. data/test/unit/pool_test.rb +0 -9
  129. data/test/unit/util_test.rb +0 -55
data/test/test_helper.rb CHANGED
@@ -1,210 +1,61 @@
1
- begin
2
- require 'pry-rescue'
3
- require 'pry-nav'
4
- rescue LoadError
5
- # failed to load, skipping pry
6
- end
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.
7
14
 
8
- # SimpleCov must load before our code - A coverage report summary line will print after each test suite
9
- if RUBY_VERSION >= '1.9.0' && RUBY_ENGINE == 'ruby'
10
- if ENV.key?('COVERAGE')
11
- require 'simplecov'
12
- SimpleCov.start do
13
- add_group "Mongo", 'lib/mongo'
14
- add_group "BSON", 'lib/bson'
15
- add_filter "/test/"
16
- merge_timeout 3600
17
- command_name ENV['SIMPLECOV_COMMAND_NAME'] if ENV.has_key?('SIMPLECOV_COMMAND_NAME')
18
- end
19
- end
20
- end
21
- gem 'test-unit' # Do NOT remove this line - gem version is needed for Test::Unit::TestCase.shutdown
22
- require 'test/unit'
23
- require 'tools/mongo_config'
24
-
25
- class Test::Unit::TestCase
26
-
27
- TEST_DATA = File.join(File.dirname(__FILE__), 'data')
28
-
29
- def ensure_cluster(kind=nil, opts={})
30
- @@cluster ||= nil
15
+ # NOTE: on ruby <1.9 you need to run individual tests with 'bundle exec'
31
16
 
32
- unless @@cluster
33
- if kind == :rs
34
- cluster_opts = Mongo::Config::DEFAULT_REPLICA_SET.dup
35
- else
36
- cluster_opts = Mongo::Config::DEFAULT_SHARDED_SIMPLE.dup
37
- end
17
+ unless RUBY_VERSION < '1.9' || ENV.key?('JENKINS_CI')
18
+ require 'simplecov'
19
+ require 'coveralls'
38
20
 
39
- cluster_opts.merge!(opts)
21
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
22
+ SimpleCov::Formatter::HTMLFormatter,
23
+ Coveralls::SimpleCov::Formatter
24
+ ]
40
25
 
41
- dbpath = ENV['DBPATH'] || 'data'
42
- cluster_opts.merge!(:dbpath => dbpath)
26
+ SimpleCov.start do
27
+ add_group 'Driver', 'lib/mongo'
28
+ add_group 'BSON', 'lib/bson'
43
29
 
44
- #debug 1, opts
45
- config = Mongo::Config.cluster(cluster_opts)
46
- #debug 1, config
47
- @@cluster = Mongo::Config::ClusterManager.new(config)
48
-
49
- Test::Unit::TestCase.class_eval do
50
- @@force_shutdown = false
51
-
52
- def self.shutdown
53
- if @@force_shutdown || /rake_test_loader/ !~ $0
54
- @@cluster.stop
55
- @@cluster.clobber
56
- end
57
- end
58
- end
59
- end
60
-
61
- @@cluster.start
62
- instance_variable_set("@#{kind}", @@cluster)
30
+ add_filter 'tasks'
31
+ add_filter 'test'
32
+ add_filter 'bin'
63
33
  end
64
-
65
- # Generic code for rescuing connection failures and retrying operations.
66
- # This could be combined with some timeout functionality.
67
- def rescue_connection_failure(max_retries=30)
68
- retries = 0
69
- begin
70
- yield
71
- rescue Mongo::ConnectionFailure => ex
72
- #puts "Rescue attempt #{retries}: from #{ex}"
73
- retries += 1
74
- raise ex if retries > max_retries
75
- sleep(2)
76
- retry
77
- end
78
- end
79
- end
80
-
81
- def silently
82
- warn_level = $VERBOSE
83
- $VERBOSE = nil
84
- begin
85
- result = yield
86
- ensure
87
- $VERBOSE = warn_level
88
- end
89
- result
90
- end
91
-
92
- begin
93
- silently { require 'shoulda' }
94
- silently { require 'mocha/setup' }
95
- rescue LoadError
96
- puts <<MSG
97
-
98
- This test suite requires shoulda and mocha.
99
- You can install them as follows:
100
- gem install shoulda
101
- gem install mocha
102
-
103
- MSG
104
- exit
105
- end
106
-
107
- unless defined? MONGO_TEST_DB
108
- MONGO_TEST_DB = 'ruby-test-db'
109
- end
110
-
111
- unless defined? TEST_PORT
112
- TEST_PORT = ENV['MONGO_RUBY_DRIVER_PORT'] ? ENV['MONGO_RUBY_DRIVER_PORT'].to_i : Mongo::MongoClient::DEFAULT_PORT
113
- end
114
-
115
- unless defined? TEST_HOST
116
- TEST_HOST = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
117
34
  end
118
35
 
119
- class Test::Unit::TestCase
120
- include Mongo
121
- include BSON
36
+ # required for at_exit, at_start hooks
37
+ require 'test-unit'
122
38
 
123
- def self.standard_connection(options={}, legacy=false)
124
- if legacy
125
- Connection.new(TEST_HOST, TEST_PORT, options)
126
- else
127
- MongoClient.new(TEST_HOST, TEST_PORT, options)
128
- end
129
- end
130
-
131
- def standard_connection(options={}, legacy=false)
132
- self.class.standard_connection(options, legacy)
133
- end
134
-
135
- def self.host_port
136
- "#{mongo_host}:#{mongo_port}"
137
- end
138
-
139
- def self.mongo_host
140
- TEST_HOST
141
- end
142
-
143
- def self.mongo_port
144
- TEST_PORT
145
- end
146
-
147
- def host_port
148
- self.class.host_port
149
- end
150
-
151
- def mongo_host
152
- self.class.mongo_host
153
- end
154
-
155
- def mongo_port
156
- self.class.mongo_port
157
- end
158
-
159
- def method_name
160
- caller[0]=~/`(.*?)'/
161
- $1
162
- end
163
-
164
- def step_down_command
165
- # Adding force=true to avoid 'no secondaries within 10 seconds of my optime' errors
166
- step_down_command = BSON::OrderedHash.new
167
- step_down_command[:replSetStepDown] = 5
168
- step_down_command[:force] = true
169
- step_down_command
170
- end
171
-
172
- def new_mock_socket(host='localhost', port=27017)
173
- socket = Object.new
174
- socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
175
- socket.stubs(:close)
176
- socket.stubs(:closed?)
177
- socket.stubs(:checkin)
178
- socket.stubs(:pool)
179
- socket
180
- end
39
+ require 'test/unit'
40
+ require 'shoulda'
41
+ require 'mocha/setup'
181
42
 
182
- def new_mock_unix_socket(sockfile='/tmp/mongod.sock')
183
- socket = Object.new
184
- socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP)
185
- socket.stubs(:close)
186
- socket.stubs(:closed?)
187
- socket
188
- end
43
+ # cluster manager
44
+ require 'tools/mongo_config'
189
45
 
190
- def new_mock_db
191
- Object.new
192
- end
46
+ # For kerberos testing.
47
+ begin
48
+ require 'mongo_kerberos'
49
+ rescue LoadError; end
193
50
 
194
- def assert_raise_error(klass, message=nil)
195
- begin
196
- yield
197
- rescue => e
198
- if klass.to_s != e.class.to_s
199
- flunk "Expected exception class #{klass} but got #{e.class}.\n #{e.backtrace}"
200
- end
51
+ # test helpers
52
+ require 'helpers/general'
53
+ require 'helpers/test_unit'
201
54
 
202
- if message && !e.message.include?(message)
203
- p e.backtrace
204
- flunk "#{e.message} does not include #{message}.\n#{e.backtrace}"
205
- end
206
- else
207
- flunk "Expected assertion #{klass} but none was raised."
208
- end
209
- end
55
+ # optional development and debug utilities
56
+ begin
57
+ require 'pry-rescue'
58
+ require 'pry-nav'
59
+ rescue LoadError
60
+ # failed to load, skipping pry
210
61
  end
@@ -1,12 +1,26 @@
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 TestThreading < Test::Unit::TestCase
17
+ class ThreadingTest < Test::Unit::TestCase
4
18
 
5
19
  include Mongo
6
20
 
7
21
  def setup
8
22
  @client = standard_connection(:pool_size => 10, :pool_timeout => 30)
9
- @db = @client.db(MONGO_TEST_DB)
23
+ @db = @client.db(TEST_DB)
10
24
  @coll = @db.collection('thread-test-collection')
11
25
  @coll.drop
12
26
 
@@ -77,4 +91,30 @@ class TestThreading < Test::Unit::TestCase
77
91
  assert thread_values.all?{|v| v == 1000}
78
92
  assert_equal thread_values.size, n_threads
79
93
  end
94
+
95
+ def test_threading
96
+ @coll.drop
97
+ @coll = @db.collection('thread-test-collection')
98
+
99
+ docs = []
100
+ 1000.times {|i| docs << {:x => i}}
101
+ @coll.insert(docs)
102
+
103
+ threads = []
104
+
105
+ 10.times do |i|
106
+ threads[i] = Thread.new do
107
+ sum = 0
108
+ @coll.find().each do |document|
109
+ sum += document["x"]
110
+ end
111
+ assert_equal 499500, sum
112
+ end
113
+ end
114
+
115
+ 10.times do |i|
116
+ threads[i].join
117
+ end
118
+ end
119
+
80
120
  end
@@ -1,4 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
+
3
+ # Copyright (C) 2009-2013 MongoDB, Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
2
17
  require 'socket'
3
18
  require 'fileutils'
4
19
  require 'mongo'
@@ -26,7 +41,7 @@ module Mongo
26
41
  class Config
27
42
  DEFAULT_BASE_OPTS = { :host => 'localhost', :dbpath => 'data', :logpath => 'data/log' }
28
43
  DEFAULT_REPLICA_SET = DEFAULT_BASE_OPTS.merge( :replicas => 3, :arbiters => 0 )
29
- DEFAULT_SHARDED_SIMPLE = DEFAULT_BASE_OPTS.merge( :shards => 2, :configs => 1, :routers => 4 )
44
+ DEFAULT_SHARDED_SIMPLE = DEFAULT_BASE_OPTS.merge( :shards => 2, :configs => 1, :routers => 2 )
30
45
  DEFAULT_SHARDED_REPLICA = DEFAULT_SHARDED_SIMPLE.merge( :replicas => 3, :arbiters => 0)
31
46
 
32
47
  IGNORE_KEYS = [:host, :command, :_id]
@@ -35,7 +50,7 @@ module Mongo
35
50
  MONGODS_OPT_KEYS = [:mongods]
36
51
  CLUSTER_OPT_KEYS = SHARDING_OPT_KEYS + REPLICA_OPT_KEYS + MONGODS_OPT_KEYS
37
52
 
38
- FLAGS = [:noprealloc, :smallfiles, :logappend, :configsvr, :shardsvr, :quiet, :fastsync, :auth]
53
+ FLAGS = [:noprealloc, :smallfiles, :logappend, :configsvr, :shardsvr, :quiet, :fastsync, :auth, :ipv6]
39
54
 
40
55
  DEFAULT_VERIFIES = 60
41
56
  BASE_PORT = 3000
@@ -49,6 +64,7 @@ module Mongo
49
64
  raise "missing required option" if [:host, :dbpath].any?{|k| !opts[k]}
50
65
 
51
66
  config = opts.reject {|k,v| CLUSTER_OPT_KEYS.include?(k)}
67
+
52
68
  kinds = CLUSTER_OPT_KEYS.select{|key| opts.has_key?(key)} # order is significant
53
69
 
54
70
  replica_count = 0
@@ -67,9 +83,11 @@ module Mongo
67
83
  make_config(opts)
68
84
  when :routers
69
85
  make_router(config, opts)
86
+ when :shards
87
+ make_standalone_shard(kind, opts)
70
88
  else
71
89
  make_mongod(kind, opts)
72
- end
90
+ end
73
91
 
74
92
  replica_count += 1 if [:replicas, :arbiters].member?(kind)
75
93
  node
@@ -102,6 +120,8 @@ module Mongo
102
120
  quiet = opts[:quiet] || true
103
121
  fast_sync = opts[:fastsync] || false
104
122
  auth = opts[:auth] || true
123
+ ipv6 = opts[:ipv6].nil? ? true : opts[:ipv6]
124
+ setParameter = opts[:setParameter] || 'enableTestCommands=1'
105
125
 
106
126
  params.merge(:command => mongod,
107
127
  :dbpath => path,
@@ -109,7 +129,23 @@ module Mongo
109
129
  :noprealloc => noprealloc,
110
130
  :quiet => quiet,
111
131
  :fastsync => fast_sync,
112
- :auth => auth)
132
+ :auth => auth,
133
+ :ipv6 => ipv6,
134
+ :setParameter => setParameter)
135
+ end
136
+
137
+ def self.key_file(opts)
138
+ keyFile = opts[:key_file] || '/test/fixtures/auth/keyfile'
139
+ keyFile = Dir.pwd << keyFile
140
+ system "chmod 600 #{keyFile}"
141
+ keyFile
142
+ end
143
+
144
+ # A regular mongod minus --auth and plus --keyFile.
145
+ def self.make_standalone_shard(kind, opts)
146
+ params = make_mongod(kind, opts)
147
+ params.delete(:auth)
148
+ params.merge(:keyFile => key_file(opts))
113
149
  end
114
150
 
115
151
  def self.make_replica(opts, id)
@@ -117,20 +153,18 @@ module Mongo
117
153
 
118
154
  replSet = opts[:replSet] || 'ruby-driver-test'
119
155
  oplogSize = opts[:oplog_size] || 5
120
- keyFile = opts[:key_file] || '/test/tools/keyfile.txt'
121
-
122
- keyFile = Dir.pwd << keyFile
123
- system "chmod 600 #{keyFile}"
124
156
 
125
157
  params.merge(:_id => id,
126
158
  :replSet => replSet,
127
159
  :oplogSize => oplogSize,
128
- :keyFile => keyFile)
160
+ :keyFile => key_file(opts))
129
161
  end
130
162
 
131
163
  def self.make_config(opts)
132
164
  params = make_mongod('configs', opts)
133
- params.merge(:configsvr => nil)
165
+ params.delete(:auth)
166
+ params.merge(:configsvr => true,
167
+ :keyFile => key_file(opts))
134
168
  end
135
169
 
136
170
  def self.make_router(config, opts)
@@ -138,8 +172,9 @@ module Mongo
138
172
  mongos = ENV['MONGOS'] || 'mongos'
139
173
 
140
174
  params.merge(
141
- :command => mongos,
142
- :configdb => self.configdb(config)
175
+ :command => mongos,
176
+ :configdb => self.configdb(config),
177
+ :keyFile => key_file(opts)
143
178
  )
144
179
  end
145
180
 
@@ -268,6 +303,11 @@ module Mongo
268
303
 
269
304
  def initialize(config)
270
305
  @config = config
306
+ cmd = init_config!
307
+ super(cmd, @config[:host], @config[:port])
308
+ end
309
+
310
+ def init_config!
271
311
  dbpath = @config[:dbpath]
272
312
  [dbpath, File.dirname(@config[:logpath])].compact.each{|dir| FileUtils.mkdir_p(dir) unless File.directory?(dir) }
273
313
  command = @config[:command] || 'mongod'
@@ -281,7 +321,6 @@ module Mongo
281
321
  end
282
322
  end
283
323
  cmd = [command, arguments].flatten.compact
284
- super(cmd, @config[:host], @config[:port])
285
324
  end
286
325
 
287
326
  def start(verifies = DEFAULT_VERIFIES)
@@ -301,8 +340,13 @@ module Mongo
301
340
  sleep 1
302
341
  end
303
342
  end
304
- system "ps -fp #{@pid}; cat #{@config[:logpath]}"
305
- raise Mongo::ConnectionFailure, "DbServer.start verify via connection probe failed - port:#{@port.inspect} @pid:#{@pid.inspect} kill:#{Process.kill(0, @pid).inspect} running?:#{running?.inspect} cmd:#{cmd.inspect}"
343
+ if @config.delete(:setParameter)
344
+ @cmd = init_config!
345
+ start(verifies)
346
+ else
347
+ system "ps -fp #{@pid}; cat #{@config[:logpath]}"
348
+ raise Mongo::ConnectionFailure, "DbServer.start verify via connection probe failed - port:#{@port.inspect} @pid:#{@pid.inspect} kill:#{Process.kill(0, @pid).inspect} running?:#{running?.inspect} cmd:#{cmd.inspect}"
349
+ end
306
350
  end
307
351
 
308
352
  end
@@ -313,7 +357,7 @@ module Mongo
313
357
  @config = config
314
358
  @servers = {}
315
359
  Mongo::Config::CLUSTER_OPT_KEYS.each do |key|
316
- @servers[key] = @config[key].collect{|conf| DbServer.new(conf)} if @config[key]
360
+ @servers[key] = @config[key].collect{|conf| p conf; DbServer.new(conf)} if @config[key]
317
361
  end
318
362
  end
319
363
 
@@ -321,6 +365,35 @@ module Mongo
321
365
  @servers.collect{|k,v| (!key || key == k) ? v : nil}.flatten.compact
322
366
  end
323
367
 
368
+ def ensure_authenticated(client)
369
+ begin
370
+ client[TEST_DB].authenticate(TEST_USER, TEST_USER_PWD)
371
+ rescue Mongo::MongoArgumentError => ex
372
+ # client is already authenticated
373
+ raise ex unless ex.message =~ /already authenticated/
374
+ rescue Mongo::AuthenticationError => ex
375
+ # 1) The creds are wrong
376
+ # 2) Or the user doesn't exist
377
+ roles = [ 'dbAdminAnyDatabase',
378
+ 'userAdminAnyDatabase',
379
+ 'readWriteAnyDatabase',
380
+ 'clusterAdmin' ]
381
+ begin
382
+ # Try to add the user for case (2)
383
+ client[TEST_DB].add_user(TEST_USER, TEST_USER_PWD, nil, :roles => roles)
384
+ client[TEST_DB].authenticate(TEST_USER, TEST_USER_PWD)
385
+ rescue Mongo::ConnectionFailure, Mongo::OperationFailure => ex
386
+ # Maybe not master, so try to authenticate
387
+ # 2.2 throws an OperationFailure if add_user fails
388
+ begin
389
+ client[TEST_DB].authenticate(TEST_USER, TEST_USER_PWD)
390
+ rescue => ex
391
+ # Maybe creds are wrong, nothing we can do
392
+ end
393
+ end
394
+ end
395
+ end
396
+
324
397
  def command( cmd_servers, db_name, cmd, opts = {} )
325
398
  ret = []
326
399
  cmd = cmd.class == Array ? cmd : [ cmd ]
@@ -330,6 +403,7 @@ module Mongo
330
403
  debug 3, cmd_server.inspect
331
404
  cmd_server = cmd_server.config if cmd_server.is_a?(DbServer)
332
405
  client = Mongo::MongoClient.new(cmd_server[:host], cmd_server[:port])
406
+ ensure_authenticated(client)
333
407
  cmd.each do |c|
334
408
  debug 3, "ClusterManager.command c:#{c.inspect}"
335
409
  response = client[db_name].command( c, opts )
@@ -343,6 +417,10 @@ module Mongo
343
417
  ret.size == 1 ? ret.first : ret
344
418
  end
345
419
 
420
+ def replica_set?
421
+ !!config[:replicas]
422
+ end
423
+
346
424
  def repl_set_get_status
347
425
  command( @config[:replicas], 'admin', { :replSetGetStatus => 1 }, {:check_response => false } )
348
426
  end
@@ -350,6 +428,7 @@ module Mongo
350
428
  def repl_set_get_config
351
429
  host, port = primary_name.split(":")
352
430
  client = Mongo::MongoClient.new(host, port)
431
+ ensure_authenticated(client)
353
432
  client['local']['system.replset'].find_one
354
433
  end
355
434
 
@@ -368,27 +447,49 @@ module Mongo
368
447
  end
369
448
 
370
449
  def repl_set_startup
371
- states = nil
372
- 60.times do
373
- states = repl_set_get_status.zip(repl_set_is_master)
450
+ states = nil
451
+ healthy = false
452
+
453
+ 80.times do
454
+ # enter the thunderdome...
455
+ states = repl_set_get_status.zip(repl_set_is_master)
374
456
  healthy = states.all? do |status, is_master|
375
- members = status['members']
376
- if status['ok'] == 1.0 && members.collect{|m| m['state']}.all?{|state| [1,2,7].index(state)}
377
- members.any?{|m| m['state'] == 1} &&
378
- case status['myState']
379
- when 1
380
- is_master['ismaster'] == true && is_master['secondary'] == false
381
- when 2
382
- is_master['ismaster'] == false && is_master['secondary'] == true
383
- when 7
384
- is_master['ismaster'] == false && is_master['secondary'] == false
385
- end
457
+ # check replica set status for member list
458
+ next unless status['ok'] == 1.0 && (members = status['members'])
459
+
460
+ # ensure all replica set members are in a valid state
461
+ next unless members.all? { |m| [1,2,7].include?(m['state']) }
462
+
463
+ # check for primary replica set member
464
+ next unless (primary = members.find { |m| m['state'] == 1 })
465
+
466
+ # check replica set member optimes
467
+ primary_optime = (primary['optime']['ts'] ? primary['optime']['ts'] : primary['optime']).seconds
468
+ next unless primary_optime && members.all? do |m|
469
+ m_optime = (m['optime']['ts'] ? m['optime']['ts'] : m['optime']).seconds
470
+ m['state'] == 7 || primary_optime - m_optime < 5
471
+ end
472
+
473
+ # check replica set state
474
+ case status['myState']
475
+ when 1
476
+ is_master['ismaster'] == true &&
477
+ is_master['secondary'] == false
478
+ when 2
479
+ is_master['ismaster'] == false &&
480
+ is_master['secondary'] == true
481
+ when 7
482
+ is_master['ismaster'] == false &&
483
+ is_master['secondary'] == false
386
484
  end
387
485
  end
388
- return true if healthy
486
+
487
+ return healthy if healthy
389
488
  sleep(1)
390
489
  end
391
- raise Mongo::OperationFailure, "replSet startup failed - status: #{states.inspect}"
490
+
491
+ raise Mongo::OperationFailure,
492
+ "replSet startup failed - status: #{states.inspect}"
392
493
  end
393
494
 
394
495
  def repl_set_seeds
@@ -403,13 +504,19 @@ module Mongo
403
504
  repl_set_seeds.join(',')
404
505
  end
405
506
 
507
+ def members_uri
508
+ members = @config[:replicas] || @config[:routers]
509
+ members.collect{|node| "#{node[:host]}:#{node[:port]}"}.join(',')
510
+ end
511
+
406
512
  def repl_set_name
407
513
  @config[:replicas].first[:replSet]
408
514
  end
409
515
 
410
516
  def member_names_by_state(state)
411
517
  states = Array(state)
412
- status = repl_set_get_status.first
518
+ # Any status with a REMOVED node won't have the full cluster state
519
+ status = repl_set_get_status.find {|status| status['members'].find {|m| m['state'] == 'REMOVED'}.nil?}
413
520
  status['members'].find_all{|member| states.index(member['state']) }.collect{|member| member['name']}
414
521
  end
415
522
 
@@ -513,7 +620,15 @@ module Mongo
513
620
  end
514
621
 
515
622
  def addshards(shards = @config[:shards])
516
- command( @config[:routers].first, 'admin', Array(shards).collect{|s| { :addshard => "#{s[:host]}:#{s[:port]}" } } )
623
+ begin
624
+ command( @config[:routers].first, 'admin', Array(shards).collect{|s| { :addshard => "#{s[:host]}:#{s[:port]}" } } )
625
+ rescue Mongo::OperationFailure => ex
626
+ # Because we cannot run the listshards command under the localhost
627
+ # exception in > 2.7.1, we run the risk of attempting to add the same shard twice.
628
+ # Our tests may add a local db to a shard, if the cluster is still up,
629
+ # then we can ignore this.
630
+ raise ex unless ex.message =~ /host already used/
631
+ end
517
632
  end
518
633
 
519
634
  def listshards
@@ -543,7 +658,8 @@ module Mongo
543
658
  servers.each{|server| server.start}
544
659
  # TODO - sharded replica sets - pending
545
660
  if @config[:replicas]
546
- repl_set_initiate if repl_set_get_status.first['startupStatus'] == 3
661
+ repl_set_initiate if repl_set_get_status.first['code'] == 94 ||
662
+ (repl_set_get_status.first['startupStatus'] && repl_set_get_status.first['startupStatus'] == 3)
547
663
  repl_set_startup
548
664
  end
549
665
  if @config[:routers]
@@ -553,7 +669,31 @@ module Mongo
553
669
  end
554
670
  alias :restart :start
555
671
 
672
+ def delete_users
673
+ cmd_servers = replica_set? ? [ primary ] : routers
674
+
675
+ cmd_servers.each do |cmd_server|
676
+ next unless cmd_server
677
+ begin
678
+ client = Mongo::MongoClient.new(cmd_server.config[:host],
679
+ cmd_server.config[:port])
680
+ ensure_authenticated(client)
681
+ db = client[TEST_DB]
682
+
683
+ if client.server_version < '2.5'
684
+ db['system.users'].remove
685
+ else
686
+ db.command(:dropAllUsersFromDatabase => 1)
687
+ end
688
+ break
689
+ rescue Mongo::ConnectionFailure
690
+ end
691
+ end
692
+ end
693
+
556
694
  def stop
695
+ start
696
+ delete_users
557
697
  servers.each{|server| server.stop}
558
698
  self
559
699
  end