mongo 2.11.1 → 2.11.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -1
  3. data.tar.gz.sig +2 -1
  4. data/Rakefile +24 -0
  5. data/lib/mongo/address.rb +53 -37
  6. data/lib/mongo/auth.rb +30 -10
  7. data/lib/mongo/auth/cr.rb +1 -0
  8. data/lib/mongo/auth/cr/conversation.rb +13 -13
  9. data/lib/mongo/auth/ldap.rb +2 -1
  10. data/lib/mongo/auth/ldap/conversation.rb +9 -12
  11. data/lib/mongo/auth/scram.rb +1 -0
  12. data/lib/mongo/auth/scram/conversation.rb +36 -27
  13. data/lib/mongo/auth/user.rb +7 -1
  14. data/lib/mongo/auth/x509.rb +2 -1
  15. data/lib/mongo/auth/x509/conversation.rb +9 -9
  16. data/lib/mongo/bulk_write/transformable.rb +3 -3
  17. data/lib/mongo/client.rb +17 -6
  18. data/lib/mongo/cluster.rb +67 -49
  19. data/lib/mongo/cluster/sdam_flow.rb +87 -3
  20. data/lib/mongo/collection/view/readable.rb +3 -1
  21. data/lib/mongo/collection/view/writable.rb +3 -3
  22. data/lib/mongo/cursor/builder/kill_cursors_command.rb +8 -1
  23. data/lib/mongo/cursor/builder/op_kill_cursors.rb +8 -1
  24. data/lib/mongo/database.rb +1 -1
  25. data/lib/mongo/grid/file.rb +5 -0
  26. data/lib/mongo/grid/file/chunk.rb +2 -0
  27. data/lib/mongo/grid/fs_bucket.rb +15 -13
  28. data/lib/mongo/grid/stream/write.rb +9 -3
  29. data/lib/mongo/protocol/serializers.rb +12 -2
  30. data/lib/mongo/server.rb +13 -6
  31. data/lib/mongo/server/connection.rb +15 -8
  32. data/lib/mongo/server/connection_base.rb +7 -4
  33. data/lib/mongo/server/description.rb +34 -21
  34. data/lib/mongo/server/monitor.rb +1 -1
  35. data/lib/mongo/server/monitor/connection.rb +2 -3
  36. data/lib/mongo/session.rb +10 -10
  37. data/lib/mongo/socket.rb +10 -1
  38. data/lib/mongo/uri.rb +1 -1
  39. data/lib/mongo/version.rb +1 -1
  40. data/mongo.gemspec +1 -1
  41. data/spec/README.md +13 -0
  42. data/spec/integration/auth_spec.rb +27 -8
  43. data/spec/integration/bson_symbol_spec.rb +34 -0
  44. data/spec/integration/client_construction_spec.rb +14 -0
  45. data/spec/integration/client_options_spec.rb +5 -5
  46. data/spec/integration/connection_spec.rb +57 -9
  47. data/spec/integration/crud_spec.rb +45 -0
  48. data/spec/integration/cursor_reaping_spec.rb +2 -1
  49. data/spec/integration/grid_fs_bucket_spec.rb +48 -0
  50. data/spec/integration/retryable_errors_spec.rb +2 -2
  51. data/spec/integration/zlib_compression_spec.rb +25 -0
  52. data/spec/lite_spec_helper.rb +1 -0
  53. data/spec/mongo/address_spec.rb +19 -13
  54. data/spec/mongo/auth/ldap/conversation_spec.rb +1 -1
  55. data/spec/mongo/auth/scram/conversation_spec.rb +25 -14
  56. data/spec/mongo/auth/user/view_spec.rb +39 -7
  57. data/spec/mongo/auth/user_spec.rb +12 -0
  58. data/spec/mongo/auth/x509/conversation_spec.rb +1 -1
  59. data/spec/mongo/bulk_write_spec.rb +2 -2
  60. data/spec/mongo/client_construction_spec.rb +3 -22
  61. data/spec/mongo/cluster_spec.rb +57 -0
  62. data/spec/mongo/collection/view/map_reduce_spec.rb +1 -1
  63. data/spec/mongo/collection_spec.rb +26 -2
  64. data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +56 -0
  65. data/spec/mongo/server/connection_spec.rb +76 -8
  66. data/spec/mongo/server/monitor/connection_spec.rb +14 -7
  67. data/spec/mongo/socket/ssl_spec.rb +132 -98
  68. data/spec/mongo/socket/tcp_spec.rb +1 -9
  69. data/spec/mongo/uri_spec.rb +1 -1
  70. data/spec/runners/sdam/verifier.rb +6 -3
  71. data/spec/spec_tests/data/sdam/rs/primary_address_change.yml +29 -0
  72. data/spec/spec_tests/data/sdam/rs/primary_mismatched_me.yml +27 -23
  73. data/spec/spec_tests/data/sdam/rs/primary_to_no_primary_mismatched_me.yml +56 -79
  74. data/spec/spec_tests/data/sdam/sharded/primary_address_change.yml +21 -0
  75. data/spec/spec_tests/data/sdam/sharded/primary_mismatched_me.yml +22 -0
  76. data/spec/spec_tests/data/sdam/single/primary_address_change.yml +24 -0
  77. data/spec/spec_tests/data/sdam/single/primary_mismatched_me.yml +25 -0
  78. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_me_mismatch.yml +159 -0
  79. data/spec/spec_tests/data/sdam_monitoring/{replica_set_other_seed.yml → replica_set_with_primary_change.yml} +97 -101
  80. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_primary_removal.yml +22 -18
  81. data/spec/spec_tests/data/sdam_monitoring/standalone_to_rs_with_me_mismatch.yml +90 -0
  82. data/spec/support/cluster_config.rb +36 -0
  83. data/spec/support/cluster_tools.rb +5 -3
  84. data/spec/support/command_monitoring.rb +1 -1
  85. data/spec/support/constraints.rb +18 -18
  86. data/spec/support/lite_constraints.rb +8 -0
  87. data/spec/support/server_discovery_and_monitoring.rb +2 -0
  88. data/spec/support/spec_config.rb +3 -3
  89. data/spec/support/utils.rb +11 -1
  90. metadata +661 -637
  91. metadata.gz.sig +0 -0
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'GridFS bucket integration' do
4
+ let(:fs) do
5
+ authorized_client.database.fs
6
+ end
7
+
8
+ describe 'UTF-8 string write' do
9
+ let(:data) { "hello\u2210" }
10
+
11
+ before do
12
+ data.length.should_not == data.bytesize
13
+ end
14
+
15
+ shared_examples 'round-trips' do
16
+ it 'round-trips' do
17
+ stream = fs.open_upload_stream('test') do |stream|
18
+ stream.write(data_to_write)
19
+ end
20
+
21
+ actual = nil
22
+ fs.open_download_stream(stream.file_id) do |stream|
23
+ actual = stream.read
24
+ end
25
+
26
+ actual.encoding.name.should == 'ASCII-8BIT'
27
+ actual.should == data.dup.force_encoding('binary')
28
+ end
29
+ end
30
+
31
+ context 'in binary encoding' do
32
+ let(:data_to_write) do
33
+ data.force_encoding('binary').freeze
34
+ end
35
+
36
+ it_behaves_like 'round-trips'
37
+ end
38
+
39
+ context 'in UTF-8 encoding' do
40
+ let(:data_to_write) do
41
+ data.encoding.name.should == 'UTF-8'
42
+ data.freeze
43
+ end
44
+
45
+ it_behaves_like 'round-trips'
46
+ end
47
+ end
48
+ end
@@ -24,7 +24,7 @@ describe 'Failing retryable operations' do
24
24
  end
