mongo 2.7.2 → 2.8.0.rc0

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 (94) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -3
  4. data/lib/mongo/address.rb +17 -20
  5. data/lib/mongo/address/ipv4.rb +6 -3
  6. data/lib/mongo/address/ipv6.rb +6 -3
  7. data/lib/mongo/address/unix.rb +5 -2
  8. data/lib/mongo/auth.rb +15 -2
  9. data/lib/mongo/auth/cr/conversation.rb +4 -2
  10. data/lib/mongo/auth/ldap/conversation.rb +4 -2
  11. data/lib/mongo/auth/scram.rb +3 -7
  12. data/lib/mongo/auth/scram/conversation.rb +28 -19
  13. data/lib/mongo/auth/user.rb +45 -10
  14. data/lib/mongo/auth/x509/conversation.rb +4 -2
  15. data/lib/mongo/cluster.rb +9 -17
  16. data/lib/mongo/error.rb +2 -0
  17. data/lib/mongo/error/missing_password.rb +29 -0
  18. data/lib/mongo/error/operation_failure.rb +7 -3
  19. data/lib/mongo/error/parser.rb +2 -1
  20. data/lib/mongo/error/sdam_error_detection.rb +54 -0
  21. data/lib/mongo/operation/aggregate/command.rb +1 -16
  22. data/lib/mongo/operation/aggregate/op_msg.rb +1 -1
  23. data/lib/mongo/operation/collections_info.rb +2 -3
  24. data/lib/mongo/operation/delete/command.rb +2 -15
  25. data/lib/mongo/operation/delete/legacy.rb +1 -16
  26. data/lib/mongo/operation/explain/command.rb +1 -16
  27. data/lib/mongo/operation/explain/legacy.rb +1 -16
  28. data/lib/mongo/operation/find/command.rb +1 -16
  29. data/lib/mongo/operation/find/legacy.rb +1 -16
  30. data/lib/mongo/operation/get_more/command.rb +1 -16
  31. data/lib/mongo/operation/indexes/command.rb +1 -16
  32. data/lib/mongo/operation/indexes/legacy.rb +4 -16
  33. data/lib/mongo/operation/list_collections/command.rb +1 -16
  34. data/lib/mongo/operation/map_reduce/command.rb +1 -16
  35. data/lib/mongo/operation/parallel_scan/command.rb +1 -16
  36. data/lib/mongo/operation/result.rb +3 -0
  37. data/lib/mongo/operation/shared/executable.rb +4 -0
  38. data/lib/mongo/operation/shared/polymorphic_lookup.rb +1 -1
  39. data/lib/mongo/operation/shared/polymorphic_result.rb +8 -1
  40. data/lib/mongo/operation/shared/result/aggregatable.rb +0 -5
  41. data/lib/mongo/operation/update/command.rb +2 -15
  42. data/lib/mongo/operation/update/legacy.rb +1 -16
  43. data/lib/mongo/operation/users_info/command.rb +1 -16
  44. data/lib/mongo/retryable.rb +22 -10
  45. data/lib/mongo/server.rb +10 -1
  46. data/lib/mongo/server/app_metadata.rb +7 -2
  47. data/lib/mongo/server/connectable.rb +0 -6
  48. data/lib/mongo/server/connection.rb +86 -135
  49. data/lib/mongo/server/connection_base.rb +133 -0
  50. data/lib/mongo/server/connection_pool.rb +11 -24
  51. data/lib/mongo/server/connection_pool/queue.rb +41 -41
  52. data/lib/mongo/server/description.rb +1 -1
  53. data/lib/mongo/server/monitor.rb +4 -4
  54. data/lib/mongo/server/monitor/connection.rb +26 -7
  55. data/lib/mongo/server/pending_connection.rb +36 -0
  56. data/lib/mongo/server_selector/selectable.rb +9 -1
  57. data/lib/mongo/session.rb +0 -1
  58. data/lib/mongo/socket.rb +23 -6
  59. data/lib/mongo/socket/ssl.rb +11 -18
  60. data/lib/mongo/socket/tcp.rb +13 -14
  61. data/lib/mongo/socket/unix.rb +9 -27
  62. data/lib/mongo/uri.rb +1 -1
  63. data/lib/mongo/version.rb +1 -1
  64. data/spec/integration/auth_spec.rb +160 -0
  65. data/spec/integration/retryable_writes_spec.rb +55 -58
  66. data/spec/integration/sdam_error_handling_spec.rb +115 -0
  67. data/spec/mongo/address/ipv4_spec.rb +4 -0
  68. data/spec/mongo/address/ipv6_spec.rb +4 -0
  69. data/spec/mongo/auth/scram/conversation_spec.rb +6 -5
  70. data/spec/mongo/auth/scram/negotiation_spec.rb +25 -36
  71. data/spec/mongo/auth/scram_spec.rb +2 -2
  72. data/spec/mongo/auth/user_spec.rb +97 -0
  73. data/spec/mongo/client_construction_spec.rb +1 -1
  74. data/spec/mongo/error/operation_failure_spec.rb +125 -1
  75. data/spec/mongo/retryable_spec.rb +17 -8
  76. data/spec/mongo/server/connection_pool/queue_spec.rb +24 -10
  77. data/spec/mongo/server/connection_pool_spec.rb +30 -117
  78. data/spec/mongo/server/connection_spec.rb +147 -25
  79. data/spec/mongo/server/description_spec.rb +0 -14
  80. data/spec/mongo/server/monitor/connection_spec.rb +22 -0
  81. data/spec/mongo/server_selector_spec.rb +1 -0
  82. data/spec/mongo/server_spec.rb +6 -6
  83. data/spec/mongo/socket/ssl_spec.rb +48 -116
  84. data/spec/mongo/socket/tcp_spec.rb +22 -0
  85. data/spec/mongo/socket/unix_spec.rb +9 -9
  86. data/spec/mongo/socket_spec.rb +15 -3
  87. data/spec/spec_tests/server_selection_spec.rb +2 -0
  88. data/spec/support/client_registry.rb +8 -2
  89. data/spec/support/common_shortcuts.rb +20 -1
  90. data/spec/support/constraints.rb +10 -2
  91. data/spec/support/lite_constraints.rb +8 -0
  92. data/spec/support/spec_config.rb +9 -1
  93. metadata +14 -4
  94. metadata.gz.sig +0 -0
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'SDAM error handling' do
4
+ before(:all) do
5
+ ClientRegistry.instance.close_all_clients
6
+ end
7
+
8
+ describe 'when there is an error during an operation' do
9
+ let(:client) { authorized_client }
10
+
11
+ before do
12
+ wait_for_all_servers(client.cluster)
13
+ # we also need a connection to the primary so that our error
14
+ # expectations do not get triggered during handshakes which
15
+ # have different behavior from non-handshake errors
16
+ client.database.command(ping: 1)
17
+ client.cluster.servers_list.each do |server|
18
+ server.monitor.stop!(true)
19
+ end
20
+ end
21
+
22
+ let(:server) { client.cluster.next_primary }
23
+
24
+ let(:operation) do
25
+ expect_any_instance_of(Mongo::Server::Connection).to receive(:deliver).and_return(reply)
26
+ expect do
27
+ client.database.command(ping: 1)
28
+ end.to raise_error(Mongo::Error::OperationFailure, exception_message)
29
+ end
30
+
31
+ shared_examples_for 'marks server unknown' do
32
+ it 'marks server unknown' do
33
+ expect(server).not_to be_unknown
34
+ operation
35
+ expect(server).to be_unknown
36
+ end
37
+ end
38
+
39
+ shared_examples_for 'does not mark server unknown' do
40
+ it 'does not mark server unknown' do
41
+ expect(server).not_to be_unknown
42
+ operation
43
+ expect(server).not_to be_unknown
44
+ end
45
+ end
46
+
47
+ shared_examples_for 'requests server scan' do
48
+ it 'requests server scan' do
49
+ expect(server.monitor.scan_semaphore).to receive(:signal)
50
+ operation
51
+ end
52
+ end
53
+
54
+ shared_examples_for 'does not request server scan' do
55
+ it 'does not request server scan' do
56
+ expect(server.monitor.scan_semaphore).not_to receive(:signal)
57
+ operation
58
+ end
59
+ end
60
+
61
+ context 'not master error' do
62
+ let(:exception_message) do
63
+ /not master/
64
+ end
65
+
66
+ let(:reply) do
67
+ make_not_master_reply
68
+ end
69
+
70
+ it_behaves_like 'marks server unknown'
71
+ it_behaves_like 'requests server scan'
72
+ end
73
+
74
+ context 'node is recovering error' do
75
+ let(:exception_message) do
76
+ /shutdown in progress/
77
+ end
78
+
79
+ let(:reply) do
80
+ make_node_recovering_reply
81
+ end
82
+
83
+ it_behaves_like 'marks server unknown'
84
+ it_behaves_like 'requests server scan'
85
+ end
86
+
87
+ context 'network error' do
88
+
89
+ let(:operation) do
90
+ expect_any_instance_of(Mongo::Socket).to receive(:read).and_raise(exception)
91
+ expect do
92
+ client.database.command(ping: 1)
93
+ end.to raise_error(exception)
94
+ end
95
+
96
+ context 'non-timeout network error' do
97
+ let(:exception) do
98
+ Mongo::Error::SocketError
99
+ end
100
+
101
+ it_behaves_like 'marks server unknown'
102
+ it_behaves_like 'does not request server scan'
103
+ end
104
+
105
+ context 'network timeout error' do
106
+ let(:exception) do
107
+ Mongo::Error::SocketTimeoutError
108
+ end
109
+
110
+ it_behaves_like 'does not mark server unknown'
111
+ it_behaves_like 'does not request server scan'
112
+ end
113
+ end
114
+ end
115
+ end
@@ -69,10 +69,12 @@ describe Mongo::Address::IPv4 do
69
69
  end
