mongo 2.14.1 → 2.15.0.alpha

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 (230) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +4 -1
  4. data/Rakefile +8 -15
  5. data/lib/mongo/auth/aws/conversation.rb +1 -4
  6. data/lib/mongo/auth/base.rb +13 -7
  7. data/lib/mongo/auth/conversation_base.rb +32 -0
  8. data/lib/mongo/auth/cr/conversation.rb +6 -29
  9. data/lib/mongo/auth/gssapi/conversation.rb +4 -15
  10. data/lib/mongo/auth/ldap/conversation.rb +3 -14
  11. data/lib/mongo/auth/sasl_conversation_base.rb +1 -13
  12. data/lib/mongo/auth/scram_conversation_base.rb +7 -34
  13. data/lib/mongo/auth/user/view.rb +16 -9
  14. data/lib/mongo/auth/x509/conversation.rb +4 -25
  15. data/lib/mongo/bulk_write.rb +21 -18
  16. data/lib/mongo/client.rb +82 -6
  17. data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -2
  18. data/lib/mongo/cluster.rb +19 -2
  19. data/lib/mongo/collection/view/aggregation.rb +1 -1
  20. data/lib/mongo/collection/view/change_stream.rb +1 -1
  21. data/lib/mongo/collection/view/iterable.rb +7 -17
  22. data/lib/mongo/collection/view/map_reduce.rb +2 -2
  23. data/lib/mongo/collection/view/readable.rb +42 -20
  24. data/lib/mongo/collection/view/writable.rb +14 -14
  25. data/lib/mongo/collection.rb +6 -6
  26. data/lib/mongo/cursor.rb +2 -12
  27. data/lib/mongo/database/view.rb +1 -1
  28. data/lib/mongo/database.rb +8 -3
  29. data/lib/mongo/error/bulk_write_error.rb +17 -3
  30. data/lib/mongo/error/internal_driver_error.rb +22 -0
  31. data/lib/mongo/error/operation_failure.rb +21 -2
  32. data/lib/mongo/error/parser.rb +65 -12
  33. data/lib/mongo/error/server_api_conflict.rb +23 -0
  34. data/lib/mongo/error/server_api_not_supported.rb +24 -0
  35. data/lib/mongo/error/unmet_dependency.rb +21 -0
  36. data/lib/mongo/error.rb +9 -1
  37. data/lib/mongo/index/view.rb +21 -11
  38. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +27 -16
  39. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +26 -15
  40. data/lib/mongo/monitoring.rb +13 -4
  41. data/lib/mongo/operation/collections_info/command.rb +2 -2
  42. data/lib/mongo/operation/collections_info.rb +18 -1
  43. data/lib/mongo/operation/context.rb +99 -0
  44. data/lib/mongo/operation/indexes.rb +15 -1
  45. data/lib/mongo/operation/insert/command.rb +2 -2
  46. data/lib/mongo/operation/insert/legacy.rb +2 -2
  47. data/lib/mongo/operation/insert/op_msg.rb +2 -2
  48. data/lib/mongo/operation/list_collections/result.rb +4 -1
  49. data/lib/mongo/operation/parallel_scan/command.rb +2 -1
  50. data/lib/mongo/operation/result.rb +2 -0
  51. data/lib/mongo/operation/shared/executable.rb +24 -14
  52. data/lib/mongo/operation/shared/executable_no_validate.rb +2 -2
  53. data/lib/mongo/operation/shared/op_msg_or_command.rb +1 -7
  54. data/lib/mongo/operation/shared/op_msg_or_find_command.rb +1 -7
  55. data/lib/mongo/operation/shared/polymorphic_operation.rb +39 -0
  56. data/lib/mongo/operation/shared/read_preference_supported.rb +36 -38
  57. data/lib/mongo/operation/shared/response_handling.rb +23 -23
  58. data/lib/mongo/operation/shared/sessions_supported.rb +15 -5
  59. data/lib/mongo/operation/shared/write.rb +8 -18
  60. data/lib/mongo/operation.rb +2 -2
  61. data/lib/mongo/protocol/compressed.rb +51 -5
  62. data/lib/mongo/protocol/message.rb +20 -2
  63. data/lib/mongo/protocol/msg.rb +38 -13
  64. data/lib/mongo/protocol/query.rb +11 -11
  65. data/lib/mongo/query_cache.rb +30 -0
  66. data/lib/mongo/retryable.rb +1 -1
  67. data/lib/mongo/server/app_metadata.rb +52 -18
  68. data/lib/mongo/server/connection.rb +5 -0
  69. data/lib/mongo/server/connection_base.rb +13 -10
  70. data/lib/mongo/server/connection_pool.rb +6 -2
  71. data/lib/mongo/server/description/features.rb +9 -8
  72. data/lib/mongo/server/description.rb +4 -0
  73. data/lib/mongo/server/monitor/app_metadata.rb +1 -1
  74. data/lib/mongo/server/monitor/connection.rb +9 -10
  75. data/lib/mongo/server/monitor.rb +20 -1
  76. data/lib/mongo/server/pending_connection.rb +24 -6
  77. data/lib/mongo/server/push_monitor.rb +11 -1
  78. data/lib/mongo/server.rb +7 -1
  79. data/lib/mongo/server_selector/secondary_preferred.rb +7 -2
  80. data/lib/mongo/session/session_pool.rb +4 -2
  81. data/lib/mongo/session.rb +2 -2
  82. data/lib/mongo/socket/ssl.rb +8 -0
  83. data/lib/mongo/socket.rb +29 -4
  84. data/lib/mongo/uri/options_mapper.rb +38 -0
  85. data/lib/mongo/utils.rb +15 -0
  86. data/lib/mongo/version.rb +1 -1
  87. data/lib/mongo.rb +23 -0
  88. data/spec/README.md +24 -1
  89. data/spec/integration/auth_spec.rb +25 -15
  90. data/spec/integration/bulk_write_error_message_spec.rb +41 -0
  91. data/spec/integration/change_stream_spec.rb +4 -4
  92. data/spec/integration/command_monitoring_spec.rb +2 -2
  93. data/spec/integration/connection_spec.rb +2 -0
  94. data/spec/integration/docs_examples_spec.rb +8 -1
  95. data/spec/integration/fork_reconnect_spec.rb +4 -1
  96. data/spec/integration/ocsp_verifier_spec.rb +13 -7
  97. data/spec/integration/operation_failure_code_spec.rb +1 -1
  98. data/spec/integration/operation_failure_message_spec.rb +90 -0
  99. data/spec/integration/query_cache_spec.rb +0 -45
  100. data/spec/integration/reconnect_spec.rb +1 -1
  101. data/spec/integration/snappy_compression_spec.rb +25 -0
  102. data/spec/integration/srv_monitoring_spec.rb +1 -1
  103. data/spec/integration/transactions_examples_spec.rb +6 -0
  104. data/spec/integration/zlib_compression_spec.rb +1 -1
  105. data/spec/integration/zstd_compression_spec.rb +26 -0
  106. data/spec/lite_spec_helper.rb +7 -1
  107. data/spec/mongo/address_spec.rb +15 -11
  108. data/spec/mongo/auth/ldap/conversation_spec.rb +1 -1
  109. data/spec/mongo/auth/ldap_spec.rb +5 -1
  110. data/spec/mongo/auth/scram_negotiation_spec.rb +1 -1
  111. data/spec/mongo/auth/scram_spec.rb +1 -1
  112. data/spec/mongo/auth/x509/conversation_spec.rb +3 -3
  113. data/spec/mongo/client_construction_spec.rb +207 -33
  114. data/spec/mongo/client_spec.rb +17 -0
  115. data/spec/mongo/cluster_spec.rb +1 -0
  116. data/spec/mongo/collection/view/explainable_spec.rb +1 -1
  117. data/spec/mongo/collection/view/readable_spec.rb +33 -19
  118. data/spec/mongo/collection_crud_spec.rb +4357 -0
  119. data/spec/mongo/collection_ddl_spec.rb +534 -0
  120. data/spec/mongo/collection_spec.rb +5 -4859
  121. data/spec/mongo/database_spec.rb +66 -4
  122. data/spec/mongo/error/bulk_write_error_spec.rb +3 -3
  123. data/spec/mongo/error/parser_spec.rb +37 -6
  124. data/spec/mongo/index/view_spec.rb +4 -0
  125. data/spec/mongo/monitoring/event/server_heartbeat_failed_spec.rb +1 -1
  126. data/spec/mongo/monitoring/event/server_heartbeat_succeeded_spec.rb +1 -1
  127. data/spec/mongo/operation/aggregate_spec.rb +2 -1
  128. data/spec/mongo/operation/collections_info_spec.rb +4 -1
  129. data/spec/mongo/operation/command_spec.rb +6 -3
  130. data/spec/mongo/operation/create_index_spec.rb +6 -3
  131. data/spec/mongo/operation/create_user_spec.rb +6 -3
  132. data/spec/mongo/operation/delete/bulk_spec.rb +9 -6
  133. data/spec/mongo/operation/delete_spec.rb +11 -7
  134. data/spec/mongo/operation/drop_index_spec.rb +6 -2
  135. data/spec/mongo/operation/find/legacy_spec.rb +3 -1
  136. data/spec/mongo/operation/get_more_spec.rb +3 -1
  137. data/spec/mongo/operation/indexes_spec.rb +5 -1
  138. data/spec/mongo/operation/insert/bulk_spec.rb +10 -7
  139. data/spec/mongo/operation/insert_spec.rb +15 -12
  140. data/spec/mongo/operation/map_reduce_spec.rb +5 -2
  141. data/spec/mongo/operation/read_preference_legacy_spec.rb +19 -9
  142. data/spec/mongo/operation/read_preference_op_msg_spec.rb +3 -3
  143. data/spec/mongo/operation/remove_user_spec.rb +6 -3
  144. data/spec/mongo/operation/result_spec.rb +1 -1
  145. data/spec/mongo/operation/update/bulk_spec.rb +9 -6
  146. data/spec/mongo/operation/update_spec.rb +10 -7
  147. data/spec/mongo/operation/update_user_spec.rb +4 -1
  148. data/spec/mongo/protocol/compressed_spec.rb +26 -12
  149. data/spec/mongo/query_cache_middleware_spec.rb +55 -0
  150. data/spec/mongo/retryable_spec.rb +3 -2
  151. data/spec/mongo/server/app_metadata_shared.rb +7 -33
  152. data/spec/mongo/server/app_metadata_spec.rb +2 -0
  153. data/spec/mongo/server/connection_pool/populator_spec.rb +3 -1
  154. data/spec/mongo/server/connection_pool_spec.rb +1 -1
  155. data/spec/mongo/server/connection_spec.rb +24 -17
  156. data/spec/mongo/server/monitor/connection_spec.rb +17 -7
  157. data/spec/mongo/server/monitor_spec.rb +9 -1
  158. data/spec/mongo/server_selector/secondary_preferred_spec.rb +6 -6
  159. data/spec/mongo/server_spec.rb +15 -2
  160. data/spec/mongo/socket/ssl_spec.rb +40 -0
  161. data/spec/mongo/socket_spec.rb +2 -2
  162. data/spec/mongo/tls_context_hooks_spec.rb +37 -0
  163. data/spec/runners/connection_string.rb +0 -4
  164. data/spec/runners/crud/requirement.rb +40 -3
  165. data/spec/runners/crud/verifier.rb +8 -0
  166. data/spec/runners/transactions/operation.rb +1 -1
  167. data/spec/runners/transactions/test.rb +1 -0
  168. data/spec/runners/unified/assertions.rb +249 -0
  169. data/spec/runners/unified/change_stream_operations.rb +26 -0
  170. data/spec/runners/unified/crud_operations.rb +199 -0
  171. data/spec/runners/unified/ddl_operations.rb +96 -0
  172. data/spec/runners/unified/entity_map.rb +39 -0
  173. data/spec/runners/unified/error.rb +25 -0
  174. data/spec/runners/unified/event_subscriber.rb +91 -0
  175. data/spec/runners/unified/exceptions.rb +21 -0
  176. data/spec/runners/unified/grid_fs_operations.rb +55 -0
  177. data/spec/runners/unified/support_operations.rb +250 -0
  178. data/spec/runners/unified/test.rb +393 -0
  179. data/spec/runners/unified/test_group.rb +28 -0
  180. data/spec/runners/unified/using_hash.rb +31 -0
  181. data/spec/runners/unified.rb +96 -0
  182. data/spec/shared/lib/mrss/cluster_config.rb +0 -3
  183. data/spec/shared/lib/mrss/docker_runner.rb +0 -3
  184. data/spec/shared/lib/mrss/lite_constraints.rb +0 -16
  185. data/spec/shared/lib/mrss/server_version_registry.rb +0 -3
  186. data/spec/shared/lib/mrss/spec_organizer.rb +0 -3
  187. data/spec/shared/shlib/server.sh +1 -1
  188. data/spec/spec_helper.rb +4 -1
  189. data/spec/spec_tests/crud_unified_spec.rb +10 -0
  190. data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
  191. data/spec/spec_tests/data/crud_unified/estimatedDocumentCount.yml +267 -0
  192. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-4.9.yml +60 -0
  193. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount.yml → estimatedDocumentCount-pre4.9.yml} +2 -0
  194. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors-4.9.yml +146 -0
  195. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount-serverErrors.yml → estimatedDocumentCount-serverErrors-pre4.9.yml} +2 -0
  196. data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +1 -1
  197. data/spec/spec_tests/data/unified/valid-fail/operation-failure.yml +31 -0
  198. data/spec/spec_tests/data/unified/valid-pass/poc-change-streams.yml +220 -0
  199. data/spec/spec_tests/data/unified/valid-pass/poc-command-monitoring.yml +102 -0
  200. data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +184 -0
  201. data/spec/spec_tests/data/unified/valid-pass/poc-gridfs.yml +155 -0
  202. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-reads.yml +193 -0
  203. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +210 -0
  204. data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +215 -0
  205. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +235 -0
  206. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +169 -0
  207. data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +170 -0
  208. data/spec/spec_tests/data/uri_options/compression-options.yml +1 -1
  209. data/spec/spec_tests/data/versioned_api/crud-api-version-1-strict.yml +416 -0
  210. data/spec/spec_tests/data/versioned_api/crud-api-version-1.yml +409 -0
  211. data/spec/spec_tests/data/versioned_api/runcommand-helper-no-api-version-declared.yml +67 -0
  212. data/spec/spec_tests/data/versioned_api/test-commands-deprecation-errors.yml +47 -0
  213. data/spec/spec_tests/data/versioned_api/test-commands-strict-mode.yml +44 -0
  214. data/spec/spec_tests/data/versioned_api/transaction-handling.yml +180 -0
  215. data/spec/spec_tests/unified_spec.rb +15 -0
  216. data/spec/spec_tests/uri_options_spec.rb +16 -0
  217. data/spec/spec_tests/versioned_api_spec.rb +10 -0
  218. data/spec/support/client_registry.rb +4 -8
  219. data/spec/support/client_registry_macros.rb +4 -4
  220. data/spec/support/common_shortcuts.rb +15 -1
  221. data/spec/support/shared/session.rb +2 -2
  222. data/spec/support/spec_config.rb +42 -11
  223. data/spec/support/utils.rb +64 -3
  224. data.tar.gz.sig +0 -0
  225. metadata +1005 -915
  226. metadata.gz.sig +0 -0
  227. data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +0 -58
  228. data/lib/mongo/operation/shared/op_msg_or_list_indexes_command.rb +0 -47
  229. data/spec/integration/secondary_reads_spec.rb +0 -102
  230. data/spec/support/cluster_config.rb +0 -207