25
25
 
26
26
  after do
27
- ClusterTools.instance.direct_client_for_each_server do |client|
27
+ ClusterTools.instance.direct_client_for_each_data_bearing_server do |client|
28
28
  client.use(:admin).database.command(clear_fail_point_command)
29
29
  end
30
30
  end
@@ -62,7 +62,7 @@ describe 'Failing retryable operations' do
62
62
  server.monitor.stop!
63
63
  end
64
64
 
65
- ClusterTools.instance.direct_client_for_each_server do |client|
65
+ ClusterTools.instance.direct_client_for_each_data_bearing_server do |client|
66
66
  client.use(:admin).database.command(fail_point_command)
67
67
  end
68
68
  end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Zlib compression' do
4
+ require_compression
5
+
6
+ before do
7
+ authorized_client['test'].drop
8
+ end
9
+
10
+ context 'when client has zlib compressor option enabled' do
11
+ it 'compresses the message to the server' do
12
+ # Double check that the client has zlib compression enabled
13
+ expect(authorized_client.options[:compressors]).to include('zlib')
14
+
15
+ expect(Mongo::Protocol::Compressed).to receive(:new).twice.and_call_original
16
+ expect(Zlib::Deflate).to receive(:deflate).twice.and_call_original
17
+ expect(Zlib::Inflate).to receive(:inflate).twice.and_call_original
18
+
19
+ authorized_client['test'].insert_one(_id: 1, text: 'hello world')
20
+ document = authorized_client['test'].find(_id: 1).first
21
+
22
+ expect(document['text']).to eq('hello world')
23
+ end
24
+ end
25
+ end
@@ -127,6 +127,7 @@ RSpec.configure do |config|
127
127
  end
