mongo 2.11.0 → 2.11.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 (95) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  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/retryable.rb +33 -8
  31. data/lib/mongo/server.rb +13 -6
  32. data/lib/mongo/server/connection.rb +15 -8
  33. data/lib/mongo/server/connection_base.rb +7 -4
  34. data/lib/mongo/server/description.rb +34 -21
  35. data/lib/mongo/server/monitor.rb +1 -1
  36. data/lib/mongo/server/monitor/connection.rb +2 -3
  37. data/lib/mongo/session.rb +10 -10
  38. data/lib/mongo/socket.rb +10 -1
  39. data/lib/mongo/uri.rb +1 -1
  40. data/lib/mongo/version.rb +1 -1
  41. data/mongo.gemspec +1 -1
  42. data/spec/README.md +13 -0
  43. data/spec/integration/auth_spec.rb +27 -8
  44. data/spec/integration/bson_symbol_spec.rb +34 -0
  45. data/spec/integration/client_construction_spec.rb +14 -0
  46. data/spec/integration/client_options_spec.rb +5 -5
  47. data/spec/integration/connection_spec.rb +57 -9
  48. data/spec/integration/crud_spec.rb +45 -0
  49. data/spec/integration/cursor_reaping_spec.rb +2 -1
  50. data/spec/integration/grid_fs_bucket_spec.rb +48 -0
  51. data/spec/integration/retryable_errors_spec.rb +204 -39
  52. data/spec/integration/retryable_writes_spec.rb +36 -36
  53. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +98 -0
  54. data/spec/lite_spec_helper.rb +1 -0
  55. data/spec/mongo/address_spec.rb +19 -13
  56. data/spec/mongo/auth/ldap/conversation_spec.rb +1 -1
  57. data/spec/mongo/auth/scram/conversation_spec.rb +25 -14
  58. data/spec/mongo/auth/user/view_spec.rb +36 -1
  59. data/spec/mongo/auth/user_spec.rb +12 -0
  60. data/spec/mongo/auth/x509/conversation_spec.rb +1 -1
  61. data/spec/mongo/bulk_write_spec.rb +2 -2
  62. data/spec/mongo/client_construction_spec.rb +1 -21
  63. data/spec/mongo/cluster_spec.rb +57 -0
  64. data/spec/mongo/collection/view/map_reduce_spec.rb +1 -1
  65. data/spec/mongo/collection_spec.rb +26 -2
  66. data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +56 -0
  67. data/spec/mongo/server/connection_spec.rb +76 -8
  68. data/spec/mongo/server/monitor/connection_spec.rb +14 -7
  69. data/spec/mongo/socket/ssl_spec.rb +132 -98
  70. data/spec/mongo/socket/tcp_spec.rb +1 -9
  71. data/spec/mongo/uri_spec.rb +1 -1
  72. data/spec/runners/sdam/verifier.rb +91 -0
  73. data/spec/spec_tests/data/sdam/rs/primary_address_change.yml +29 -0
  74. data/spec/spec_tests/data/sdam/rs/primary_mismatched_me.yml +27 -23
  75. data/spec/spec_tests/data/sdam/rs/primary_to_no_primary_mismatched_me.yml +56 -79
  76. data/spec/spec_tests/data/sdam/sharded/primary_address_change.yml +21 -0
  77. data/spec/spec_tests/data/sdam/sharded/primary_mismatched_me.yml +22 -0
  78. data/spec/spec_tests/data/sdam/single/primary_address_change.yml +24 -0
  79. data/spec/spec_tests/data/sdam/single/primary_mismatched_me.yml +25 -0
  80. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_me_mismatch.yml +159 -0
  81. data/spec/spec_tests/data/sdam_monitoring/{replica_set_other_seed.yml → replica_set_with_primary_change.yml} +97 -101
  82. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_primary_removal.yml +22 -18
  83. data/spec/spec_tests/data/sdam_monitoring/standalone_to_rs_with_me_mismatch.yml +90 -0
  84. data/spec/spec_tests/sdam_monitoring_spec.rb +9 -4
  85. data/spec/support/cluster_config.rb +36 -0
  86. data/spec/support/cluster_tools.rb +5 -3
  87. data/spec/support/command_monitoring.rb +1 -1
  88. data/spec/support/constraints.rb +18 -18
  89. data/spec/support/lite_constraints.rb +8 -0
  90. data/spec/support/sdam_monitoring.rb +0 -115
  91. data/spec/support/server_discovery_and_monitoring.rb +2 -0
  92. data/spec/support/spec_config.rb +1 -1
  93. data/spec/support/utils.rb +11 -1
  94. metadata +687 -659
  95. metadata.gz.sig +3 -2