@@ -63,6 +63,10 @@ module Mongo
63
63
  # @option options [ Float ] :heartbeat_interval The interval between
64
64
  # regular server checks.
65
65
  # @option options [ Logger ] :logger A custom logger to use.
66
+ # @option options [ Mongo::Server::Monitor::AppMetadata ] :monitor_app_metadata
67
+ # The metadata to use for regular monitoring connection.
68
+ # @option options [ Mongo::Server::Monitor::AppMetadata ] :push_monitor_app_metadata
69
+ # The metadata to use for push monitor's connection.
66
70
  # @option options [ Float ] :socket_timeout The timeout, in seconds, to
67
71
  # execute operations on the monitoring connection.
68
72
  #
@@ -72,6 +76,12 @@ module Mongo
72
76
  unless monitoring.is_a?(Monitoring)
73
77
  raise ArgumentError, "Wrong monitoring type: #{monitoring.inspect}"
74
78
  end
79
+ unless options[:app_metadata]
80
+ raise ArgumentError, 'App metadata is required'
81
+ end
82
+ unless options[:push_monitor_app_metadata]
83
+ raise ArgumentError, 'Push monitor app metadata is required'
84
+ end
75
85
  @server = server
76
86
  @event_listeners = event_listeners
77
87
  @monitoring = monitoring