128
128
 
129
129
  config.expect_with :rspec do |c|
130
+ c.syntax = [:should, :expect]
130
131
  c.max_formatted_output_length = 10000
131
132
  end
132
133
  end
@@ -249,11 +249,7 @@ describe Mongo::Address do
249
249
  end
250
250
  end
251
251
 
252
- context 'when creating a socket using the resolver' do
253
-
254
- before do
255
- address.send(:create_resolver, SpecConfig.instance.ssl_options)
256
- end
252
+ context 'when creating a socket' do
257
253
 
258
254
  it 'uses the host, not the IP address' do
259
255
  expect(address.socket(0.0).host).to eq(socket_address_or_host)
@@ -285,6 +281,24 @@ describe Mongo::Address do
285
281
  end
286
282
  end
287
283
  end
284
+
285
+ describe ':connect_timeout option' do
286
+ clean_slate
287
+
288
+ let(:address) { Mongo::Address.new('127.0.0.1') }
289
+
290
+ it 'defaults to 10' do
291
+ RSpec::Mocks.with_temporary_scope do
292
+ resolved_address = double('address')
293
+ # This test's expectation
294
+ expect(resolved_address).to receive(:socket).with(0, {}, connect_timeout: 10)
295
+
296
+ expect(Mongo::Address::IPv4).to receive(:new).and_return(resolved_address)
297
+
298
+ address.socket(0)
299
+ end
300
+ end
301
+ end
288
302
  end
289
303
 
290
304
  describe '#to_s' do
@@ -320,12 +334,4 @@ describe Mongo::Address do
320
334
  end
321
335
  end
322
336
  end
323
-
324
- describe '#connect_timeout' do
325
- let(:address) { Mongo::Address.new('127.0.0.1') }
326
-
327
- it 'defaults to 10' do
328
- expect(address.send(:connect_timeout)).to eq(10)
329
- end
330
- end
331
337
  end
@@ -17,7 +17,7 @@ describe Mongo::Auth::LDAP::Conversation do
17
17
  describe '#start' do
18
18
 
19
19
  let(:query) do
20
- conversation.start
20
+ conversation.start(nil)
21
21
  end
22
22
 
23
23
  let(:selector) do
@@ -7,6 +7,15 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
7
7
  described_class.new(user, mechanism)
8
8
  end
9
9
 