70
70
 
71
71
  it 'returns an ssl socket' do
72
+ allow_any_instance_of(Mongo::Socket::SSL).to receive(:connect!)
72
73
  expect(socket).to be_a(Mongo::Socket::SSL)
73
74
  end
74
75
 
75
76
  it 'sets the family as ipv4' do
77
+ allow_any_instance_of(Mongo::Socket::SSL).to receive(:connect!)
76
78
  expect(socket.family).to eq(Socket::PF_INET)
77
79
  end
78
80
  end
@@ -84,10 +86,12 @@ describe Mongo::Address::IPv4 do
84
86
  end
85
87
 
86
88
  it 'returns a tcp socket' do
89
+ allow_any_instance_of(Mongo::Socket::TCP).to receive(:connect!)
87
90
  expect(socket).to be_a(Mongo::Socket::TCP)
88
91
  end
89
92
 
90
93
  it 'sets the family a ipv4' do
94
+ allow_any_instance_of(Mongo::Socket::TCP).to receive(:connect!)
91
95
  expect(socket.family).to eq(Socket::PF_INET)
92
96
  end
93
97
  end
@@ -97,10 +97,12 @@ describe Mongo::Address::IPv6 do
97
97
  end
98
98
 
99
99
  it 'returns an ssl socket' do
