mongo 2.13.0.beta1 → 2.13.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -5
  4. data/Rakefile +15 -9
  5. data/lib/mongo.rb +4 -2
  6. data/lib/mongo/auth/aws/request.rb +4 -2
  7. data/lib/mongo/bulk_write.rb +1 -0
  8. data/lib/mongo/client.rb +143 -21
  9. data/lib/mongo/cluster.rb +53 -17
  10. data/lib/mongo/cluster/sdam_flow.rb +13 -10
  11. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +3 -2
  12. data/lib/mongo/cluster/topology/sharded.rb +1 -1
  13. data/lib/mongo/cluster/topology/single.rb +1 -1
  14. data/lib/mongo/collection.rb +17 -13
  15. data/lib/mongo/collection/view/readable.rb +3 -1
  16. data/lib/mongo/collection/view/writable.rb +41 -5
  17. data/lib/mongo/database.rb +31 -4
  18. data/lib/mongo/database/view.rb +19 -4
  19. data/lib/mongo/distinguishing_semaphore.rb +55 -0
  20. data/lib/mongo/error.rb +1 -0
  21. data/lib/mongo/error/invalid_session.rb +2 -1
  22. data/lib/mongo/error/operation_failure.rb +6 -0
  23. data/lib/mongo/error/sessions_not_supported.rb +35 -0
  24. data/lib/mongo/event/base.rb +6 -0
  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/monitoring.rb +38 -0
  30. data/lib/mongo/monitoring/command_log_subscriber.rb +10 -2
  31. data/lib/mongo/monitoring/event/command_failed.rb +11 -0
  32. data/lib/mongo/monitoring/event/command_started.rb +37 -2
  33. data/lib/mongo/monitoring/event/command_succeeded.rb +11 -0
  34. data/lib/mongo/monitoring/event/server_closed.rb +1 -1
  35. data/lib/mongo/monitoring/event/server_description_changed.rb +27 -4
  36. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +9 -2
  37. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +9 -2
  38. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +9 -2
  39. data/lib/mongo/monitoring/event/server_opening.rb +1 -1
  40. data/lib/mongo/monitoring/event/topology_changed.rb +1 -1
  41. data/lib/mongo/monitoring/event/topology_closed.rb +1 -1
  42. data/lib/mongo/monitoring/event/topology_opening.rb +1 -1
  43. data/lib/mongo/monitoring/publishable.rb +6 -3
  44. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +9 -1
  45. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
  46. data/lib/mongo/protocol/message.rb +36 -8
  47. data/lib/mongo/protocol/msg.rb +14 -0
  48. data/lib/mongo/protocol/serializers.rb +5 -2
  49. data/lib/mongo/server.rb +10 -3
  50. data/lib/mongo/server/connection.rb +4 -4
  51. data/lib/mongo/server/connection_base.rb +3 -1
  52. data/lib/mongo/server/description.rb +5 -0
  53. data/lib/mongo/server/monitor.rb +76 -44
  54. data/lib/mongo/server/monitor/connection.rb +55 -7
  55. data/lib/mongo/server/pending_connection.rb +14 -4
  56. data/lib/mongo/server/push_monitor.rb +173 -0
  57. data/{spec/runners/transactions/context.rb → lib/mongo/server/push_monitor/connection.rb} +9 -14
  58. data/lib/mongo/server_selector.rb +0 -1
  59. data/lib/mongo/server_selector/base.rb +579 -1
  60. data/lib/mongo/server_selector/nearest.rb +1 -6
  61. data/lib/mongo/server_selector/primary.rb +1 -6
  62. data/lib/mongo/server_selector/primary_preferred.rb +7 -10
  63. data/lib/mongo/server_selector/secondary.rb +1 -6
  64. data/lib/mongo/server_selector/secondary_preferred.rb +1 -7
  65. data/lib/mongo/session.rb +2 -0
  66. data/lib/mongo/socket.rb +20 -8
  67. data/lib/mongo/socket/ssl.rb +1 -1
  68. data/lib/mongo/socket/tcp.rb +1 -1
  69. data/lib/mongo/topology_version.rb +9 -0
  70. data/lib/mongo/utils.rb +62 -0
  71. data/lib/mongo/version.rb +1 -1
  72. data/spec/README.aws-auth.md +2 -2
  73. data/spec/integration/awaited_ismaster_spec.rb +28 -0
  74. data/spec/integration/change_stream_examples_spec.rb +6 -2
  75. data/spec/integration/check_clean_slate_spec.rb +16 -0
  76. data/spec/integration/client_construction_spec.rb +1 -0
  77. data/spec/integration/connect_single_rs_name_spec.rb +5 -2
  78. data/spec/integration/connection_spec.rb +7 -4
  79. data/spec/integration/crud_spec.rb +4 -4
  80. data/spec/integration/docs_examples_spec.rb +6 -0
  81. data/spec/integration/grid_fs_bucket_spec.rb +48 -0
  82. data/spec/integration/heartbeat_events_spec.rb +4 -23
  83. data/spec/integration/read_concern_spec.rb +1 -1
  84. data/spec/integration/retryable_errors_spec.rb +1 -1
  85. data/spec/integration/retryable_writes/shared/performs_legacy_retries.rb +2 -2
  86. data/spec/integration/retryable_writes/shared/performs_modern_retries.rb +3 -3
  87. data/spec/integration/retryable_writes/shared/performs_no_retries.rb +2 -2
  88. data/spec/integration/sdam_error_handling_spec.rb +37 -15
  89. data/spec/integration/sdam_events_spec.rb +77 -6
  90. data/spec/integration/sdam_prose_spec.rb +64 -0
  91. data/spec/integration/server_monitor_spec.rb +25 -1
  92. data/spec/integration/size_limit_spec.rb +7 -3
  93. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +98 -0
  94. data/spec/integration/ssl_uri_options_spec.rb +2 -2
  95. data/spec/integration/zlib_compression_spec.rb +25 -0
  96. data/spec/lite_spec_helper.rb +12 -5
  97. data/spec/mongo/auth/aws/request_spec.rb +76 -0
  98. data/spec/mongo/auth/scram_spec.rb +1 -1
  99. data/spec/mongo/client_construction_spec.rb +207 -0
  100. data/spec/mongo/client_spec.rb +38 -3
  101. data/spec/mongo/cluster/topology/replica_set_spec.rb +52 -9
  102. data/spec/mongo/cluster/topology/single_spec.rb +4 -2
  103. data/spec/mongo/cluster_spec.rb +34 -35
  104. data/spec/mongo/collection/view/change_stream_resume_spec.rb +6 -6
  105. data/spec/mongo/collection_spec.rb +500 -0
  106. data/spec/mongo/database_spec.rb +245 -8
  107. data/spec/mongo/distinguishing_semaphore_spec.rb +63 -0
  108. data/spec/mongo/error/operation_failure_spec.rb +40 -0
  109. data/spec/mongo/index/view_spec.rb +2 -2
  110. data/spec/mongo/monitoring/event/server_description_changed_spec.rb +1 -4
  111. data/spec/mongo/protocol/msg_spec.rb +10 -0
  112. data/spec/mongo/semaphore_spec.rb +51 -0
  113. data/spec/mongo/server/connection_auth_spec.rb +2 -2
  114. data/spec/mongo/server_selector/nearest_spec.rb +23 -23
  115. data/spec/mongo/server_selector/primary_preferred_spec.rb +26 -26
  116. data/spec/mongo/server_selector/primary_spec.rb +9 -9
  117. data/spec/mongo/server_selector/secondary_preferred_spec.rb +22 -22
  118. data/spec/mongo/server_selector/secondary_spec.rb +18 -18
  119. data/spec/mongo/server_selector_spec.rb +4 -4
  120. data/spec/mongo/session_spec.rb +35 -0
  121. data/spec/runners/change_streams/test.rb +2 -2
  122. data/spec/runners/cmap.rb +1 -1
  123. data/spec/runners/command_monitoring.rb +3 -34
  124. data/spec/runners/crud/context.rb +9 -5
  125. data/spec/runners/crud/operation.rb +59 -27
  126. data/spec/runners/crud/spec.rb +0 -8
  127. data/spec/runners/crud/test.rb +1 -1
  128. data/spec/runners/sdam.rb +2 -2
  129. data/spec/runners/server_selection.rb +242 -28
  130. data/spec/runners/transactions.rb +12 -12
  131. data/spec/runners/transactions/operation.rb +151 -25
  132. data/spec/runners/transactions/test.rb +60 -16
  133. data/spec/spec_tests/command_monitoring_spec.rb +22 -12
  134. data/spec/spec_tests/crud_spec.rb +1 -1
  135. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +4 -8
  136. data/spec/spec_tests/data/change_streams/change-streams-resume-whitelist.yml +66 -0
  137. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml +15 -0
  138. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml +4 -3
  139. data/spec/spec_tests/data/max_staleness/Unknown/SmallMaxStaleness.yml +1 -0
  140. data/spec/spec_tests/data/sdam_integration/cancel-server-check.yml +96 -0
  141. data/spec/spec_tests/data/sdam_integration/connectTimeoutMS.yml +88 -0
  142. data/spec/spec_tests/data/sdam_integration/find-network-error.yml +83 -0
  143. data/spec/spec_tests/data/sdam_integration/find-shutdown-error.yml +116 -0
  144. data/spec/spec_tests/data/sdam_integration/insert-network-error.yml +86 -0
  145. data/spec/spec_tests/data/sdam_integration/insert-shutdown-error.yml +115 -0
  146. data/spec/spec_tests/data/sdam_integration/isMaster-command-error.yml +168 -0
  147. data/spec/spec_tests/data/sdam_integration/isMaster-network-error.yml +162 -0
  148. data/spec/spec_tests/data/sdam_integration/isMaster-timeout.yml +229 -0
  149. data/spec/spec_tests/data/sdam_integration/rediscover-quickly-after-step-down.yml +87 -0
  150. data/spec/spec_tests/max_staleness_spec.rb +4 -142
  151. data/spec/spec_tests/retryable_reads_spec.rb +2 -2
  152. data/spec/spec_tests/sdam_integration_spec.rb +13 -0
  153. data/spec/spec_tests/sdam_monitoring_spec.rb +1 -2
  154. data/spec/spec_tests/server_selection_spec.rb +4 -116
  155. data/spec/stress/cleanup_spec.rb +17 -2
  156. data/spec/stress/connection_pool_stress_spec.rb +10 -8
  157. data/spec/support/child_process_helper.rb +78 -0
  158. data/spec/support/client_registry.rb +1 -0
  159. data/spec/support/cluster_config.rb +4 -0
  160. data/spec/support/event_subscriber.rb +123 -33
  161. data/spec/support/keyword_struct.rb +26 -0
  162. data/spec/support/shared/server_selector.rb +13 -1
  163. data/spec/support/spec_config.rb +38 -13
  164. data/spec/support/spec_organizer.rb +129 -0
  165. data/spec/support/spec_setup.rb +1 -1
  166. data/spec/support/utils.rb +46 -0
  167. metadata +992 -942
  168. metadata.gz.sig +0 -0
  169. data/lib/mongo/server_selector/selectable.rb +0 -560
  170. data/spec/runners/sdam_monitoring.rb +0 -89