@@ -273,7 +283,15 @@ module Mongo
273
283
  if @connection
274
284
  result = server.round_trip_time_averager.measure do
275
285
  begin
276
- message = @connection.dispatch_bytes(Monitor::Connection::ISMASTER_BYTES)
286
+ ismaster_bytes = if server_api = options[:server_api]
287
+ ismaster_doc = Monitor::Connection::ISMASTER.merge(
288
+ Utils.transform_server_api(server_api)
289
+ )
290
+ Protocol::Query.new(Database::ADMIN, Database::COMMAND, ismaster_doc, limit: -1).serialize.to_s
291
+ else
292
+ Monitor::Connection::ISMASTER_BYTES
293
+ end
294
+ message = @connection.dispatch_bytes(ismaster_bytes)
277
295
  message.documents.first
278
296
  rescue Mongo::Error
279
297
  @connection.disconnect!
@@ -297,6 +315,7 @@ module Mongo
297
315
  monitoring,
298
316
  **Utils.shallow_symbolize_keys(options.merge(
299
317
  socket_timeout: heartbeat_interval + connection.socket_timeout,
318
+ app_metadata: options[:push_monitor_app_metadata],
300
319
  )),
301
320
  )
302
321
  end
@@ -63,6 +63,11 @@ module Mongo
63
63
  end