10
+ let(:connection) do
11
+ double('connection').tap do |connection|
12
+ features = double('features')
13
+ allow(features).to receive(:op_msg_enabled?)
14
+ allow(connection).to receive(:features).and_return(features)
15
+ allow(connection).to receive(:server)
16
+ end
17
+ end
18
+
10
19
  context 'when SCRAM-SHA-1 is used' do
11
20
  min_server_fcv '3.0'
12
21
 
@@ -26,9 +35,11 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
26
35
  end
27
36
 
28
37
  describe '#start' do
38
+ # Test uses global assertions
39
+ clean_slate
29
40
 
30
41
  let(:query) do
31
- conversation.start
42
+ conversation.start(nil)
32
43
  end
33
44
 
34
45
  before do
@@ -85,7 +96,7 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
85
96
  end
86
97
 
87
98
  let(:query) do
88
- conversation.continue(reply)
99
+ conversation.continue(reply, connection)
89
100
  end
90
101
 
91
102
  let(:selector) do
@@ -117,7 +128,7 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
117
128
 
118
129
  it 'raises an error' do
119
130
  expect {
120
- conversation.continue(reply)
131
+ conversation.continue(reply, connection)
121
132
  }.to raise_error(Mongo::Error::InvalidNonce)
122
133
  end
123
134
  end
@@ -170,8 +181,8 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
170
181
  end
171
182
 
172
183
  let(:query) do
173
- conversation.continue(continue_reply)
174
- conversation.finalize(reply)
184
+ conversation.continue(continue_reply, connection)
185
+ conversation.finalize(reply, connection)
175
186
  end
176
187
 
177
188
  let(:selector) do
@@ -199,8 +210,8 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
199
210
 
200
211
  it 'raises an error' do
201
212
  expect {
202
- conversation.continue(continue_reply)
203
- conversation.finalize(reply)
213
+ conversation.continue(continue_reply, connection)
214
+ conversation.finalize(reply, connection)
204
215
  }.to raise_error(Mongo::Error::InvalidSignature)
205
216
  end
206
217
  end
@@ -226,7 +237,7 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
226
237
  describe '#start' do
227
238
 
228
239
  let(:query) do
229
- conversation.start
240
+ conversation.start(nil)
230
241
  end
231
242
 
232
243
  before do
@@ -283,7 +294,7 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
283
294
  end
284
295
 
285
296
  let(:query) do
286
- conversation.continue(reply)
297
+ conversation.continue(reply, connection)
287
298
  end
288
299
 
289
300
  let(:selector) do
@@ -315,7 +326,7 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
315
326
 
316
327
  it 'raises an error' do
317
328
  expect {
318
- conversation.continue(reply)
329
+ conversation.continue(reply, connection)
319
330
  }.to raise_error(Mongo::Error::InvalidNonce)
320
331
  end
321
332
  end
@@ -368,8 +379,8 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
368
379
  end
369
380
 
370
381
  let(:query) do
371
- conversation.continue(continue_reply)
372
- conversation.finalize(reply)
382
+ conversation.continue(continue_reply, connection)
383
+ conversation.finalize(reply, connection)
373
384
  end
374
385
 
375
386
  let(:selector) do
@@ -397,8 +408,8 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
397
408
 
398
409
  it 'raises an error' do
399
410
  expect {
400
- conversation.continue(continue_reply)
401
- conversation.finalize(reply)
411
+ conversation.continue(continue_reply, connection)
412
+ conversation.finalize(reply, connection)
402
413
  }.to raise_error(Mongo::Error::InvalidSignature)
403
414
  end
404
415
  end
@@ -2,8 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  describe Mongo::Auth::User::View do
4
4
 
5
+ let(:database) { root_authorized_client.database }
6
+
5
7
  let(:view) do
6
- described_class.new(root_authorized_client.database)
8
+ described_class.new(database)
7
9
  end
8
10
 
9
11
  before do
@@ -51,6 +53,39 @@ describe Mongo::Auth::User::View do
51
53
 
52
54
  describe '#create' do
53
55
 