@@ -8,7 +8,7 @@ describe 'Retryable reads spec tests' do
8
8
 
9
9
  define_crud_spec_tests(RETRYABLE_READS_TESTS) do |spec, req, test|
10
10
  let(:client) do
11
- authorized_client.with({max_read_retries: 0}.update(test.client_options)).tap do |client|
11
+ authorized_client.use(spec.database_name).with({max_read_retries: 0}.update(test.client_options)).tap do |client|
12
12
  client.subscribe(Mongo::Monitoring::COMMAND, event_subscriber)
13
13
  end
14
14
  end
@@ -28,7 +28,7 @@ describe 'Retryable reads spec tests - legacy' do
28
28
  end
29
29
 
30
30
  let(:client) do
31
- authorized_client.with(client_options).tap do |client|
31
+ authorized_client.use(spec.database_name).with(client_options).tap do |client|
32
32
  client.subscribe(Mongo::Monitoring::COMMAND, event_subscriber)
33
33
  end
34
34
  end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ require 'runners/crud'
4
+ require 'runners/transactions'
5
+
6
+ SDAM_INTEGRATION_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/sdam_integration/*.yml").sort
7
+
8
+ describe 'SDAM integration tests' do
9
+ require_no_multi_shard
10
+ require_wired_tiger
11
+
12
+ define_transactions_spec_tests(SDAM_INTEGRATION_TESTS)
13
+ end
@@ -2,7 +2,6 @@ require 'lite_spec_helper'
2
2
 
3
3
  require 'runners/sdam'
4
4
  require 'runners/sdam/verifier'
5
- require 'runners/sdam_monitoring'
6
5
 
7
6
  describe 'SDAM Monitoring' do
8
7
  include Mongo::SDAM
@@ -14,7 +13,7 @@ describe 'SDAM Monitoring' do
14
13
  context("#{spec.description} (#{file.sub(%r'.*/data/sdam_monitoring/', '')})") do
