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,708 @@
|
|
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
|
+
|
17
|
+
require 'socket'
|
18
|
+
require 'fileutils'
|
19
|
+
require 'mongo'
|
20
|
+
require 'sfl'
|
21
|
+
|
22
|
+
$debug_level = 2
|
23
|
+
STDOUT.sync = true
|
24
|
+
|
25
|
+
def debug(level, arg)
|
26
|
+
if level <= $debug_level
|
27
|
+
file_line = caller[0][/(.*:\d+):/, 1]
|
28
|
+
calling_method = caller[0][/`([^']*)'/, 1]
|
29
|
+
puts "#{file_line}:#{calling_method}:#{arg.class == String ? arg : arg.inspect}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Design Notes
|
35
|
+
# Configuration and Cluster Management are modularized with the concept that the Cluster Manager
|
36
|
+
# can be supplied with any configuration to run.
|
37
|
+
# A configuration can be edited, modified, copied into a test file, and supplied to a cluster manager
|
38
|
+
# as a parameter.
|
39
|
+
#
|
40
|
+
module Mongo
|
41
|
+
class Config
|
42
|
+
DEFAULT_BASE_OPTS = { :host => 'localhost', :dbpath => 'data', :logpath => 'data/log' }
|
43
|
+
DEFAULT_REPLICA_SET = DEFAULT_BASE_OPTS.merge( :replicas => 3, :arbiters => 0 )
|
44
|
+
DEFAULT_SHARDED_SIMPLE = DEFAULT_BASE_OPTS.merge( :shards => 2, :configs => 1, :routers => 2 )
|
45
|
+
DEFAULT_SHARDED_REPLICA = DEFAULT_SHARDED_SIMPLE.merge( :replicas => 3, :arbiters => 0)
|
46
|
+
|
47
|
+
IGNORE_KEYS = [:host, :command, :_id]
|
48
|
+
SHARDING_OPT_KEYS = [:shards, :configs, :routers]
|
49
|
+
REPLICA_OPT_KEYS = [:replicas, :arbiters]
|
50
|
+
MONGODS_OPT_KEYS = [:mongods]
|
51
|
+
CLUSTER_OPT_KEYS = SHARDING_OPT_KEYS + REPLICA_OPT_KEYS + MONGODS_OPT_KEYS
|
52
|
+
|
53
|
+
FLAGS = [:noprealloc, :smallfiles, :logappend, :configsvr, :shardsvr, :quiet, :fastsync, :auth, :ipv6]
|
54
|
+
|
55
|
+
DEFAULT_VERIFIES = 60
|
56
|
+
BASE_PORT = 3000
|
57
|
+
@@port = BASE_PORT
|
58
|
+
|
59
|
+
def self.configdb(config)
|
60
|
+
config[:configs].collect{|c|"#{c[:host]}:#{c[:port]}"}.join(' ')
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.cluster(opts = DEFAULT_SHARDED_SIMPLE)
|
64
|
+
raise "missing required option" if [:host, :dbpath].any?{|k| !opts[k]}
|
65
|
+
|
66
|
+
config = opts.reject {|k,v| CLUSTER_OPT_KEYS.include?(k)}
|
67
|
+
|
68
|
+
kinds = CLUSTER_OPT_KEYS.select{|key| opts.has_key?(key)} # order is significant
|
69
|
+
|
70
|
+
replica_count = 0
|
71
|
+
|
72
|
+
kinds.each do |kind|
|
73
|
+
config[kind] = opts.fetch(kind,1).times.collect do |i| #default to 1 of whatever
|
74
|
+
if kind == :shards && opts[:replicas]
|
75
|
+
self.cluster(opts.reject{|k,v| SHARDING_OPT_KEYS.include?(k)}.merge(:dbpath => path))
|
76
|
+
else
|
77
|
+
node = case kind
|
78
|
+
when :replicas
|
79
|
+
make_replica(opts, replica_count)
|
80
|
+
when :arbiters
|
81
|
+
make_replica(opts, replica_count)
|
82
|
+
when :configs
|
83
|
+
make_config(opts)
|
84
|
+
when :routers
|
85
|
+
make_router(config, opts)
|
86
|
+
when :shards
|
87
|
+
make_standalone_shard(kind, opts)
|
88
|
+
else
|
89
|
+
make_mongod(kind, opts)
|
90
|
+
end
|
91
|
+
|
92
|
+
replica_count += 1 if [:replicas, :arbiters].member?(kind)
|
93
|
+
node
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
config
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.make_mongo(kind, opts)
|
101
|
+
dbpath = opts[:dbpath]
|
102
|
+
port = self.get_available_port
|
103
|
+
path = "#{dbpath}/#{kind}-#{port}"
|
104
|
+
logpath = "#{path}/#{kind}.log"
|
105
|
+
|
106
|
+
{ :host => opts[:host],
|
107
|
+
:port => port,
|
108
|
+
:logpath => logpath,
|
109
|
+
:logappend => true }
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.make_mongod(kind, opts)
|
113
|
+
params = make_mongo('mongods', opts)
|
114
|
+
|
115
|
+
mongod = ENV['MONGOD'] || 'mongod'
|
116
|
+
path = File.dirname(params[:logpath])
|
117
|
+
|
118
|
+
noprealloc = opts[:noprealloc] || true
|
119
|
+
smallfiles = opts[:smallfiles] || true
|
120
|
+
quiet = opts[:quiet] || true
|
121
|
+
fast_sync = opts[:fastsync] || false
|
122
|
+
auth = opts[:auth] || true
|
123
|
+
ipv6 = opts[:ipv6].nil? ? true : opts[:ipv6]
|
124
|
+
setParameter = opts[:setParameter] || 'enableTestCommands=1'
|
125
|
+
|
126
|
+
params.merge(:command => mongod,
|
127
|
+
:dbpath => path,
|
128
|
+
:smallfiles => smallfiles,
|
129
|
+
:noprealloc => noprealloc,
|
130
|
+
:quiet => quiet,
|
131
|
+
:fastsync => fast_sync,
|
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))
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.make_replica(opts, id)
|
152
|
+
params = make_mongod('replicas', opts)
|
153
|
+
|
154
|
+
replSet = opts[:replSet] || 'ruby-driver-test'
|
155
|
+
oplogSize = opts[:oplog_size] || 5
|
156
|
+
|
157
|
+
params.merge(:_id => id,
|
158
|
+
:replSet => replSet,
|
159
|
+
:oplogSize => oplogSize,
|
160
|
+
:keyFile => key_file(opts))
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.make_config(opts)
|
164
|
+
params = make_mongod('configs', opts)
|
165
|
+
params.delete(:auth)
|
166
|
+
params.merge(:configsvr => true,
|
167
|
+
:keyFile => key_file(opts))
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.make_router(config, opts)
|
171
|
+
params = make_mongo('routers', opts)
|
172
|
+
mongos = ENV['MONGOS'] || 'mongos'
|
173
|
+
|
174
|
+
params.merge(
|
175
|
+
:command => mongos,
|
176
|
+
:configdb => self.configdb(config),
|
177
|
+
:keyFile => key_file(opts)
|
178
|
+
)
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.port_available?(port)
|
182
|
+
ret = false
|
183
|
+
socket = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0)
|
184
|
+
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
|
185
|
+
sockaddr = Socket.sockaddr_in(port, '0.0.0.0')
|
186
|
+
begin
|
187
|
+
socket.bind(sockaddr)
|
188
|
+
ret = true
|
189
|
+
rescue Exception
|
190
|
+
end
|
191
|
+
socket.close
|
192
|
+
ret
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.get_available_port
|
196
|
+
while true
|
197
|
+
port = @@port
|
198
|
+
@@port += 1
|
199
|
+
break if port_available?(port)
|
200
|
+
end
|
201
|
+
port
|
202
|
+
end
|
203
|
+
|
204
|
+
class SysProc
|
205
|
+
attr_reader :pid, :cmd
|
206
|
+
|
207
|
+
def initialize(cmd = nil)
|
208
|
+
@pid = nil
|
209
|
+
@cmd = cmd
|
210
|
+
end
|
211
|
+
|
212
|
+
def clear_zombie
|
213
|
+
if @pid
|
214
|
+
begin
|
215
|
+
pid = Process.waitpid(@pid, Process::WNOHANG)
|
216
|
+
rescue Errno::ECHILD
|
217
|
+
# JVM might have already reaped the exit status
|
218
|
+
end
|
219
|
+
@pid = nil if pid && pid > 0
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def start(verifies = 0)
|
224
|
+
clear_zombie
|
225
|
+
return @pid if running?
|
226
|
+
begin
|
227
|
+
# redirection not supported in jruby
|
228
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
229
|
+
@pid = Process.spawn(*@cmd)
|
230
|
+
else
|
231
|
+
cmd_and_opts = [@cmd, {:out => '/dev/null'}].flatten
|
232
|
+
@pid = Process.spawn(*cmd_and_opts)
|
233
|
+
end
|
234
|
+
verify(verifies) if verifies > 0
|
235
|
+
@pid
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def stop
|
240
|
+
kill
|
241
|
+
wait
|
242
|
+
end
|
243
|
+
|
244
|
+
def kill(signal_no = 2)
|
245
|
+
begin
|
246
|
+
@pid && Process.kill(signal_no, @pid) && true
|
247
|
+
rescue Errno::ESRCH
|
248
|
+
false
|
249
|
+
end
|
250
|
+
# cleanup lock if unclean shutdown
|
251
|
+
begin
|
252
|
+
File.delete(File.join(@config[:dbpath], 'mongod.lock')) if @config[:dbpath]
|
253
|
+
rescue Errno::ENOENT
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def wait
|
258
|
+
begin
|
259
|
+
Process.waitpid(@pid) if @pid
|
260
|
+
rescue Errno::ECHILD
|
261
|
+
# JVM might have already reaped the exit status
|
262
|
+
end
|
263
|
+
@pid = nil
|
264
|
+
end
|
265
|
+
|
266
|
+
def running?
|
267
|
+
begin
|
268
|
+
@pid && Process.kill(0, @pid) && true
|
269
|
+
rescue Errno::ESRCH
|
270
|
+
false
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def verify(verifies = DEFAULT_VERIFIES)
|
275
|
+
verifies.times do |i|
|
276
|
+
return @pid if running?
|
277
|
+
sleep 1
|
278
|
+
end
|
279
|
+
nil
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
class Server < SysProc
|
284
|
+
attr_reader :host, :port
|
285
|
+
|
286
|
+
def initialize(cmd = nil, host = nil, port = nil)
|
287
|
+
super(cmd)
|
288
|
+
@host = host
|
289
|
+
@port = port
|
290
|
+
end
|
291
|
+
|
292
|
+
def host_port
|
293
|
+
[@host, @port].join(':')
|
294
|
+
end
|
295
|
+
|
296
|
+
def host_port_a # for old format
|
297
|
+
[@host, @port]
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
class DbServer < Server
|
302
|
+
attr_accessor :config
|
303
|
+
|
304
|
+
def initialize(config)
|
305
|
+
@config = config
|
306
|
+
cmd = init_config!
|
307
|
+
super(cmd, @config[:host], @config[:port])
|
308
|
+
end
|
309
|
+
|
310
|
+
def init_config!
|
311
|
+
dbpath = @config[:dbpath]
|
312
|
+
[dbpath, File.dirname(@config[:logpath])].compact.each{|dir| FileUtils.mkdir_p(dir) unless File.directory?(dir) }
|
313
|
+
command = @config[:command] || 'mongod'
|
314
|
+
params = @config.reject{|k,v| IGNORE_KEYS.include?(k)}
|
315
|
+
arguments = params.sort{|a, b| a[0].to_s <=> b[0].to_s}.collect do |arg, value| # sort block is needed for 1.8.7 which lacks Symbol#<=>
|
316
|
+
argument = '--' + arg.to_s
|
317
|
+
if FLAGS.member?(arg) && value == true
|
318
|
+
[argument]
|
319
|
+
elsif !FLAGS.member?(arg)
|
320
|
+
[argument, value.to_s]
|
321
|
+
end
|
322
|
+
end
|
323
|
+
cmd = [command, arguments].flatten.compact
|
324
|
+
end
|
325
|
+
|
326
|
+
def start(verifies = DEFAULT_VERIFIES)
|
327
|
+
super(verifies)
|
328
|
+
verify(verifies)
|
329
|
+
end
|
330
|
+
|
331
|
+
def verify(verifies = 600)
|
332
|
+
verifies.times do |i|
|
333
|
+
#puts "DbServer.verify via connection probe - port:#{@port.inspect} iteration:#{i} @pid:#{@pid.inspect} kill:#{Process.kill(0, @pid).inspect} running?:#{running?.inspect} cmd:#{cmd.inspect}"
|
334
|
+
begin
|
335
|
+
raise Mongo::ConnectionFailure unless running?
|
336
|
+
Mongo::MongoClient.new(@host, @port).close
|
337
|
+
#puts "DbServer.verified via connection - port: #{@port} iteration: #{i}"
|
338
|
+
return @pid
|
339
|
+
rescue Mongo::ConnectionFailure
|
340
|
+
sleep 1
|
341
|
+
end
|
342
|
+
end
|
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
|
350
|
+
end
|
351
|
+
|
352
|
+
end
|
353
|
+
|
354
|
+
class ClusterManager
|
355
|
+
attr_reader :config
|
356
|
+
def initialize(config)
|
357
|
+
@config = config
|
358
|
+
@servers = {}
|
359
|
+
Mongo::Config::CLUSTER_OPT_KEYS.each do |key|
|
360
|
+
@servers[key] = @config[key].collect{|conf| p conf; DbServer.new(conf)} if @config[key]
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def servers(key = nil)
|
365
|
+
@servers.collect{|k,v| (!key || key == k) ? v : nil}.flatten.compact
|
366
|
+
end
|
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
|
+
|
397
|
+
def command( cmd_servers, db_name, cmd, opts = {} )
|
398
|
+
ret = []
|
399
|
+
cmd = cmd.class == Array ? cmd : [ cmd ]
|
400
|
+
debug 3, "ClusterManager.command cmd:#{cmd.inspect}"
|
401
|
+
cmd_servers = cmd_servers.class == Array ? cmd_servers : [cmd_servers]
|
402
|
+
cmd_servers.each do |cmd_server|
|
403
|
+
debug 3, cmd_server.inspect
|
404
|
+
cmd_server = cmd_server.config if cmd_server.is_a?(DbServer)
|
405
|
+
client = Mongo::MongoClient.new(cmd_server[:host], cmd_server[:port])
|
406
|
+
ensure_authenticated(client)
|
407
|
+
cmd.each do |c|
|
408
|
+
debug 3, "ClusterManager.command c:#{c.inspect}"
|
409
|
+
response = client[db_name].command( c, opts )
|
410
|
+
debug 3, "ClusterManager.command response:#{response.inspect}"
|
411
|
+
raise Mongo::OperationFailure, "c:#{c.inspect} opts:#{opts.inspect} failed" unless response["ok"] == 1.0 || opts.fetch(:check_response, true) == false
|
412
|
+
ret << response
|
413
|
+
end
|
414
|
+
client.close
|
415
|
+
end
|
416
|
+
debug 3, "command ret:#{ret.inspect}"
|
417
|
+
ret.size == 1 ? ret.first : ret
|
418
|
+
end
|
419
|
+
|
420
|
+
def replica_set?
|
421
|
+
!!config[:replicas]
|
422
|
+
end
|
423
|
+
|
424
|
+
def repl_set_get_status
|
425
|
+
command( @config[:replicas], 'admin', { :replSetGetStatus => 1 }, {:check_response => false } )
|
426
|
+
end
|
427
|
+
|
428
|
+
def repl_set_get_config
|
429
|
+
host, port = primary_name.split(":")
|
430
|
+
client = Mongo::MongoClient.new(host, port)
|
431
|
+
ensure_authenticated(client)
|
432
|
+
client['local']['system.replset'].find_one
|
433
|
+
end
|
434
|
+
|
435
|
+
def repl_set_config
|
436
|
+
members = []
|
437
|
+
@config[:replicas].each{|s| members << { :_id => s[:_id], :host => "#{s[:host]}:#{s[:port]}", :tags => { :node => s[:_id].to_s } } }
|
438
|
+
@config[:arbiters].each{|s| members << { :_id => s[:_id], :host => "#{s[:host]}:#{s[:port]}", :arbiterOnly => true } }
|
439
|
+
{
|
440
|
+
:_id => @config[:replicas].first[:replSet],
|
441
|
+
:members => members
|
442
|
+
}
|
443
|
+
end
|
444
|
+
|
445
|
+
def repl_set_initiate( cfg = nil )
|
446
|
+
command( @config[:replicas].first, 'admin', { :replSetInitiate => cfg || repl_set_config } )
|
447
|
+
end
|
448
|
+
|
449
|
+
def repl_set_startup
|
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)
|
456
|
+
healthy = states.all? do |status, is_master|
|
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
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
return healthy if healthy
|
488
|
+
sleep(1)
|
489
|
+
end
|
490
|
+
|
491
|
+
raise Mongo::OperationFailure,
|
492
|
+
"replSet startup failed - status: #{states.inspect}"
|
493
|
+
end
|
494
|
+
|
495
|
+
def repl_set_seeds
|
496
|
+
@config[:replicas].collect{|node| "#{node[:host]}:#{node[:port]}"}
|
497
|
+
end
|
498
|
+
|
499
|
+
def repl_set_seeds_old
|
500
|
+
@config[:replicas].collect{|node| [node[:host], node[:port]]}
|
501
|
+
end
|
502
|
+
|
503
|
+
def repl_set_seeds_uri
|
504
|
+
repl_set_seeds.join(',')
|
505
|
+
end
|
506
|
+
|
507
|
+
def members_uri
|
508
|
+
members = @config[:replicas] || @config[:routers]
|
509
|
+
members.collect{|node| "#{node[:host]}:#{node[:port]}"}.join(',')
|
510
|
+
end
|
511
|
+
|
512
|
+
def repl_set_name
|
513
|
+
@config[:replicas].first[:replSet]
|
514
|
+
end
|
515
|
+
|
516
|
+
def member_names_by_state(state)
|
517
|
+
states = Array(state)
|
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?}
|
520
|
+
status['members'].find_all{|member| states.index(member['state']) }.collect{|member| member['name']}
|
521
|
+
end
|
522
|
+
|
523
|
+
def primary_name
|
524
|
+
member_names_by_state(1).first
|
525
|
+
end
|
526
|
+
|
527
|
+
def secondary_names
|
528
|
+
member_names_by_state(2)
|
529
|
+
end
|
530
|
+
|
531
|
+
def replica_names
|
532
|
+
member_names_by_state([1,2])
|
533
|
+
end
|
534
|
+
|
535
|
+
def arbiter_names
|
536
|
+
member_names_by_state(7)
|
537
|
+
end
|
538
|
+
|
539
|
+
def members_by_name(names)
|
540
|
+
names.collect do |name|
|
541
|
+
member_by_name(name)
|
542
|
+
end.compact
|
543
|
+
end
|
544
|
+
|
545
|
+
def member_by_name(name)
|
546
|
+
servers.find{|server| server.host_port == name}
|
547
|
+
end
|
548
|
+
|
549
|
+
def primary
|
550
|
+
members_by_name([primary_name]).first
|
551
|
+
end
|
552
|
+
|
553
|
+
def secondaries
|
554
|
+
members_by_name(secondary_names)
|
555
|
+
end
|
556
|
+
|
557
|
+
def stop_primary
|
558
|
+
primary.stop
|
559
|
+
end
|
560
|
+
|
561
|
+
def stop_secondary
|
562
|
+
secondaries[rand(secondaries.length)].stop
|
563
|
+
end
|
564
|
+
|
565
|
+
def replicas
|
566
|
+
members_by_name(replica_names)
|
567
|
+
end
|
568
|
+
|
569
|
+
def arbiters
|
570
|
+
members_by_name(arbiter_names)
|
571
|
+
end
|
572
|
+
|
573
|
+
def config_names_by_kind(kind)
|
574
|
+
@config[kind].collect{|conf| "#{conf[:host]}:#{conf[:port]}"}
|
575
|
+
end
|
576
|
+
|
577
|
+
def shards
|
578
|
+
members_by_name(config_names_by_kind(:shards))
|
579
|
+
end
|
580
|
+
|
581
|
+
def repl_set_reconfig(new_config)
|
582
|
+
new_config['version'] = repl_set_get_config['version'] + 1
|
583
|
+
command( primary, 'admin', { :replSetReconfig => new_config } )
|
584
|
+
repl_set_startup
|
585
|
+
end
|
586
|
+
|
587
|
+
def repl_set_remove_node(state = [1,2])
|
588
|
+
names = member_names_by_state(state)
|
589
|
+
name = names[rand(names.length)]
|
590
|
+
|
591
|
+
@config[:replicas].delete_if{|node| "#{node[:host]}:#{node[:port]}" == name}
|
592
|
+
repl_set_reconfig(repl_set_config)
|
593
|
+
end
|
594
|
+
|
595
|
+
def repl_set_add_node
|
596
|
+
end
|
597
|
+
|
598
|
+
def configs
|
599
|
+
members_by_name(config_names_by_kind(:configs))
|
600
|
+
end
|
601
|
+
|
602
|
+
def routers
|
603
|
+
members_by_name(config_names_by_kind(:routers))
|
604
|
+
end
|
605
|
+
|
606
|
+
def mongos_seeds
|
607
|
+
config_names_by_kind(:routers)
|
608
|
+
end
|
609
|
+
|
610
|
+
def ismaster(servers)
|
611
|
+
command( servers, 'admin', { :ismaster => 1 } )
|
612
|
+
end
|
613
|
+
|
614
|
+
def sharded_cluster_is_master
|
615
|
+
ismaster(@config[:routers])
|
616
|
+
end
|
617
|
+
|
618
|
+
def repl_set_is_master
|
619
|
+
ismaster(@config[:replicas])
|
620
|
+
end
|
621
|
+
|
622
|
+
def addshards(shards = @config[:shards])
|
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
|
632
|
+
end
|
633
|
+
|
634
|
+
def listshards
|
635
|
+
command( @config[:routers].first, 'admin', { :listshards => 1 } )
|
636
|
+
end
|
637
|
+
|
638
|
+
def enablesharding( dbname )
|
639
|
+
command( @config[:routers].first, 'admin', { :enablesharding => dbname } )
|
640
|
+
end
|
641
|
+
|
642
|
+
def shardcollection( namespace, key, unique = false )
|
643
|
+
command( @config[:routers].first, 'admin', { :shardcollection => namespace, :key => key, :unique => unique } )
|
644
|
+
end
|
645
|
+
|
646
|
+
def mongos_discover # can also do @config[:routers] find but only want mongos for connections
|
647
|
+
(@config[:configs]).collect do |cmd_server|
|
648
|
+
client = Mongo::MongoClient.new(cmd_server[:host], cmd_server[:port])
|
649
|
+
result = client['config']['mongos'].find.to_a
|
650
|
+
client.close
|
651
|
+
result
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
def start
|
656
|
+
# Must start configs before mongos -- hash order not guaranteed on 1.8.X
|
657
|
+
servers(:configs).each{|server| server.start}
|
658
|
+
servers.each{|server| server.start}
|
659
|
+
# TODO - sharded replica sets - pending
|
660
|
+
if @config[:replicas]
|
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)
|
663
|
+
repl_set_startup
|
664
|
+
end
|
665
|
+
if @config[:routers]
|
666
|
+
addshards if listshards['shards'].size == 0
|
667
|
+
end
|
668
|
+
self
|
669
|
+
end
|
670
|
+
alias :restart :start
|
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
|
+
|
694
|
+
def stop
|
695
|
+
start
|
696
|
+
delete_users
|
697
|
+
servers.each{|server| server.stop}
|
698
|
+
self
|
699
|
+
end
|
700
|
+
|
701
|
+
def clobber
|
702
|
+
FileUtils.rm_rf @config[:dbpath]
|
703
|
+
self
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
end
|
708
|
+
end
|