100
+ allow_any_instance_of(Mongo::Socket::SSL).to receive(:connect!)
100
101
  expect(socket).to be_a(Mongo::Socket::SSL)
101
102
  end
102
103
 
103
104
  it 'sets the family as ipv6' do
105
+ allow_any_instance_of(Mongo::Socket::SSL).to receive(:connect!)
104
106
  expect(socket.family).to eq(Socket::PF_INET6)
105
107
  end
106
108
  end
@@ -112,10 +114,12 @@ describe Mongo::Address::IPv6 do
112
114
  end
113
115
 
114
116
  it 'returns a tcp socket' do
117
+ allow_any_instance_of(Mongo::Socket::TCP).to receive(:connect!)
115
118
  expect(socket).to be_a(Mongo::Socket::TCP)
116
119
  end
117
120
 
118
121
  it 'sets the family a ipv6' do
122
+ allow_any_instance_of(Mongo::Socket::TCP).to receive(:connect!)
119
123
  expect(socket.family).to eq(Socket::PF_INET6)
120
124
  end
121
125
  end
@@ -15,13 +15,14 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
15
15
  database: Mongo::Database::ADMIN,
16
16
  user: 'user',
17
17
  password: 'pencil',
18
- # We specify SCRAM-SHA-1 so that we don't accidentally use SCRAM-SHA-256 on newer server versions.
19
- auth_mech: 'SCRAM-SHA-1'
18
+ # We specify SCRAM-SHA-1 so that we don't accidentally use
19
+ # SCRAM-SHA-256 on newer server versions.
20
+ auth_mech: :scram,
20
21
  )