15
14
 
16
15
  before(:all) do
17
- @subscriber = Mongo::SDAMMonitoring::PhasedTestSubscriber.new
16
+ @subscriber = PhasedEventSubscriber.new
18
17
  sdam_proc = lambda do |client|
19
18
  client.subscribe(Mongo::Monitoring::SERVER_OPENING, @subscriber)
20
19
  client.subscribe(Mongo::Monitoring::SERVER_CLOSED, @subscriber)
@@ -1,121 +1,9 @@
1
- require 'spec_helper'
1
+ require 'lite_spec_helper'
2
2
 
3
3
  require 'runners/server_selection'
4
4
 
5
- describe 'Server Selection' do
5
+ SERVER_SELECTION_TESTS = Dir.glob("#{CURRENT_PATH}/spec_tests/data/server_selection/**/*.yml").sort
6
6
 
7
- include Mongo::ServerSelection::Read
8
-
9
- SERVER_SELECTION_TESTS.each do |file|
10
-
11
- spec = Mongo::ServerSelection::Read::Spec.new(file)
12
-
13
- context(spec.description) do
14
-
15
- let(:monitoring) do
16
- Mongo::Monitoring.new(monitoring: false)
17
- end
18
-
19
- # Cluster needs a topology and topology needs a cluster...
20
- # This temporary cluster is used for topology construction.
21
- let(:temp_cluster) do
22
- double('temp cluster').tap do |cluster|
23
- allow(cluster).to receive(:servers_list).and_return([])
24
- end
25
- end
26
-
27
- let(:topology) do
28
- options = if spec.type <= Mongo::Cluster::Topology::ReplicaSetNoPrimary
29
- {replica_set_name: 'foo'}
30
- else
31
- {}
32
- end
33
- spec.type.new(options, monitoring, temp_cluster)
34
- end
35
-
36
- let(:listeners) do
37
- Mongo::Event::Listeners.new
38
- end
39
-
40
- let(:cluster) do
41
- double('cluster').tap do |c|
42
- allow(c).to receive(:connected?).and_return(true)
43
- allow(c).to receive(:summary)
44
- allow(c).to receive(:topology).and_return(topology)
45
- allow(c).to receive(:single?).and_return(topology.single?)
46
- allow(c).to receive(:sharded?).and_return(topology.sharded?)
47
- allow(c).to receive(:replica_set?).and_return(topology.replica_set?)
48
- allow(c).to receive(:unknown?).and_return(topology.unknown?)
49
- allow(c).to receive(:app_metadata).and_return(app_metadata)
50
- allow(c).to receive(:options).and_return({})
51
- allow(c).to receive(:server_selection_semaphore).and_return(nil)
52
- end
53
- end
54
-
55
- let(:candidate_servers) do
56
- spec.candidate_servers.collect do |server|
57
- address = Mongo::Address.new(server['address'])
58
- Mongo::Server.new(address, cluster, monitoring, listeners,
59
- {monitoring_io: false}.update(SpecConfig.instance.test_options)
60
- ).tap do |s|
61
- allow(s).to receive(:average_round_trip_time).and_return(server['avg_rtt_ms'] / 1000.0)
62
- allow(s).to receive(:tags).and_return(server['tags'])
63
- allow(s).to receive(:secondary?).and_return(server['type'] == 'RSSecondary')
64
- allow(s).to receive(:primary?).and_return(server['type'] == 'RSPrimary')
65
- allow(s).to receive(:connectable?).and_return(true)
66
- allow(s).to receive(:check_driver_support!).and_return(true)
67
- end
68
- end
69
- end
70
-
71
- let(:in_latency_window) do
72
- spec.in_latency_window.collect do |server|
73
- address = Mongo::Address.new(server['address'])
74
- Mongo::Server.new(address, cluster, monitoring, listeners,
75
- {monitoring_io: false}.update(SpecConfig.instance.test_options)
76
- ).tap do |s|
77
- allow(s).to receive(:average_round_trip_time).and_return(server['avg_rtt_ms'] / 1000.0)
78
- allow(s).to receive(:tags).and_return(server['tags'])
79
- allow(s).to receive(:connectable?).and_return(true)
80
- allow(s).to receive(:check_driver_support!).and_return(true)
81
- end
82
- end
83
- end
84
-
85
- let(:server_selector) do
86
- Mongo::ServerSelector.get(:mode => spec.read_preference['mode'],
87
- :tag_sets => spec.read_preference['tag_sets'])
88
- end
89
-
90
- before do
91
- allow(cluster).to receive(:servers).and_return(candidate_servers)
92
- allow(cluster).to receive(:servers_list).and_return(candidate_servers)
93
- allow(cluster).to receive(:addresses).and_return(candidate_servers.map(&:address))
94
- allow(cluster).to receive(:options).and_return(server_selection_timeout: 0.2)
95
- allow(cluster).to receive(:scan!).and_return(true)
96
- allow(cluster).to receive(:app_metadata).and_return(app_metadata)
97
- end
98
-
99
- context 'Valid read preference and matching server available', if: spec.server_available? do
100
-
101
- it 'Finds all suitable servers in the latency window', if: spec.replica_set? do
102
- expect(server_selector.send(:select, cluster.servers)).to match_array(in_latency_window)
103
- end
104
-
105
- it 'Finds the most suitable server in the latency window' do
106
- expect(in_latency_window).to include(server_selector.select_server(cluster))
107
- end
108
- end
109
-
110
- context 'No matching server available', if: !spec.server_available? do
111
- skip_if_linting
112
-
113
- it 'Raises exception' do
114
- expect do
115
- server_selector.select_server(cluster)
116
- end.to raise_exception(Mongo::Error::NoServerAvailable)
117
- end
118
- end
119
- end
120
- end
7
+ describe 'Server selection spec tests' do
8
+ define_server_selection_spec_tests(SERVER_SELECTION_TESTS)
121
9
  end