56
+ context 'when password is not provided' do
57
+
58
+ let(:database) { root_authorized_client.use('$external').database }
59
+
60
+ let(:username) { 'passwordless-user' }
61
+
62
+ let(:response) do
63
+ view.create(
64
+ username,
65
+ # https://stackoverflow.com/questions/55939832/mongodb-external-database-cannot-create-new-user-with-user-defined-role
66
+ roles: [{role: 'read', db: 'admin'}],
67
+ )
68
+ end
69
+
70
+ before do
71
+ begin
72
+ view.remove(username)
73
+ rescue Mongo::Error::OperationFailure
74
+ # can be user not found, ignore
75
+ end
76
+ end
77
+
78
+ it 'creates the user' do
79
+ view.info(username).should == []
80
+
81
+ lambda do
82
+ response
83
+ end.should_not raise_error
84
+
85
+ view.info(username).first['user'].should == username
86
+ end
87
+ end
88
+
54
89
  context 'when a session is not used' do
55
90
 
56
91
  let!(:response) do
@@ -74,8 +109,7 @@ describe Mongo::Auth::User::View do
74
109
  min_server_fcv '3.6'
75
110
 
76
111
  it 'does not compress the message' do
77
- # The dropUser command message will be compressed, so expect instantiation once.
78
- expect(Mongo::Protocol::Compressed).to receive(:new).once.and_call_original
112
+ expect(Mongo::Protocol::Compressed).not_to receive(:new)
79
113
  expect(response).to be_successful
80
114
  end
81
115
  end
@@ -162,8 +196,7 @@ describe Mongo::Auth::User::View do
162
196
  min_server_fcv '3.6'
163
197
 
164
198
  it 'does not compress the message' do
165
- # The dropUser command message will be compressed, so expect instantiation once.
166
- expect(Mongo::Protocol::Compressed).to receive(:new).once.and_call_original
199
+ expect(Mongo::Protocol::Compressed).not_to receive(:new)
167
200
  expect(response).to be_successful
168
201
  end
169
202
  end
@@ -212,8 +245,7 @@ describe Mongo::Auth::User::View do
212
245
  min_server_fcv '3.6'
213
246
 
214
247
  it 'does not compress the message' do
215
- # The dropUser command message will be compressed, so expect instantiation once.
216
- expect(Mongo::Protocol::Compressed).to receive(:new).once.and_call_original
248
+ expect(Mongo::Protocol::Compressed).not_to receive(:new)
217
249
  expect(response).to be_successful
218
250
  end
219
251
  end
@@ -325,4 +325,16 @@ describe Mongo::Auth::User do
325
325
  end
326
326
  end
327
327
  end
328
+
329
+ describe '#spec' do
330
+ context 'when no password and no roles are set' do
331
+ let(:user) do
332
+ described_class.new(user: 'foo')
333
+ end
334
+
335
+ it 'is a hash with empty roles' do
336
+ user.spec.should == {roles: []}
337
+ end
338
+ end
339
+ end
328
340
  end
@@ -16,7 +16,7 @@ describe Mongo::Auth::X509::Conversation do
16
16
  describe '#start' do
17
17
 
18
18
  let(:query) do
19
- conversation.start
19
+ conversation.start(nil)
20
20
  end
21
21
 
22
22
  let(:selector) do
@@ -1924,11 +1924,11 @@ describe Mongo::BulkWrite do
1924
1924
  end
1925
1925
 
1926
1926
  let(:first_txn_number) do
1927
- started_events[-2].command['txnNumber'].value
1927
+ Utils.int64_value(started_events[-2].command['txnNumber'])
1928
1928
  end
1929
1929
 
1930
1930
  let(:second_txn_number) do
1931
- started_events[-1].command['txnNumber'].value
1931
+ Utils.int64_value(started_events[-1].command['txnNumber'])
1932
1932
  end
1933
1933
 
1934
1934
  it 'inserts the documents' do
@@ -94,27 +94,7 @@ describe Mongo::Client do
94
94
  # cluster during SDAM
95
95
  context 'me mismatch on the only initial seed' do