64
64
 
65
65
  result = handshake!(speculative_auth_doc: speculative_auth_doc)
66
+
67
+ if description.unknown?
68
+ raise Error::InternalDriverError, "Connection description cannot be unknown after successful handshake: #{description.inspect}"
69
+ end
70
+
66
71
  if speculative_auth_doc && (speculative_auth_result = result['speculativeAuthenticate'])
67
72
  unless description.features.scram_sha_1_enabled?
68
73
  raise Error::InvalidServerAuthResponse, "Speculative auth succeeded on a pre-3.0 server"
@@ -80,11 +85,15 @@ module Mongo
80
85
  speculative_auth_result: speculative_auth_result,
81
86
  )
82
87
  else
83
- raise NotImplementedError, "Speculative auth unexpectedly succeeded for mechanism #{speculative_auth_user.mechanism.inspect}"
88
+ raise Error::InternalDriverError, "Speculative auth unexpectedly succeeded for mechanism #{speculative_auth_user.mechanism.inspect}"
84
89
  end
85
90
  elsif !description.arbiter?
86
91
  authenticate!
87
92
  end
93
+
94
+ if description.unknown?
95
+ raise Error::InternalDriverError, "Connection description cannot be unknown after successful authentication: #{description.inspect}"
96
+ end
88
97
  end
89
98
 
90
99
  private
@@ -96,7 +105,7 @@ module Mongo
96
105
  # this particular connection.
97
106
  def handshake!(speculative_auth_doc: nil)
98
107
  unless socket
99
- raise Error::HandshakeError, "Cannot handshake because there is no usable socket (for #{address})"
108
+ raise Error::InternalDriverError, "Cannot handshake because there is no usable socket (for #{address})"
100
109
  end
101
110
 
102
111
  ismaster_doc = app_metadata.validated_document
@@ -104,18 +113,27 @@ module Mongo
104
113
  ismaster_doc = ismaster_doc.merge(speculativeAuthenticate: speculative_auth_doc)
105
114
  end
106
115
 
116
+ if server_api = options[:server_api]
117
+ ismaster_doc = ismaster_doc.merge(
118
+ Utils.transform_server_api(server_api)
119
+ )
120
+ end
121
+
107
122
  ismaster_command = Protocol::Query.new(Database::ADMIN, Database::COMMAND,
108
123
  ismaster_doc, :limit => -1)
109
124
 
110
- response = nil
125
+ doc = nil
111
126
  @server.handle_handshake_failure! do
112
127
  begin
113
128
  response = @server.round_trip_time_averager.measure do
114
129
  add_server_diagnostics do
115
130
  socket.write(ismaster_command.serialize.to_s)
116
- Protocol::Message.deserialize(socket, Protocol::Message::MAX_MESSAGE_SIZE).documents.first
131
+ Protocol::Message.deserialize(socket, Protocol::Message::MAX_MESSAGE_SIZE)
117
132
  end
118
133
  end
134
+ result = Operation::Result.new([response])
135
+ result.validate!
136
+ doc = result.documents.first
119
137
  rescue => exc
120
138
  msg = "Failed to handshake with #{address}"
121
139
  Utils.warn_bg_exception(msg, exc,
@@ -127,9 +145,9 @@ module Mongo
127
145
  end
128
146
  end
129
147
 
130
- post_handshake(response, @server.round_trip_time_averager.average_round_trip_time)
148
+ post_handshake(doc, @server.round_trip_time_averager.average_round_trip_time)
131
149
 
132
- response
150
+ doc
133
151
  end
134
152
 
135
153
  # @param [ String | nil ] speculative_auth_client_nonce The client
@@ -33,6 +33,9 @@ module Mongo
33
33
  if topology_version.nil?
34
34
  raise ArgumentError, 'Topology version must be provided but it was nil'
35
35
  end
36
+ unless options[:app_metadata]
37
+ raise ArgumentError, 'App metadata is required'
38
+ end
36
39
  @monitor = monitor
37
40
  @topology_version = topology_version
38
41
  @monitoring = monitoring
@@ -139,7 +142,9 @@ module Mongo
139
142
  raise
140
143
  end
141
144
  @server_pushing = resp_msg.flags.include?(:more_to_come)
142
- result = resp_msg.documents.first
145
+ result = Operation::Result.new(resp_msg)
146
+ result.validate!
147
+ result.documents.first
143
148
  end
144
149
 
145
150
  def write_ismaster
@@ -147,6 +152,11 @@ module Mongo
147
152
  topologyVersion: topology_version.to_doc,
148
153
  maxAwaitTimeMS: monitor.heartbeat_interval * 1000,
149
154
  )