@@ -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
@@ -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
@@ -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
 
@@ -575,7 +575,7 @@ describe Mongo::Collection::View::MapReduce do
575
575
  expect(map_reduce.send(:secondary_ok?)).to be false
576
576
  end
577
577
 
578
- context 'when the server is not a valid for writing' do
578
+ context 'when the server is not valid for writing' do
579
579
  clean_slate
580
580
 
581
581
  before do
@@ -278,9 +278,9 @@ describe Mongo::Collection do
278
278
  require_topology :replica_set
279
279
 
280
280
  let(:client_options) do
281
- {
281
+ SpecConfig.instance.auth_options.merge(
282
282
  read: { mode: :primary_preferred },
283
- }
283
+ )
284
284
  end
285
285
 
286
286
  let(:subscriber) { EventSubscriber.new }
@@ -945,6 +945,30 @@ describe Mongo::Collection do
945
945
  it_behaves_like 'a failed operation using a session'
946
946
  end
947
947
  end
948
+
949
+ context 'when collation has a strength' do
950
+ min_server_fcv '3.4'
951
+
952
+ let(:band_collection) do
953
+ described_class.new(database, :bands)
954
+ end
955
+
956
+ before do
957
+ band_collection.delete_many
958
+ band_collection.insert_many([{ name: "Depeche Mode" }, { name: "New Order" }])
959
+ end
960
+
961
+ let(:options) do
962
+ { collation: { locale: 'en_US', strength: 2 } }
963
+ end
964
+ let(:band_result) do
965
+ band_collection.find({ name: 'DEPECHE MODE' }, options)
966
+ end
967
+
968
+ it 'finds Capitalize from UPPER CASE' do
969
+ expect(band_result.count_documents).to eq(1)
970
+ end
971
+ end
948
972
  end
949
973
 
950
974
  describe '#drop' do
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongo::Cursor::Builder::OpKillCursors do
4
+
5
+ let(:reply) do
6
+ Mongo::Protocol::Reply.allocate.tap do |reply|
7
+ allow(reply).to receive(:cursor_id).and_return(8000)
8
+ end
9
+ end
10
+
11
+ let(:result) do
12
+ Mongo::Operation::Result.new(reply)
13
+ end
14
+
15
+ let(:view) do
16
+ Mongo::Collection::View.new(
17
+ authorized_collection,
18
+ {},
19
+ tailable: true,
20
+ max_time_ms: 100
21
+ )
22
+ end
23
+
24
+ let(:cursor) do
25
+ Mongo::Cursor.new(view, result, authorized_primary)
26
+ end
27
+
28
+ let(:builder) do
29
+ described_class.new(cursor)
30
+ end
31
+
32
+ describe '#specification' do
33
+
34
+ let(:specification) do
35
+ builder.specification
36
+ end
37
+
38
+ it 'includes the cursor ids' do
39
+ expect(specification[:cursor_ids]).to eq([BSON::Int64.new(8000)])
40
+ end
41
+
42
+ it 'includes the database name' do
43
+ expect(specification[:db_name]).to eq(SpecConfig.instance.test_db)
44
+ end
45
+
46
+ it 'includes the collection name' do
47
+ expect(specification[:coll_name]).to eq(TEST_COLL)
48
+ end
49
+ end
50
+
51
+ describe '.get_cursors_list' do
52
+ it 'returns integer cursor ids' do
53
+ expect(described_class.get_cursors_list(builder.specification)).to eq([8000])
54
+ end
55
+ end
56
+ end
@@ -137,12 +137,13 @@ describe Mongo::Server::Connection, retry: 3 do
137
137
 
