mongo 2.7.2 → 2.8.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
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