@@ -20,17 +20,32 @@ describe 'Cleanup stress test' do
20
20
 
21
21
  it 'cleans up' do
22
22
  client
23
+ client.cluster.servers_list.map(&:scan!)
24
+
25
+ sleep 1
26
+ GC.start
23
27
 
24
28
  start_resources = resources
25
29
 
26
- 100.times do
30
+ 500.times do
27
31
  client.close
28
32
  client.reconnect
29
33
  end
30
34
 
35
+ sleep 1
36
+ GC.start
37
+
31
38
  end_resources = resources
32
39
 
33
- end_resources.should == start_resources
40
+ # There seem to be a temporary file descriptor leak in CI,
41
+ # where we start with 75 fds and end with 77 fds.
42
+ # Allow a few to be leaked, run more iterations to ensure the leak
43
+ # is not a real one.
44
+ # Sometimes we end with fewer fds than we started with also...
45
+ end_resources[:open_file_count].should >= start_resources[:open_file_count] - 3
46
+ end_resources[:open_file_count].should <= start_resources[:open_file_count] + 3
47
+
48
+ end_resources[:running_thread_count].should == start_resources[:running_thread_count]
34
49
  end
35
50
  end
36
51
 
@@ -21,9 +21,9 @@ describe 'Connection pool stress test' do
21
21
  [].tap do |threads|