21
22
  end
22
23
 
23
24
  let(:mechanism) do
24
- 'SCRAM-SHA-1'
25
+ :scram
25
26
  end
26
27
 
27
28
  describe '#start' do
@@ -216,12 +217,12 @@ describe Mongo::Auth::SCRAM::Conversation, retry: 3 do
216
217
  database: Mongo::Database::ADMIN,
217
218
  user: 'user',
218
219
  password: 'pencil',
219
- auth_mech: 'SCRAM-SHA-256'
220
+ auth_mech: :scram256,
220
221
  )
221
222
  end
222
223
 
223
224
  let(:mechanism) do
224
- 'SCRAM-SHA-256'
225
+ :scram256
225
226
  end
226
227
 
227
228
  describe '#start' do
@@ -22,7 +22,7 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
22
22
  createUser: user.name,
23
23
  pwd: password,
24
24
  roles: ['root'],
25
- mechanisms: auth_mechanisms
25
+ mechanisms: server_user_auth_mechanisms,
26
26
  )
27
27
  client.close
28
28
  end
@@ -57,7 +57,7 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
57
57
 
58
58
  context 'when the user only can use SCRAM-SHA-1 to authenticate' do
59
59
 
60
- let(:auth_mechanisms) do
60
+ let(:server_user_auth_mechanisms) do
61
61
  ['SCRAM-SHA-1']
62
62
  end
63
63
 
@@ -111,7 +111,7 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
111
111
 
112
112
  context 'when the user only can use SCRAM-SHA-256 to authenticate' do
113
113
 
114
- let(:auth_mechanisms) do
114
+ let(:server_user_auth_mechanisms) do
115
115
  ['SCRAM-SHA-256']
116
116
  end
117
117
 
@@ -165,7 +165,7 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
165
165
 
166
166
  context 'when the user only can use either SCRAM-SHA-1 or SCRAM-SHA-256 to authenticate' do
167
167
 
168
- let(:auth_mechanisms) do
168
+ let(:server_user_auth_mechanisms) do
169
169
  ['SCRAM-SHA-1', 'SCRAM-SHA-256']
170
170
  end
171
171
 
@@ -200,13 +200,11 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
200
200
  create_user!
201
201
 
202
202
  mechanism = nil
203
- # Negotiation currently happens on monitoring sockets,
204
- # hence may be invoked more than once here
205
- expect(Mongo::Auth).to receive(:get).at_least(:once).and_wrap_original do |m, *args|
203
+ expect(Mongo::Auth).to receive(:get).and_wrap_original do |m, user|
206
204
  # copy mechanism here rather than whole user
207
205
  # in case something mutates mechanism later
208
- mechanism = args.first.mechanism
209
- m.call(*args)
206
+ mechanism = user.mechanism
207
+ m.call(user)
210
208
  end
211
209
 
212
210
  expect { result }.not_to raise_error
@@ -224,15 +222,11 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
224
222
  create_user!
225
223
 
226
224
  mechanism = nil