138
138
  it 'logs a warning' do
139
139
  messages = []
140
- # Straightforward expectations are not working here for some reason
141
140
  expect(Mongo::Logger.logger).to receive(:warn) do |msg|
142
141
  messages << msg
143
142
  end
143
+
144
144
  expect(error).not_to be nil
145
- expect(messages).to include(expected_message)
145
+
146
+ messages.any? { |msg| msg.include?(expected_message) }.should be true
146
147
  end
147
148
 
148
149
  end
@@ -419,6 +420,73 @@ describe Mongo::Server::Connection, retry: 3 do
419
420
  end
420
421
  end
421
422
 
423
+ context 'connecting to arbiter' do
424
+ require_topology :replica_set
425
+
426
+ before(:all) do
427
+ unless ENV['HAVE_ARBITER']
428
+ skip 'Test requires an arbiter in the deployment'
429
+ end
430
+ end
431
+
432
+ let(:arbiter_server) do
433
+ authorized_client.cluster.servers_list.each do |server|
434
+ server.scan!
435
+ end
436
+ server = authorized_client.cluster.servers_list.detect do |server|
437
+ server.arbiter?
438
+ end.tap do |server|
439
+ raise 'No arbiter in the deployment' unless server
440
+ end
441
+ end
442
+
443
+ shared_examples_for 'does not authenticate' do
444
+ let(:client) do
445
+ new_local_client([address],
446
+ SpecConfig.instance.test_options.merge(
447
+ :user => 'bogus',
448
+ :password => 'bogus',
449
+ :database => 'bogus'
450
+ ).merge(connect: :direct),
451
+ )
452
+ end
453
+
454
+ let(:connection) do
455
+ described_class.new(
456
+ server,
457
+ )
458
+ end
459
+
460
+ let(:ping) do
461
+ client.database.command(ping: 1)
462
+ end
463
+
464
+ it 'does not authenticate' do
465
+ ClientRegistry.instance.close_all_clients
466
+
467
+ expect_any_instance_of(Mongo::Server::Connection).not_to receive(:authenticate!)
468
+
469
+ expect(ping.documents.first['ok']).to eq(1) rescue nil
470
+ end
471
+ end
472
+
473
+ context 'without me mismatch' do
474
+ let(:address) do
475
+ arbiter_server.address.to_s
476
+ end
477
+
478
+ it_behaves_like 'does not authenticate'
479
+ end
480
+
481
+ context 'with me mismatch' do
482
+ let(:address) do
483
+ "#{ClusterConfig.instance.alternate_address.host}:#{arbiter_server.address.port}"
484
+ end
485
+
486
+ it_behaves_like 'does not authenticate'
487
+ end
488
+ end
489
+
422
490
  end
423
491
 
424
492
  describe '#disconnect!' do
@@ -1012,7 +1080,7 @@ describe Mongo::Server::Connection, retry: 3 do
1012
1080
  end
1013
1081
 
1014
1082
  it 'uses the connect_timeout for the address' do
1015
- expect(connection.address.send(:connect_timeout)).to eq(3)
1083
+ expect(connection.address.options[:connect_timeout]).to eq(3)
1016
1084
  end
1017
1085
 
1018
1086
  it 'uses the socket_timeout as the socket_timeout' do
@@ -1031,7 +1099,7 @@ describe Mongo::Server::Connection, retry: 3 do
1031
1099
  end
1032
1100
 
1033
1101
  it 'uses the connect_timeout for the address' do
1034
- expect(connection.address.send(:connect_timeout)).to eq(3)
1102
+ expect(connection.address.options[:connect_timeout]).to eq(3)
1035
1103
  end
1036
1104
 
1037
1105
  it 'does not use a socket_timeout' do