22
22
  thread_count.times do |i|
23
23
  threads << Thread.new do
24
- 10.times do |j|
24
+ 100.times do |j|
25
25
  collection.find(a: i+j).to_a
26
- sleep 0.5
26
+ sleep 0.1
27
27
  collection.find(a: i+j).to_a
28
28
  end
29
29
  end
@@ -132,7 +132,7 @@ describe 'Connection pool stress test' do
132
132
  end
133
133
 
134
134
  context '25 threads, max pool size 5' do
135
- let(:thread_count) { 20 }
135
+ let(:thread_count) { 25 }
136
136
 
137
137
  it_behaves_like 'does not raise error'
138
138
  end
@@ -183,17 +183,19 @@ describe 'Connection pool stress test' do
183
183
  # The populator calls create_and_add_connection, which calls connection.connect!
184
184
  # and raises if an error occurs (eg, an auth error), so in this test we
185
185
  # randomly raise errors on this method.
186
- expect(server.pool).to receive(:create_and_add_connection).at_least(:once).and_wrap_original { |m, *args|
187
- if rand < 0.05
186
+ expect(server.pool).to receive(:create_and_add_connection).at_least(:once).and_wrap_original do |m, *args|
187
+ if rand < 0.1
188
188
  raise Mongo::Error::SocketError
189
189
  else
190
190
  m.call(*args)
191
191
  end
192
- }
192
+ end
193
193
 