96
96
  let(:address) do
97
- address = SpecConfig.instance.addresses.first
98
- port = address.sub(/^.*:/, '').to_i
99
- address = address.sub(/:.*/, '')
100
- case address
101
- when '127.0.0.1'
102
- 'localhost'
103
- when /^(\d+\.){3}\d+$/
104
- skip 'This test requires a hostname or 127.0.0.1 as address'
105
- else
106
- # We don't know if mongod is listening on ipv4 or ipv6,
107
- # in principle.
108
- # Our tests use ipv4, so hardcode that for now.
109
- # To support both we need to try both addresses
110
- # which will make this test more complicated.
111
- resolved_address = Addrinfo.getaddrinfo(address, port, Socket::PF_INET).first.ip_address
112
- if resolved_address.include?(':')
113
- "[#{resolved_address}]"
114
- else
115
- resolved_address
116
- end + ":#{port}"
117
- end
97
+ ClusterConfig.instance.alternate_address.to_s
118
98
  end
119
99
 
120
100
  let(:logger) do
@@ -255,7 +235,8 @@ describe Mongo::Client do
255
235
  min_server_fcv '3.6'
256
236
 
257
237
  it 'uses compression for messages' do
258
- expect(Mongo::Protocol::Compressed).to receive(:new).and_call_original
238
+ # A Compressed object will be created for both the request and the response
239
+ expect(Mongo::Protocol::Compressed).to receive(:new).twice.and_call_original
259
240
  client[TEST_COLL].find({}, limit: 1).first
260
241
  end
261
242
  end
@@ -20,6 +20,25 @@ describe Mongo::Cluster do
20
20
 
21
21
  let(:cluster) { cluster_without_io }
22
22
 
23
+ describe 'initialize' do
24
+
25
+ context 'when there are duplicate addresses' do
26
+
27
+ let(:addresses) do
28
+ SpecConfig.instance.addresses + SpecConfig.instance.addresses
29
+ end
30
+ let(:cluster_with_dup_addresses) do
31
+ register_cluster(
32
+ described_class.new(addresses, monitoring, SpecConfig.instance.test_options))
33
+ end
34
+
35
+ it 'does not raise an exception' do
36
+ expect { cluster_with_dup_addresses }.not_to raise_error
37
+ end
38
+ end
39
+
40
+ end
41
+
23
42
  describe '#==' do
24
43
 
25
44
  context 'when the other is a cluster' do
@@ -575,6 +594,44 @@ describe Mongo::Cluster do
575
594
  end
576
595
 
577
596
  describe '#sessions_supported?' do
597
+ context 'when client has not contacted any servers' do
598
+
599
+ let(:cluster) do
600
+ described_class.new(SpecConfig.instance.addresses, monitoring,
601
+ SpecConfig.instance.test_options.merge(
602
+ monitoring_io: false, server_selection_timeout: 0.183))
603
+ end
604
+
605
+ it 'is false' do
606
+ expect(cluster.send(:sessions_supported?)).to be false
607
+ end
608
+ end
609
+
610
+ context 'when client has contacted servers and then disconnected' do
611
+ min_server_fcv '3.6'
612
+ require_wired_tiger
613
+ require_topology :sharded, :replica_set
614
+
615
+ let(:cluster) do
616
+ described_class.new(SpecConfig.instance.addresses, monitoring,
617
+ SpecConfig.instance.test_options.merge(
618
+ server_selection_timeout: 0.183)
619
+ ).tap do |cluster|
620
+ register_cluster(cluster)
621
+ end
622
+ end
623
+
624
+ before do
625
+ cluster.next_primary
626
+ cluster.servers_list.map(&:disconnect!)
627
+ cluster.servers_list.map(&:unknown!)
628
+ end
629
+
630
+ it 'is true' do
631
+ expect(cluster.send(:sessions_supported?)).to be true
632
+ end
633
+ end
634
+
578
635
  context 'in server < 3.6' do
579
636
  max_server_version '3.4'
580
637