155
+ if server_api = options[:server_api]
156
+ payload.update(
157
+ Utils.transform_server_api(server_api)
158
+ )
159
+ end
150
160
 
151
161
  req_msg = Protocol::Msg.new([:exhaust_allowed], {}, payload)
152
162
  @lock.synchronize { @connection }.write_bytes(req_msg.serialize.to_s)
data/lib/mongo/server.rb CHANGED
@@ -71,7 +71,8 @@ module Mongo
71
71
  unless options[:monitoring_io] == false
72
72
  @monitor = Monitor.new(self, event_listeners, monitoring,
73
73
  options.merge(
74
- app_metadata: Monitor::AppMetadata.new(cluster.options),
74
+ app_metadata: cluster.monitor_app_metadata,
75
+ push_monitor_app_metadata: cluster.push_monitor_app_metadata,
75
76
  heartbeat_interval: cluster.heartbeat_interval,
76
77
  ))
77
78
  unless _monitor == false
@@ -172,6 +173,11 @@ module Mongo
172
173
  :cluster_time,
173
174
  :update_cluster_time
174
175
 
176
+ # @api private
177
+ def_delegators :cluster,
178
+ :monitor_app_metadata,
179
+ :push_monitor_app_metadata
180
+
175
181
  def_delegators :features,
176
182
  :check_driver_support!
177
183
 
@@ -86,8 +86,13 @@ module Mongo
86
86
  #
87
87
  # @since 2.0.0
88
88
  def to_mongos
89
- # Always send the read preference to mongos: DRIVERS-1642.
90
- to_doc
89
+ if tag_sets.empty? && max_staleness.nil? && hedge.nil?
90
+ # The server preference is not sent to mongos as part of the query
91
+ # selector if there are no tag sets, for maximum backwards compatibility.
92
+ nil
93
+ else
94
+ to_doc
95
+ end
91
96
  end
92
97
 
93
98
  private
@@ -119,8 +119,10 @@ module Mongo
119
119
  },
120
120
  db_name: Database::ADMIN,
121
121
  )
122
- # end_sessions does not take a client as an argument
123
- op.execute(server, client: nil)
122
+ context = Operation::Context.new(options: {
123
+ server_api: server.options[:server_api],
124
+ })
125
+ op.execute(server, context: context)
124
126
  end
125
127
  rescue Mongo::Error, Error::AuthError
126
128
  end
data/lib/mongo/session.rb CHANGED
@@ -582,7 +582,7 @@ module Mongo
582
582
  txn_num: txn_num,
583
583
  write_concern: write_concern,
584
584
  }
585
- Operation::Command.new(spec).execute(server, client: @client)
585
+ Operation::Command.new(spec).execute(server, context: Operation::Context.new(client: @client, session: self))
586
586
  end
587
587
  end
588
588
  ensure
@@ -629,7 +629,7 @@ module Mongo
629
629
  db_name: 'admin',
630
630
  session: self,
631
631
  txn_num: txn_num
632
- ).execute(server, client: @client)
632
+ ).execute(server, context: Operation::Context.new(client: @client, session: self))
633
633
  end
634
634
  end
635
635
 
@@ -106,6 +106,8 @@ module Mongo
106
106
  begin
107
107
  @tcp_socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
108
108
  set_socket_options(@tcp_socket)
109
+ run_tls_context_hooks
110
+
109
111
  connect!
110
112
  rescue
111
113
  @tcp_socket.close
@@ -379,6 +381,12 @@ module Mongo
379
381
  def human_address
380
382
  "#{host}:#{port} (#{host_name}:#{port}, TLS)"
381
383
  end
384
+
385
+ def run_tls_context_hooks
386
+ Mongo.tls_context_hooks.each do |hook|
387
+ hook.call(@context)
388
+ end
389
+ end
382
390
  end
383
391
  end
384
392
  end
data/lib/mongo/socket.rb CHANGED
@@ -30,6 +30,7 @@ module Mongo
30
30
  # Error message for TLS related exceptions.
31
31
  #
32
32
  # @since 2.0.0
33
+ # @deprecated
33
34
  SSL_ERROR = 'MongoDB may not be configured with TLS support'.freeze
34
35
 
35
36
  # Error message for timeouts on socket calls.
@@ -154,7 +155,14 @@ module Mongo
154
155
  #
155
156
  # @since 2.0.0
156
157
  def close
157
- @socket.close rescue nil
158
+ begin
159
+ # Sometimes it seems the close call can hang for a long time
160
+ ::Timeout.timeout(5) do
161
+ @socket.close
162
+ end
163
+ rescue
164
+ # Silence all errors
165
+ end
158
166
  true
159
167
  end
160
168
 
@@ -326,8 +334,21 @@ module Mongo
326
334
  else
327
335
  select_args = [nil, [@socket], [@socket], select_timeout]
328
336
  end