194
- expect {
194
+ server.pool.clear(stop_populator: false)
195
+
196
+ expect do
195
197
  threads.collect { |t| t.join }
196
- }.not_to raise_error
198
+ end.not_to raise_error
197
199
  end
198
200
  end
199
201
  end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ autoload :ChildProcess, 'childprocess'
5
+ autoload :Tempfile, 'tempfile'
6
+
7
+ module ChildProcessHelper
8
+ class SpawnError < StandardError; end
9
+
10
+ module_function def call(cmd, env: nil, cwd: nil)
11
+ process = ChildProcess.new(*cmd)
12
+ process.io.inherit!
13
+ if cwd
14
+ process.cwd = cwd
15
+ end
16
+ if env
17
+ env.each do |k, v|
18
+ process.environment[k.to_s] = v
19
+ end
20
+ end
21
+ process.start
22
+ process.wait
23
+ process
24
+ end
25
+
26
+ module_function def check_call(cmd, env: nil, cwd: nil)
27
+ process = call(cmd, env: env, cwd: cwd)
28
+ unless process.exit_code == 0
29
+ raise SpawnError, "Failed to execute: #{cmd}"
30
+ end
31
+ end
32
+
33
+ module_function def get_output(cmd, env: nil, cwd: nil)
34
+ process = ChildProcess.new(*cmd)
35
+ process.io.inherit!
36
+ if cwd
37
+ process.cwd = cwd
38
+ end
39
+ if env
40
+ env.each do |k, v|
41
+ process.environment[k.to_s] = v
42
+ end
43
+ end
44
+
45
+ output = ''
46
+ r, w = IO.pipe
47
+
48
+ begin
49
+ process.io.stdout = w
50
+ process.start
51
+ w.close
52
+
53
+ thread = Thread.new do
54
+ begin
55
+ loop do
56
+ output << r.readpartial(16384)
57
+ end
58
+ rescue EOFError
59
+ end
60
+ end
61
+
62
+ process.wait
63
+ thread.join
64
+ ensure
65
+ r.close
66
+ end
67
+
68
+ [process, output]
69
+ end
70
+
71
+ module_function def check_output(*args)
72
+ process, output = get_output(*args)
73
+ unless process.exit_code == 0
74
+ raise SpawnError,"Failed to execute: #{args}"
75
+ end
76
+ output
77
+ end
78
+ end
@@ -105,6 +105,7 @@ class ClientRegistry
105
105
  }.update(SpecConfig.instance.credentials_or_external_user(
106
106
  user: SpecConfig.instance.test_user.name,
107
107
  password: SpecConfig.instance.test_user.password,
108
+ auth_source: 'admin',
108
109
  ))
