mongo 2.11.1 → 2.11.6

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 (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