329
- unless Kernel::select(*select_args)
330
- raise Errno::ETIMEDOUT, "Took more than #{_timeout} seconds to receive data"
337
+ rv = Kernel.select(*select_args)
338
+ if BSON::Environment.jruby?
339
+ # Ignore the return value of Kernel.select.
340
+ # On JRuby, select appears to return nil prior to timeout expiration
341
+ # (apparently due to a EAGAIN) which then causes us to fail the read
342
+ # even though we could have retried it.
343
+ # Check the deadline ourselves.
344
+ if deadline
345
+ select_timeout = deadline - Time.now
346
+ if select_timeout <= 0
347
+ raise Errno::ETIMEDOUT, "Took more than #{_timeout} seconds to receive data"
348
+ end
349
+ end
350
+ elsif rv.nil?
351
+ raise Errno::ETIMEDOUT, "Took more than #{_timeout} seconds to receive data (select call timed out)"
331
352
  end
332
353
  retry
333
354
  end
@@ -398,6 +419,10 @@ module Mongo
398
419
  set_option(sock, :TCP_KEEPCNT, DEFAULT_TCP_KEEPCNT)
399
420
  set_option(sock, :TCP_KEEPIDLE, DEFAULT_TCP_KEEPIDLE)
400
421
  rescue
422
+ # JRuby 9.2.13.0 and lower do not define TCP_KEEPINTVL etc. constants.
423
+ # JRuby 9.2.14.0 defines the constants but does not allow to get or
424
+ # set them with this error:
425
+ # Errno::ENOPROTOOPT: Protocol not available - Protocol not available
401
426
  end
402
427
 
403
428
  def set_option(sock, option, default)
@@ -422,7 +447,7 @@ module Mongo
422
447
  rescue IOError, SystemCallError => e
423
448
  raise Error::SocketError, "#{e.class}: #{e} (for #{human_address})"
424
449
  rescue OpenSSL::SSL::SSLError => e
425
- raise Error::SocketError, "#{e.class}: #{e} (for #{human_address}) (#{SSL_ERROR})"
450
+ raise Error::SocketError, "#{e.class}: #{e} (for #{human_address})"
426
451
  end
427
452
  end
428
453
 
@@ -82,6 +82,44 @@ module Mongo
82
82
  end
83
83
  end
84
84
 
85
+ def smc_to_ruby(opts)
86
+ uri_options = {}
87
+
88
+ opts.each do |key, value|
89
+ strategy = URI_OPTION_MAP[key.downcase]
90
+ if strategy.nil?
91
+ log_warn("Unsupported URI option '#{key}' on URI '#{@string}'. It will be ignored.")
92
+ return
93
+ end
94
+
95
+ group = strategy[:group]
96
+ target = if group
97
+ uri_options[group] || {}
98
+ else
99
+ uri_options
100
+ end
101
+
102
+ if key == 'readConcernLevel'
103
+ value = value.to_sym
104
+ end
105
+
106
+ #value = apply_transform(key, value, strategy[:type])
107
+ # Sometimes the value here would be nil, for example if we are processing
108
+ # read preference tags or auth mechanism properties and all of the
109
+ # data within is invalid. Ignore such options.
110
+ unless value.nil?
111
+ merge_uri_option(target, value, strategy[:name])
112
+ end
113
+
114
+ if group && !target.empty? && !uri_options.key?(group)
115
+ uri_options[group] = target
116
+ end
117
+ end
118
+
119
+ #p uri_options
120
+ uri_options
121
+ end
122
+
85
123
  # Converts Ruby options provided to "standardized MongoClient options".
86
124
  #
87
125
  # @param [ Hash ] opts Ruby options to convert.
data/lib/mongo/utils.rb CHANGED
@@ -69,5 +69,20 @@ module Mongo
69
69
  module_function def camelize(sym)
70
70
  sym.to_s.gsub(/_(\w)/) { $1.upcase }
71
71
  end
72
+
73
+ # @note server_api must have symbol keys or be a BSON::Document.
74
+ module_function def transform_server_api(server_api)
75
+ {}.tap do |doc|
76
+ if version = server_api[:version]
77
+ doc['apiVersion'] = version
78
+ end
79
+ unless server_api[:strict].nil?
80
+ doc['apiStrict'] = server_api[:strict]
81
+ end
82
+ unless server_api[:deprecation_errors].nil?
83
+ doc['apiDeprecationErrors'] = server_api[:deprecation_errors]
84
+ end
85
+ end
86
+ end
72
87
  end
73
88
  end
data/lib/mongo/version.rb CHANGED
@@ -17,5 +17,5 @@ module Mongo
17
17
  # The current version of the driver.
18
18
  #
19
19
  # @since 2.0.0
20
- VERSION = '2.14.1'.freeze
20
+ VERSION = '2.15.0.alpha'.freeze
21
21
  end
data/lib/mongo.rb CHANGED
@@ -78,4 +78,27 @@ module Mongo
78
78
  module_function def clear_ocsp_cache
79
79
  Socket::OcspCache.clear
80
80
  end
81
+
82
+ # This is a user-settable list of hooks that will be invoked when any new
83
+ # TLS socket is connected. Each hook should be a Proc that takes
84
+ # an OpenSSL::SSL::SSLContext object as an argument. These hooks can be used
85
+ # to modify the TLS context (for example to disallow certain ciphers).
86
+ #
87
+ # @return [ Array<Proc> ] The list of procs to be invoked when a TLS socket
88
+ # is connected (may be an empty Array).
89
+ module_function def tls_context_hooks
90
+ @tls_context_hooks ||= []
91
+ end
92
+
93
+ # Set the TLS context hooks.
94
+ #
95
+ # @param [ Array<Proc> ] hooks An Array of Procs, each of which should take
96
+ # an OpenSSL::SSL::SSLContext object as an argument.
97
+ module_function def tls_context_hooks=(hooks)
98
+ unless hooks.is_a?(Array) && hooks.all? { |hook| hook.is_a?(Proc) }
99
+ raise ArgumentError, "TLS context hooks must be an array of Procs"
100
+ end
101
+
102
+ @tls_context_hooks = hooks
103
+ end
81
104
  end