227
- # Negotiation currently happens on monitoring sockets,
228
- # hence may be invoked more than once here
229
- expect(Mongo::Auth).to receive(:get).at_least(:once).and_wrap_original do |m, *args|
230
- if args.first.name == 'both'
231
- # copy mechanism here rather than whole user
232
- # in case something mutates mechanism later
233
- mechanism = args.first.mechanism
234
- m.call(*args)
235
- end
225
+ expect(Mongo::Auth).to receive(:get).and_wrap_original do |m, user|
226
+ # copy mechanism here rather than whole user
227
+ # in case something mutates mechanism later
228
+ mechanism = user.mechanism
229
+ m.call(user)
236
230
  end
237
231
 
238
232
  expect { result }.not_to raise_error
@@ -266,7 +260,7 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
266
260
  nil
267
261
  end
268
262
 
269
- let(:auth_mechanisms) do
263
+ let(:server_user_auth_mechanisms) do
270
264
  ['SCRAM-SHA-256']
271
265
  end
272
266
 
@@ -345,7 +339,7 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
345
339
 
346
340
  context 'when the user only can use SCRAM-SHA-1 to authenticate' do
347
341
 
348
- let(:auth_mechanisms) do
342
+ let(:server_user_auth_mechanisms) do
349
343
  ['SCRAM-SHA-1']
350
344
  end
351
345
 
@@ -398,7 +392,7 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
398
392
 
399
393
  context 'when the user only can use SCRAM-SHA-256 to authenticate' do
400
394
 
401
- let(:auth_mechanisms) do
395
+ let(:server_user_auth_mechanisms) do
402
396
  ['SCRAM-SHA-256']
403
397
  end
404
398
 
@@ -452,7 +446,7 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
452
446
 
453
447
  context 'when the user only can use either SCRAM-SHA-1 or SCRAM-SHA-256 to authenticate' do
454
448
 
455
- let(:auth_mechanisms) do
449
+ let(:server_user_auth_mechanisms) do
456
450
  ['SCRAM-SHA-1', 'SCRAM-SHA-256']
457
451
  end
458
452
 
@@ -485,15 +479,14 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
485
479
 
486
480
  it 'authenticates successfully' do
487
481
  create_user!
482
+ expect(user.mechanism).to eq(:scram)
488
483
 
489
484
  mechanism = nil
490
- # Negotiation currently happens on monitoring sockets,
491
- # hence may be invoked more than once here
492
- expect(Mongo::Auth).to receive(:get).at_least(:once).and_wrap_original do |m, *args|
485
+ expect(Mongo::Auth).to receive(:get).and_wrap_original do |m, user|
493
486
  # copy mechanism here rather than whole user
494
487
  # in case something mutates mechanism later
495
- mechanism = args.first.mechanism
496
- m.call(*args)
488
+ mechanism = user.mechanism
489
+ m.call(user)
497
490
  end
498
491
 
499
492
  expect { result }.not_to raise_error
@@ -511,15 +504,11 @@ describe 'SCRAM-SHA auth mechanism negotiation' do
511
504
  create_user!
512
505
 
513
506
  mechanism = nil
514
- # Negotiation currently happens on monitoring sockets,
515
- # hence may be invoked more than once here
516
- expect(Mongo::Auth).to receive(:get).at_least(:once).and_wrap_original do |m, *args|
517
- if args.first.name == 'both'
518
- # copy mechanism here rather than whole user
519
- # in case something mutates mechanism later
520
- mechanism = args.first.mechanism
521
- m.call(*args)
522
- end
507
+ expect(Mongo::Auth).to receive(:get).and_wrap_original do |m, user|
508
+ # copy mechanism here rather than whole user
509
+ # in case something mutates mechanism later
510
+ mechanism = user.mechanism
511
+ m.call(user)
523
512
  end
524
513
 
525
514
  expect { result }.not_to raise_error
@@ -50,7 +50,7 @@ describe Mongo::Auth::SCRAM do
50
50
  database: 'driver',
51
51
  user: 'notauser',
52
52
  password: 'password',