109
110
 
110
111
  Mongo::Client.new(
@@ -38,6 +38,10 @@ class ClusterConfig
38
38
  # less than 3.4. This method returns FCV on 3.4+ servers when in single
39
39
  # or RS topologies, and otherwise returns the major.minor server version.
40
40
  def fcv_ish
41
+ if server_version.nil?
42
+ raise "Deployment server version not known - check that connection to deployment succeeded"
43
+ end
44
+
41
45
  if server_version >= '3.4' && topology != :sharded
42
46
  fcv
43
47
  else
@@ -3,6 +3,21 @@
3
3
  # @since 2.5.0
4
4
  class EventSubscriber
5
5
 
6
+ # The mappings of event names to types.
7
+ #
8
+ # @since 2.4.0
9
+ MAPPINGS = {
10
+ 'topology_opening_event' => Mongo::Monitoring::Event::TopologyOpening,
11
+ 'topology_description_changed_event' => Mongo::Monitoring::Event::TopologyChanged,
12
+ 'topology_closed_event' => Mongo::Monitoring::Event::TopologyClosed,
13
+ 'server_opening_event' => Mongo::Monitoring::Event::ServerOpening,
14
+ 'server_description_changed_event' => Mongo::Monitoring::Event::ServerDescriptionChanged,
15
+ 'server_closed_event' => Mongo::Monitoring::Event::ServerClosed
16
+ }.freeze
17
+
18
+ # All events.
19
+ attr_reader :all_events
20
+
6
21
  # The started events.
7
22
  #
8
23
  # @since 2.5.0
@@ -20,25 +35,40 @@ class EventSubscriber
20
35
 
21
36
  attr_reader :published_events
22
37
 
23
- # Cache the succeeded event.
24
- #
25
- # @param [ Event ] event The event.
26
- #
27
- # @since 2.5.0
28
- def succeeded(event)
29
- @mutex.synchronize do
30
- succeeded_events.push(event)
38
+ # @param [ String ] name Optional name for the event subscriber.
39
+ def initialize(name: nil)
40
+ @mutex = Mutex.new
41
+ clear_events!
42
+ @name = name
43
+ end
44
+
45
+ def to_s
46
+ %Q`#<EventSubscriber:#{@name ? "\"#{@name}\"" : '%x' % object_id} \
47
+ started=#{started_events.length} \
48
+ succeeded=#{succeeded_events.length} \
49
+ failed=#{failed_events.length} \
50
+ published=#{published_events.length}>`
51
+ end
52
+
53
+ alias :inspect :to_s
54
+
55
+ # Event retrieval
56
+
57
+ def select_started_events(cls)
58
+ started_events.select do |event|
59
+ event.is_a?(cls)
31
60
  end
32
61
  end
33
62
 
34
- # Cache the started event.
35
- #
36
- # @param [ Event ] event The event.
37
- #
38
- # @since 2.5.0
39
- def started(event)
40
- @mutex.synchronize do
41
- started_events.push(event)
63
+ def select_succeeded_events(cls)
64
+ succeeded_events.select do |event|
65
+ event.is_a?(cls)
66
+ end
67
+ end
68
+
69
+ def select_published_events(cls)
70
+ published_events.select do |event|
71
+ event.is_a?(cls)
42
72
  end
43
73
  end
44
74
 
@@ -59,8 +89,13 @@ class EventSubscriber
59
89
 
60
90
  # Locates command stated events for the specified command name,
61
91
  # asserts that there is exactly one such event, and returns it.
62
- def single_command_started_event(command_name)
63
- events = started_events.select do |event|
92
+ def single_command_started_event(command_name, include_auth: false)
93
+ events = if include_auth
94
+ started_events
95
+ else
96
+ non_auth_command_started_events
97
+ end
98
+ events.select! do |event|
64
99
  event.command[command_name]
65
100
  end
66
101
  if events.length != 1
@@ -69,38 +104,65 @@ class EventSubscriber
69
104
  events.first
70
105
  end
71
106
 
72
- def select_started_events(cls)
73
- @started_events.select do |event|
74
- event.is_a?(cls)
107
+ # Get the first succeeded event published for the name, and then delete it.
108
+ #
109
+ # @param [ String ] name The event name.
110
+ #
111
+ # @return [ Event ] The matching event.
112
+ def first_event(name)
113
+ cls = MAPPINGS[name]
114
+ if cls.nil?
115
+ raise ArgumentError, "Bogus event name #{name}"
75
116
  end
117
+ matching = succeeded_events.find do |event|
118
+ cls === event
119
+ end
120
+ succeeded_events.delete(matching)
121
+ matching
76
122
  end
77
123
 
78
- def select_succeeded_events(cls)
79
- @succeeded_events.select do |event|
80
- event.is_a?(cls)
124
+ # Event recording
125
+
126
+ # Cache the started event.
127
+ #
128
+ # @param [ Event ] event The event.
129
+ #
130
+ # @since 2.5.0
131
+ def started(event)
132
+ @mutex.synchronize do
133
+ started_events << event
134
+ all_events << event
81
135
  end
82
136
  end
83
137
 
84
- # Cache the failed event.
138
+ # Cache the succeeded event.
85
139
  #
86
140
  # @param [ Event ] event The event.
87
141
  #
88
142
  # @since 2.5.0
89
- def failed(event)
143
+ def succeeded(event)
90
144
  @mutex.synchronize do
91
- failed_events.push(event)
145
+ succeeded_events << event
146
+ all_events << event
92
147
  end
93
148
  end
94
149
 
95
- def select_published_events(cls)
96
- @published_events.select do |event|
97
- event.is_a?(cls)
150
+ # Cache the failed event.
151
+ #
152
+ # @param [ Event ] event The event.
153
+ #
154
+ # @since 2.5.0
155
+ def failed(event)
156
+ @mutex.synchronize do
157
+ failed_events << event
158
+ all_events << event
98
159
  end
99
160
  end
100
161
 
101
162
  def published(event)
102
163
  @mutex.synchronize do
103
- @published_events << event
164
+ published_events << event
165
+ all_events << event
104
166
  end
105
167
  end
106
168
 
@@ -108,15 +170,43 @@ class EventSubscriber
108
170
  #
109
171
  # @since 2.5.1
110
172
  def clear_events!
173
+ @all_events = []
111
174
  @started_events = []
112
175
  @succeeded_events = []
113
176
  @failed_events = []
114
177
  @published_events = []
115
178
  self
116
179
  end
180
+ end
117
181
 
182
+ # Only handles succeeded events correctly.
183
+ class PhasedEventSubscriber < EventSubscriber
118
184
  def initialize
119
- @mutex = Mutex.new
120
- clear_events!
185
+ super
186
+ @phase_events = {}
187
+ end
188
+
189
+ def phase_finished(phase_index)
190
+ @phase_events[phase_index] = succeeded_events
191
+ @succeeded_events = []
192
+ end
193
+
194
+ def phase_events(phase_index)
195
+ @phase_events[phase_index]
196
+ end
197
+
198
+ def event_count
199
+ @phase_events.inject(0) do |sum, event|
200
+ sum + event.length
201
+ end
202
+ end
203
+ end
204
+
205
+ class VerboseEventSubscriber < EventSubscriber
206
+ %w(started succeeded failed published).each do |meth|
207
+ define_method(meth) do |event|
208
+ puts event.summary
209
+ super(event)
210
+ end
121
211
  end
122
212
  end