data/spec/README.md CHANGED
@@ -512,13 +512,36 @@ To test compression, set the `compressors` URI option:
512
512
 
513
513
  MONGODB_URI="mongodb://localhost:27017/?compressors=zlib" rake
514
514
 
515
- Note that as of this writing, the driver only supports zlib compression.
515
+ Note that as of this writing, the driver supports
516
+ [ztsd](https://docs.mongodb.com/manual/reference/glossary/#term-zstd),
517
+ [snappy](https://docs.mongodb.com/manual/reference/glossary/#term-snappy)
518
+ and [zlib](https://docs.mongodb.com/manual/reference/glossary/#term-zlib)
519
+ compression.
520
+
516
521
  Servers 4.2+ enable zlib by default; to test older servers, explicitly enable
517
522
  zlib compression when launching the server:
518
523
 
519
524
  mongod --dbpath /tmp/mdb --setParameter enableTestCommands=1 \
520
525
  --networkMessageCompressors snappy,zlib
521
526
 
527
+ ## Server API
528
+
529
+ To specify server API parameters, use the `SERVER_API` environment variable.
530
+ The server API parameters cannot be specified via URI options.
531
+
532
+ Both YAML and JSON syntaxes are accepted:
533
+
534
+ SERVER_API='{version: "1", strict: true}' rake
535
+
536
+ SERVER_API='{"version":"1","strict":true}' rake
537
+
538
+ Note that the input must be valid YAML or JSON and the version number must
539
+ be a string, therefore all of the following specifications are invalid:
540
+
541
+ SERVER_API='{version:"1",strict:true}' rake
542
+ SERVER_API='{version: 1}' rake
543
+ SERVER_API='{"version":1,"strict":true}' rake
544
+
522
545
  ## Other Options
523
546
 
524
547
  Generally, all URI options recognized by the driver may be set for a test run,
@@ -10,8 +10,12 @@ describe 'Auth' do
10
10
  authorized_client.cluster.next_primary
11
11
  end
12
12
 
13
+ let(:base_options) do
14
+ SpecConfig.instance.monitoring_options
15
+ end
16
+
13
17
  let(:connection) do
14
- Mongo::Server::Connection.new(server, options)
18
+ Mongo::Server::Connection.new(server, base_options.merge(options))
15
19
  end
16
20
 
17
21
  before(:all) do
@@ -31,8 +35,9 @@ describe 'Auth' do
31
35
  context 'user mechanism not provided' do
32
36
 
33
37
  context 'user does not exist' do
34
- let(:options) { SpecConfig.instance.ssl_options.merge(
35
- user: 'nonexistent_user') }
38
+ let(:options) do
39
+ {user: 'nonexistent_user' }
40
+ end
36
41
 
37
42
  before do
38
43
  expect(connection.app_metadata.send(:document)[:saslSupportedMechs]).to eq('admin.nonexistent_user')
@@ -63,8 +68,9 @@ describe 'Auth' do
63
68
  end
64
69
 
65
70
  context 'user exists' do
66
- let(:options) { SpecConfig.instance.ssl_options.merge(
67
- user: 'existing_user', password: 'bogus') }
71
+ let(:options) do
72
+ {user: 'existing_user', password: 'bogus'}
73
+ end
68
74
 
69
75
  before do
70
76
  expect(connection.app_metadata.send(:document)[:saslSupportedMechs]).to eq("admin.existing_user")
@@ -99,8 +105,9 @@ describe 'Auth' do
99
105
  min_server_fcv '3.0'
100
106
 
101
107
  context 'scram-sha-1 requested' do
102
- let(:options) { SpecConfig.instance.ssl_options.merge(
103
- user: 'nonexistent_user', auth_mech: :scram) }
108
+ let(:options) do
109
+ {user: 'nonexistent_user', auth_mech: :scram}
110
+ end
104
111
 
105
112
  it 'indicates scram-sha-1 was requested and used' do
106
113
  expect do
@@ -112,8 +119,9 @@ describe 'Auth' do
112
119
  context 'scram-sha-256 requested' do
113
120
  min_server_fcv '4.0'
114
121
 
115
- let(:options) { SpecConfig.instance.ssl_options.merge(
116
- user: 'nonexistent_user', auth_mech: :scram256) }
122
+ let(:options) do
123
+ {user: 'nonexistent_user', auth_mech: :scram256}
124
+ end
117
125
 
118
126
  it 'indicates scram-sha-256 was requested and used' do
119
127
  expect do
@@ -124,8 +132,9 @@ describe 'Auth' do
124
132
  end
125
133
 
126
134
  context 'when authentication fails' do
127
- let(:options) { SpecConfig.instance.ssl_options.merge(
128
- user: 'nonexistent_user', password: 'foo') }
135
+ let(:options) do
136
+ {user: 'nonexistent_user', password: 'foo'}
137
+ end
129
138
 
130
139
  it 'reports which server authentication was attempted against' do
131
140
  expect do
@@ -142,8 +151,9 @@ describe 'Auth' do
142
151
  end
143
152
 
144
153
  context 'with custom auth source' do
145
- let(:options) { SpecConfig.instance.ssl_options.merge(
146
- user: 'nonexistent_user', password: 'foo', auth_source: 'authdb') }
154
+ let(:options) do
155
+ {user: 'nonexistent_user', password: 'foo', auth_source: 'authdb'}
156
+ end
147
157
 
148
158
  it 'reports auth source used' do
149
159
  expect do
@@ -240,7 +250,7 @@ describe 'Auth' do
240
250
  require_no_auth
241
251
 
242
252
  let(:client) do
243
- new_local_client(SpecConfig.instance.addresses, SpecConfig.instance.ssl_options.merge(
253
+ new_local_client(SpecConfig.instance.addresses, SpecConfig.instance.monitoring_options.merge(
244
254
  auth_source: 'foo'))
245
255
  end
246
256
 
@@ -254,7 +264,7 @@ describe 'Auth' do
254
264
  require_x509_auth
255
265
 
256
266
  let(:client) do
257
- new_local_client(SpecConfig.instance.addresses, SpecConfig.instance.ssl_options.merge(
267
+ new_local_client(SpecConfig.instance.addresses, base_options.merge(
258
268
  auth_mech: :mongodb_x509))
259
269
  end
260
270
 
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'BulkWriteError message' do
4
+ let(:client) { authorized_client }
5
+ let(:collection_name) { 'bulk_write_error_message_spec' }
6
+ let(:collection) { client[collection_name] }
7
+
8
+ before do
9
+ collection.delete_many
10
+ end
11
+
12
+ context 'a bulk write with one error' do
13
+ it 'reports code name, code and message' do
14
+ begin
15
+ collection.insert_many([
16
+ {_id: 1},
17
+ {_id: 1},
18
+ {_id: 1},
19
+ ], ordered: true)
20
+ fail('Should have raised')
21
+ rescue Mongo::Error::BulkWriteError => e
22
+ e.message.should =~ %r,\A\[11000\]: (insertDocument :: caused by :: 11000 )?E11000 duplicate key error (collection|index):,
23
+ end
24
+ end
25
+ end
26
+
27
+ context 'a bulk write with multiple errors' do
28
+ it 'reports code name, code and message' do
29
+ begin
30
+ collection.insert_many([
31
+ {_id: 1},
32
+ {_id: 1},
33
+ {_id: 1},
34
+ ], ordered: false)
35
+ fail('Should have raised')
36
+ rescue Mongo::Error::BulkWriteError => e
37
+ e.message.should =~ %r,\AMultiple errors: \[11000\]: (insertDocument :: caused by :: 11000 )?E11000 duplicate key error (collection|index):.*\[11000\]: (insertDocument :: caused by :: 11000 )?E11000 duplicate key error (collection|index):,
38
+ end
39
+ end
40
+ end
41
+ end
@@ -98,7 +98,7 @@ describe 'Change stream integration', retry: 4 do
98
98
  it 'watch raises error' do
99
99
  expect do
100
100
  client['change-stream'].watch
101
- end.to raise_error(Mongo::Error::OperationFailure, /Failing command due to 'failCommand' failpoint \(10107\)/)
101
+ end.to raise_error(Mongo::Error::OperationFailure, /10107\b.*Failing command due to 'failCommand' failpoint/)
102
102
  end
103
103
  end
104
104
 
@@ -283,7 +283,7 @@ describe 'Change stream integration', retry: 4 do
283
283
 
284
284
  expect do
285
285
  enum.next
286
- end.to raise_error(Mongo::Error::OperationFailure, /Failing command due to 'failCommand' failpoint \(101\)/)
286
+ end.to raise_error(Mongo::Error::OperationFailure, /101\b.*Failing command due to 'failCommand' failpoint/)
287
287
  end
288
288
  end
289
289
  end
@@ -414,7 +414,7 @@ describe 'Change stream integration', retry: 4 do
414
414
 
415
415
  expect do
416
416
  enum.try_next
417
- end.to raise_error(Mongo::Error::OperationFailure, /Failing command due to 'failCommand' failpoint \(10107\)/)
417
+ end.to raise_error(Mongo::Error::OperationFailure, /10107\b.*Failing command due to 'failCommand' failpoint/)
418
418
  end
419
419
  end
420
420
 
@@ -441,7 +441,7 @@ describe 'Change stream integration', retry: 4 do
441
441
 
442
442
  expect do
443
443
  enum.try_next
444
- end.to raise_error(Mongo::Error::OperationFailure, /Failing command due to 'failCommand' failpoint \(10107\)/)
444
+ end.to raise_error(Mongo::Error::OperationFailure, /10107\b.*Failing command due to 'failCommand' failpoint/)
445
445
  end
446
446
  end
447
447
  end
@@ -135,8 +135,8 @@ describe 'Command monitoring' do
135
135
 
136
136
  subscriber.clear_events!
137
137
  expect do
138
- command.execute(server, client: nil)
139
- end.to raise_error(Mongo::Error::OperationFailure, /Not enough data-bearing nodes \(100\)/)
138
+ command.execute(server, context: Mongo::Operation::Context.new(session: session))
139
+ end.to raise_error(Mongo::Error::OperationFailure, /100\b.*Not enough data-bearing nodes/)
140
140
 
141
141
  expect(subscriber.started_events.length).to eq(1)
142
142
  event = subscriber.started_events.first
@@ -232,6 +232,8 @@ describe 'Connections' do
232
232
  end
233
233
 
234
234
  describe 'wire protocol version range update' do
235
+ require_no_required_api_version
236
+
235
237
  # 3.2 wire protocol is 4.
236
238
  # Wire protocol < 2 means only scram auth is available,
237
239
  # which is not supported by modern mongos.