53
- auth_mech: 'SCRAM-SHA-1'
53
+ auth_mech: :scram,
54
54
  )
55
55
  end
56
56
 
@@ -118,7 +118,7 @@ describe Mongo::Auth::SCRAM do
118
118
  database: 'driver',
119
119
  user: 'notauser',
120
120
  password: 'password',
121
- auth_mech: 'SCRAM-SHA-256'
121
+ auth_mech: :scram256,
122
122
  )
123
123
  end
124
124
 
@@ -10,6 +10,68 @@ describe Mongo::Auth::User do
10
10
  described_class.new(options)
11
11
  end
12
12
 
13
+ describe '#initialize' do
14
+ let(:user) { Mongo::Auth::User.new(options) }
15
+
16
+ context 'no options' do
17
+ let(:options) { {} }
18
+
19
+ it 'succeeds' do
20
+ expect(user).to be_a(Mongo::Auth::User)
21
+ end
22
+ end
23
+
24
+ context 'invalid mechanism' do
25
+ let(:options) { {auth_mech: :invalid} }
26
+
27
+ it 'raises ArgumentError' do
28
+ expect do
29
+ user
30
+ end.to raise_error(Mongo::Auth::InvalidMechanism, ":invalid is invalid, please use one of the following mechanisms: :mongodb_cr, :mongodb_x509, :plain, :scram, :scram256")
31
+ end
32
+ end
33
+
34
+ context 'mechanism given as string' do
35
+ let(:options) { {auth_mech: 'scram'} }
36
+
37
+ context 'not linting' do
38
+ skip_if_linting
39
+
40
+ it 'warns' do
41
+ expect(Mongo::Logger.logger).to receive(:warn)
42
+ user
43
+ end
44
+
45
+ it 'converts mechanism to symbol' do
46
+ expect(user.mechanism).to eq(:scram)
47
+ end
48
+ end
49
+
50
+ context 'linting' do
51
+ require_linting
52
+
53
+ it 'raises LintError' do
54
+ expect do
55
+ user
56
+ end.to raise_error(Mongo::Error::LintError, "Auth mechanism \"scram\" must be specified as a symbol")
57
+ end
58
+ end
59
+ end
60
+
61
+ context 'mechanism given as symbol' do
62
+ let(:options) { {auth_mech: :scram} }
63
+
64
+ it 'does not warn' do
65
+ expect(Mongo::Logger.logger).not_to receive(:warn)
66
+ user
67
+ end
68
+
69
+ it 'stores mechanism' do
70
+ expect(user.mechanism).to eq(:scram)
71
+ end
72
+ end
73
+ end
74
+
13
75
  describe '#auth_key' do
14
76
 
15
77
  let(:nonce) do
@@ -93,6 +155,41 @@ describe Mongo::Auth::User do
93
155
  it 'returns the hashed password' do
94
156
  expect(user.hashed_password).to eq(expected)
95
157
  end
158
+
159
+ context 'password not given' do
160
+ let(:options) { {user: 'foo'} }
161
+
162
+ it 'raises MissingPassword' do
163
+ expect do
164
+ user.hashed_password
165
+ end.to raise_error(Mongo::Error::MissingPassword)
166
+ end
167
+ end
168
+ end
169
+
170
+ describe '#sasl_prepped_password' do
171
+
172
+ let(:expected) do
173
+ 'pass'
174
+ end
175
+
176
+ it 'returns the clear text password' do
177
+ expect(user.send(:sasl_prepped_password)).to eq(expected)
178
+ end
179
+
180
+ it 'returns the password encoded in utf-8' do
181
+ expect(user.sasl_prepped_password.encoding.name).to eq('UTF-8')
182
+ end
183
+
184
+ context 'password not given' do
185
+ let(:options) { {user: 'foo'} }
186
+
187
+ it 'raises MissingPassword' do
188
+ expect do
189
+ user.sasl_prepped_password
190
+ end.to raise_error(Mongo::Error::MissingPassword)
191
+ end
192
+ end
96
193
  end
97
194
 
98
195
  describe '#mechanism' do