@@ -1052,8 +1120,8 @@ describe Mongo::Server::Connection, retry: 3 do
1052
1120
  connection.connect!
1053
1121
  end
1054
1122
 
1055
- it 'uses the default connect_timeout for the address' do
1056
- expect(connection.address.send(:connect_timeout)).to eq(10)
1123
+ it 'does not specify connect_timeout for the address' do
1124
+ expect(connection.address.options[:connect_timeout]).to be nil
1057
1125
  end
1058
1126
 
1059
1127
  it 'uses the socket_timeout' do
@@ -1071,8 +1139,8 @@ describe Mongo::Server::Connection, retry: 3 do
1071
1139
  connection.connect!
1072
1140
  end
1073
1141
 
1074
- it 'uses the default connect_timeout for the address' do
1075
- expect(connection.address.send(:connect_timeout)).to eq(10)
1142
+ it 'does not specify connect_timeout for the address' do
1143
+ expect(connection.address.options[:connect_timeout]).to be nil
1076
1144
  end
1077
1145
 
1078
1146
  it 'does not use a socket_timeout' do
@@ -66,7 +66,7 @@ describe Mongo::Server::Monitor::Connection do
66
66
  end
67
67
 
68
68
  it 'uses the connect_timeout for the address' do
69
- expect(connection.address.send(:connect_timeout)).to eq(3)
69
+ expect(connection.address.options[:connect_timeout]).to eq(3)
70
70
  end
71
71
 
72
72
  it 'uses the connect_timeout as the socket_timeout' do
@@ -81,7 +81,7 @@ describe Mongo::Server::Monitor::Connection do
81
81
  end
82
82
 
83
83
  it 'uses the connect_timeout for the address' do
84
- expect(connection.address.send(:connect_timeout)).to eq(3)
84
+ expect(connection.address.options[:connect_timeout]).to eq(3)
85
85
  end
86
86
 
87
87
  it 'uses the connect_timeout as the socket_timeout' do
@@ -98,8 +98,8 @@ describe Mongo::Server::Monitor::Connection do
98
98
  SpecConfig.instance.test_options.merge(connect_timeout: nil, socket_timeout: 5)
99
99
  end
100
100
 
101
- it 'uses the default connect_timeout for the address' do
102
- expect(connection.address.send(:connect_timeout)).to eq(10)
101
+ it 'does not specify connect_timeout for the address' do
102
+ expect(connection.address.options[:connect_timeout]).to be nil
103
103
  end
104
104
 
105
105
  it 'uses the connect_timeout as the socket_timeout' do
@@ -113,8 +113,8 @@ describe Mongo::Server::Monitor::Connection do
113
113
  SpecConfig.instance.test_options.merge(connect_timeout: nil, socket_timeout: nil)
114
114
  end
115
115
 
116
- it 'uses the default connect_timeout for the address' do
117
- expect(connection.address.send(:connect_timeout)).to eq(10)
116
+ it 'does not specify connect_timeout for the address' do
117
+ expect(connection.address.options[:connect_timeout]).to be nil
118
118
  end
119
119
 
120
120
  it 'uses the connect_timeout as the socket_timeout' do
@@ -189,10 +189,17 @@ describe Mongo::Server::Monitor::Connection do
189
189
 
190
190
  it 'logs a warning' do
191
191
  expect_any_instance_of(Mongo::Socket).to receive(:write).and_raise(Mongo::Error::SocketError, 'test error')
192
- expect(Mongo::Logger.logger).to receive(:warn).with(expected_message).and_call_original
192
+
193
+ messages = []
194
+ expect(Mongo::Logger.logger).to receive(:warn) do |msg|
195
+ messages << msg
196
+ end
197
+
193
198
  expect do
194
199
  monitor.connection.connect!
195
200
  end.to raise_error(Mongo::Error::SocketError, 'test error')
201
+
202
+ messages.any? { |msg| msg.include?(expected_message) }.should be true
196
203
  end
197
204
  end
198
205
  end