mongo 2.11.6 → 2.12.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (327) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -2
  3. data.tar.gz.sig +0 -0
  4. data/CONTRIBUTING.md +1 -1
  5. data/lib/mongo.rb +3 -0
  6. data/lib/mongo/address.rb +13 -2
  7. data/lib/mongo/auth.rb +1 -0
  8. data/lib/mongo/auth/credential_cache.rb +51 -0
  9. data/lib/mongo/auth/scram/conversation.rb +20 -16
  10. data/lib/mongo/auth/user.rb +0 -8
  11. data/lib/mongo/auth/user/view.rb +4 -4
  12. data/lib/mongo/background_thread.rb +1 -1
  13. data/lib/mongo/bulk_write.rb +5 -5
  14. data/lib/mongo/client.rb +126 -11
  15. data/lib/mongo/client_encryption.rb +103 -0
  16. data/lib/mongo/cluster.rb +2 -2
  17. data/lib/mongo/cluster/reapers/cursor_reaper.rb +18 -6
  18. data/lib/mongo/cluster/sdam_flow.rb +54 -58
  19. data/lib/mongo/cluster/srv_monitor.rb +1 -1
  20. data/lib/mongo/collection.rb +3 -3
  21. data/lib/mongo/collection/view.rb +1 -1
  22. data/lib/mongo/collection/view/aggregation.rb +1 -1
  23. data/lib/mongo/collection/view/change_stream.rb +12 -3
  24. data/lib/mongo/collection/view/iterable.rb +14 -5
  25. data/lib/mongo/collection/view/map_reduce.rb +2 -2
  26. data/lib/mongo/collection/view/readable.rb +7 -9
  27. data/lib/mongo/collection/view/writable.rb +7 -7
  28. data/lib/mongo/crypt.rb +33 -0
  29. data/lib/mongo/crypt/auto_decryption_context.rb +42 -0
  30. data/lib/mongo/crypt/auto_encrypter.rb +169 -0
  31. data/lib/mongo/crypt/auto_encryption_context.rb +44 -0
  32. data/lib/mongo/crypt/binary.rb +155 -0
  33. data/lib/mongo/crypt/binding.rb +1162 -0
  34. data/lib/mongo/crypt/context.rb +135 -0
  35. data/lib/mongo/crypt/data_key_context.rb +162 -0
  36. data/lib/mongo/crypt/encryption_io.rb +283 -0
  37. data/lib/mongo/crypt/explicit_decryption_context.rb +40 -0
  38. data/lib/mongo/crypt/explicit_encrypter.rb +117 -0
  39. data/lib/mongo/crypt/explicit_encryption_context.rb +89 -0
  40. data/lib/mongo/crypt/handle.rb +293 -0
  41. data/lib/mongo/crypt/hooks.rb +90 -0
  42. data/lib/mongo/crypt/kms_context.rb +67 -0
  43. data/lib/mongo/crypt/status.rb +131 -0
  44. data/lib/mongo/cursor.rb +64 -32
  45. data/lib/mongo/database.rb +13 -6
  46. data/lib/mongo/database/view.rb +13 -4
  47. data/lib/mongo/dbref.rb +9 -2
  48. data/lib/mongo/error.rb +5 -1
  49. data/lib/mongo/error/crypt_error.rb +31 -0
  50. data/lib/mongo/error/{failed_stringprep_validation.rb → failed_string_prep_validation.rb} +0 -0
  51. data/lib/mongo/error/invalid_cursor_operation.rb +27 -0
  52. data/lib/mongo/error/kms_error.rb +22 -0
  53. data/lib/mongo/error/max_bson_size.rb +14 -3
  54. data/lib/mongo/error/mongocryptd_spawn_error.rb +22 -0
  55. data/lib/mongo/error/no_server_available.rb +8 -3
  56. data/lib/mongo/error/operation_failure.rb +1 -0
  57. data/lib/mongo/grid/file.rb +0 -5
  58. data/lib/mongo/grid/file/chunk.rb +0 -2
  59. data/lib/mongo/grid/file/info.rb +2 -1
  60. data/lib/mongo/grid/fs_bucket.rb +13 -15
  61. data/lib/mongo/grid/stream/write.rb +3 -9
  62. data/lib/mongo/index/view.rb +3 -3
  63. data/lib/mongo/monitoring/event/command_started.rb +6 -1
  64. data/lib/mongo/operation/collections_info.rb +6 -3
  65. data/lib/mongo/operation/delete/op_msg.rb +1 -1
  66. data/lib/mongo/operation/find/op_msg.rb +4 -1
  67. data/lib/mongo/operation/get_more/op_msg.rb +4 -1
  68. data/lib/mongo/operation/insert/command.rb +2 -2
  69. data/lib/mongo/operation/insert/legacy.rb +2 -2
  70. data/lib/mongo/operation/insert/op_msg.rb +3 -3
  71. data/lib/mongo/operation/result.rb +36 -27
  72. data/lib/mongo/operation/shared/executable.rb +10 -8
  73. data/lib/mongo/operation/shared/executable_no_validate.rb +2 -2
  74. data/lib/mongo/operation/shared/op_msg_or_command.rb +2 -2
  75. data/lib/mongo/operation/shared/op_msg_or_find_command.rb +2 -2
  76. data/lib/mongo/operation/shared/op_msg_or_list_indexes_command.rb +2 -2
  77. data/lib/mongo/operation/shared/write.rb +17 -10
  78. data/lib/mongo/operation/update/op_msg.rb +1 -1
  79. data/lib/mongo/protocol/compressed.rb +6 -5
  80. data/lib/mongo/protocol/insert.rb +3 -1
  81. data/lib/mongo/protocol/message.rb +72 -8
  82. data/lib/mongo/protocol/msg.rb +191 -37
  83. data/lib/mongo/protocol/query.rb +7 -9
  84. data/lib/mongo/protocol/serializers.rb +6 -2
  85. data/lib/mongo/server.rb +10 -4
  86. data/lib/mongo/server/connection.rb +20 -9
  87. data/lib/mongo/server/connection_base.rb +81 -12
  88. data/lib/mongo/server/connection_common.rb +61 -0
  89. data/lib/mongo/server/connection_pool.rb +37 -1
  90. data/lib/mongo/server/description.rb +9 -11
  91. data/lib/mongo/server/monitor.rb +2 -0
  92. data/lib/mongo/server/monitor/connection.rb +3 -18
  93. data/lib/mongo/server/pending_connection.rb +2 -1
  94. data/lib/mongo/session.rb +2 -2
  95. data/lib/mongo/session/session_pool.rb +8 -3
  96. data/lib/mongo/socket.rb +29 -16
  97. data/lib/mongo/socket/ssl.rb +23 -8
  98. data/lib/mongo/socket/tcp.rb +12 -3
  99. data/lib/mongo/timeout.rb +49 -0
  100. data/lib/mongo/uri.rb +30 -1
  101. data/lib/mongo/version.rb +1 -1
  102. data/mongo.gemspec +1 -1
  103. data/spec/README.md +134 -7
  104. data/spec/integration/auth_spec.rb +53 -0
  105. data/spec/integration/{client_options_spec.rb → client_authentication_options_spec.rb} +10 -10
  106. data/spec/integration/client_construction_spec.rb +76 -1
  107. data/spec/integration/client_side_encryption/auto_encryption_bulk_writes_spec.rb +351 -0
  108. data/spec/integration/client_side_encryption/auto_encryption_command_monitoring_spec.rb +301 -0
  109. data/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb +71 -0
  110. data/spec/integration/client_side_encryption/auto_encryption_old_wire_version_spec.rb +76 -0
  111. data/spec/integration/client_side_encryption/auto_encryption_reconnect_spec.rb +216 -0
  112. data/spec/integration/client_side_encryption/auto_encryption_spec.rb +600 -0
  113. data/spec/integration/client_side_encryption/bson_size_limit_spec.rb +183 -0
  114. data/spec/integration/client_side_encryption/bypass_mongocryptd_spawn_spec.rb +74 -0
  115. data/spec/integration/client_side_encryption/client_close_spec.rb +59 -0
  116. data/spec/integration/client_side_encryption/corpus_spec.rb +228 -0
  117. data/spec/integration/client_side_encryption/custom_endpoint_spec.rb +132 -0
  118. data/spec/integration/client_side_encryption/data_key_spec.rb +163 -0
  119. data/spec/integration/client_side_encryption/explicit_encryption_spec.rb +114 -0
  120. data/spec/integration/client_side_encryption/external_key_vault_spec.rb +137 -0
  121. data/spec/integration/client_side_encryption/views_spec.rb +42 -0
  122. data/spec/integration/client_update_spec.rb +120 -0
  123. data/spec/integration/command_monitoring_spec.rb +3 -1
  124. data/spec/integration/command_spec.rb +44 -10
  125. data/spec/integration/connection_spec.rb +57 -0
  126. data/spec/integration/reconnect_spec.rb +7 -6
  127. data/spec/integration/size_limit_spec.rb +94 -0
  128. data/spec/integration/srv_monitoring_spec.rb +14 -6
  129. data/spec/lite_spec_helper.rb +31 -22
  130. data/spec/mongo/auth/cr_spec.rb +8 -0
  131. data/spec/mongo/auth/ldap_spec.rb +5 -1
  132. data/spec/mongo/auth/scram/conversation_spec.rb +5 -6
  133. data/spec/mongo/auth/scram/negotiation_spec.rb +74 -75
  134. data/spec/mongo/auth/scram_spec.rb +45 -35
  135. data/spec/mongo/auth/x509_spec.rb +5 -1
  136. data/spec/mongo/client_construction_spec.rb +206 -3
  137. data/spec/mongo/client_encryption_spec.rb +408 -0
  138. data/spec/mongo/cluster/cursor_reaper_spec.rb +12 -8
  139. data/spec/mongo/cluster/socket_reaper_spec.rb +14 -3
  140. data/spec/mongo/collection/view/aggregation_spec.rb +0 -2
  141. data/spec/mongo/collection/view/change_stream_spec.rb +7 -7
  142. data/spec/mongo/collection/view/map_reduce_spec.rb +3 -3
  143. data/spec/mongo/collection/view_spec.rb +1 -1
  144. data/spec/mongo/collection_spec.rb +4 -33
  145. data/spec/mongo/crypt/auto_decryption_context_spec.rb +90 -0
  146. data/spec/mongo/crypt/auto_encrypter_spec.rb +182 -0
  147. data/spec/mongo/crypt/auto_encryption_context_spec.rb +107 -0
  148. data/spec/mongo/crypt/binary_spec.rb +115 -0
  149. data/spec/mongo/crypt/binding/binary_spec.rb +56 -0
  150. data/spec/mongo/crypt/binding/context_spec.rb +257 -0
  151. data/spec/mongo/crypt/binding/helpers_spec.rb +46 -0
  152. data/spec/mongo/crypt/binding/mongocrypt_spec.rb +144 -0
  153. data/spec/mongo/crypt/binding/status_spec.rb +99 -0
  154. data/spec/mongo/crypt/binding/version_spec.rb +22 -0
  155. data/spec/mongo/crypt/binding_unloaded_spec.rb +20 -0
  156. data/spec/mongo/crypt/data_key_context_spec.rb +213 -0
  157. data/spec/mongo/crypt/encryption_io_spec.rb +136 -0
  158. data/spec/mongo/crypt/explicit_decryption_context_spec.rb +72 -0
  159. data/spec/mongo/crypt/explicit_encryption_context_spec.rb +170 -0
  160. data/spec/mongo/crypt/handle_spec.rb +198 -0
  161. data/spec/mongo/crypt/helpers/mongo_crypt_spec_helper.rb +108 -0
  162. data/spec/mongo/crypt/status_spec.rb +152 -0
  163. data/spec/mongo/cursor_spec.rb +24 -4
  164. data/spec/mongo/database_spec.rb +20 -0
  165. data/spec/mongo/error/crypt_error_spec.rb +26 -0
  166. data/spec/mongo/error/max_bson_size_spec.rb +35 -0
  167. data/spec/mongo/error/no_server_available_spec.rb +11 -1
  168. data/spec/mongo/error/operation_failure_spec.rb +6 -6
  169. data/spec/mongo/operation/aggregate_spec.rb +1 -1
  170. data/spec/mongo/operation/collections_info_spec.rb +1 -1
  171. data/spec/mongo/operation/command_spec.rb +3 -3
  172. data/spec/mongo/operation/create_index_spec.rb +3 -3
  173. data/spec/mongo/operation/create_user_spec.rb +3 -3
  174. data/spec/mongo/operation/delete/bulk_spec.rb +6 -6
  175. data/spec/mongo/operation/delete/op_msg_spec.rb +1 -6
  176. data/spec/mongo/operation/delete_spec.rb +7 -7
  177. data/spec/mongo/operation/drop_index_spec.rb +2 -2
  178. data/spec/mongo/operation/find/legacy_spec.rb +1 -1
  179. data/spec/mongo/operation/get_more_spec.rb +1 -1
  180. data/spec/mongo/operation/indexes_spec.rb +1 -1
  181. data/spec/mongo/operation/insert/bulk_spec.rb +7 -7
  182. data/spec/mongo/operation/insert/op_msg_spec.rb +3 -6
  183. data/spec/mongo/operation/insert_spec.rb +12 -12
  184. data/spec/mongo/operation/map_reduce_spec.rb +2 -2
  185. data/spec/mongo/operation/remove_user_spec.rb +3 -3
  186. data/spec/mongo/operation/update/bulk_spec.rb +6 -6
  187. data/spec/mongo/operation/update/op_msg_spec.rb +3 -6
  188. data/spec/mongo/operation/update_spec.rb +7 -7
  189. data/spec/mongo/operation/update_user_spec.rb +1 -1
  190. data/spec/mongo/protocol/compressed_spec.rb +2 -3
  191. data/spec/mongo/protocol/delete_spec.rb +9 -8
  192. data/spec/mongo/protocol/get_more_spec.rb +9 -8
  193. data/spec/mongo/protocol/insert_spec.rb +9 -8
  194. data/spec/mongo/protocol/kill_cursors_spec.rb +6 -5
  195. data/spec/mongo/protocol/msg_spec.rb +57 -53
  196. data/spec/mongo/protocol/query_spec.rb +12 -12
  197. data/spec/mongo/protocol/registry_spec.rb +1 -1
  198. data/spec/mongo/protocol/reply_spec.rb +1 -1
  199. data/spec/mongo/protocol/update_spec.rb +10 -9
  200. data/spec/mongo/server/connection_pool_spec.rb +1 -1
  201. data/spec/mongo/server/connection_spec.rb +28 -7
  202. data/spec/mongo/socket_spec.rb +1 -1
  203. data/spec/mongo/timeout_spec.rb +85 -0
  204. data/spec/mongo/uri/srv_protocol_spec.rb +2 -2
  205. data/spec/mongo/uri_spec.rb +52 -5
  206. data/spec/mongo/write_concern_spec.rb +13 -1
  207. data/spec/{support → runners}/auth.rb +14 -1
  208. data/spec/{support → runners}/change_streams.rb +1 -1
  209. data/spec/{support → runners}/change_streams/operation.rb +0 -0
  210. data/spec/{support → runners}/cmap.rb +1 -1
  211. data/spec/{support → runners}/cmap/verifier.rb +0 -0
  212. data/spec/{support → runners}/command_monitoring.rb +0 -0
  213. data/spec/runners/connection_string.rb +358 -4
  214. data/spec/{support → runners}/crud.rb +9 -9
  215. data/spec/{support → runners}/crud/context.rb +0 -0
  216. data/spec/{support → runners}/crud/operation.rb +7 -3
  217. data/spec/{support → runners}/crud/outcome.rb +0 -0
  218. data/spec/{support → runners}/crud/requirement.rb +1 -1
  219. data/spec/{support → runners}/crud/spec.rb +12 -1
  220. data/spec/{support → runners}/crud/test.rb +0 -0
  221. data/spec/{support → runners}/crud/test_base.rb +0 -0
  222. data/spec/{support → runners}/crud/verifier.rb +10 -12
  223. data/spec/{support → runners}/gridfs.rb +0 -0
  224. data/spec/{support → runners}/sdam_monitoring.rb +0 -0
  225. data/spec/{support → runners}/server_discovery_and_monitoring.rb +0 -0
  226. data/spec/{support → runners}/server_selection.rb +0 -0
  227. data/spec/{support → runners}/server_selection_rtt.rb +0 -0
  228. data/spec/{support → runners}/transactions.rb +4 -4
  229. data/spec/{support → runners}/transactions/context.rb +0 -0
  230. data/spec/{support → runners}/transactions/operation.rb +0 -0
  231. data/spec/{support → runners}/transactions/spec.rb +0 -0
  232. data/spec/{support → runners}/transactions/test.rb +37 -5
  233. data/spec/spec_helper.rb +0 -5
  234. data/spec/spec_tests/auth_spec.rb +3 -3
  235. data/spec/spec_tests/client_side_encryption_spec.rb +13 -0
  236. data/spec/spec_tests/connection_string_spec.rb +1 -1
  237. data/spec/spec_tests/data/auth/connection-string.yml +13 -0
  238. data/spec/spec_tests/data/client_side_encryption/aggregate.yml +134 -0
  239. data/spec/spec_tests/data/client_side_encryption/badQueries.yml +526 -0
  240. data/spec/spec_tests/data/client_side_encryption/badSchema.yml +73 -0
  241. data/spec/spec_tests/data/client_side_encryption/basic.yml +116 -0
  242. data/spec/spec_tests/data/client_side_encryption/bulk.yml +85 -0
  243. data/spec/spec_tests/data/client_side_encryption/bypassAutoEncryption.yml +100 -0
  244. data/spec/spec_tests/data/client_side_encryption/bypassedCommand.yml +42 -0
  245. data/spec/spec_tests/data/client_side_encryption/count.yml +61 -0
  246. data/spec/spec_tests/data/client_side_encryption/countDocuments.yml +59 -0
  247. data/spec/spec_tests/data/client_side_encryption/delete.yml +105 -0
  248. data/spec/spec_tests/data/client_side_encryption/distinct.yml +73 -0
  249. data/spec/spec_tests/data/client_side_encryption/explain.yml +64 -0
  250. data/spec/spec_tests/data/client_side_encryption/find.yml +119 -0
  251. data/spec/spec_tests/data/client_side_encryption/findOneAndDelete.yml +57 -0
  252. data/spec/spec_tests/data/client_side_encryption/findOneAndReplace.yml +57 -0
  253. data/spec/spec_tests/data/client_side_encryption/findOneAndUpdate.yml +57 -0
  254. data/spec/spec_tests/data/client_side_encryption/getMore.yml +68 -0
  255. data/spec/spec_tests/data/client_side_encryption/insert.yml +102 -0
  256. data/spec/spec_tests/data/client_side_encryption/keyAltName.yml +71 -0
  257. data/spec/spec_tests/data/client_side_encryption/localKMS.yml +54 -0
  258. data/spec/spec_tests/data/client_side_encryption/localSchema.yml +72 -0
  259. data/spec/spec_tests/data/client_side_encryption/malformedCiphertext.yml +69 -0
  260. data/spec/spec_tests/data/client_side_encryption/maxWireVersion.yml +20 -0
  261. data/spec/spec_tests/data/client_side_encryption/missingKey.yml +49 -0
  262. data/spec/spec_tests/data/client_side_encryption/replaceOne.yml +61 -0
  263. data/spec/spec_tests/data/client_side_encryption/types.yml +527 -0
  264. data/spec/spec_tests/data/client_side_encryption/unsupportedCommand.yml +25 -0
  265. data/spec/spec_tests/data/client_side_encryption/updateMany.yml +77 -0
  266. data/spec/spec_tests/data/client_side_encryption/updateOne.yml +168 -0
  267. data/spec/spec_tests/data/read_write_concern/connection-string/write-concern.yml +1 -4
  268. data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +21 -0
  269. data/spec/spec_tests/data/sdam/rs/incompatible_ghost.yml +2 -4
  270. data/spec/spec_tests/data/sdam/rs/incompatible_other.yml +1 -1
  271. data/spec/spec_tests/data/sdam/rs/primary_mismatched_me_not_removed.yml +73 -0
  272. data/spec/spec_tests/data/sdam/rs/primary_to_no_primary_mismatched_me.yml +1 -2
  273. data/spec/spec_tests/data/sdam/rs/repeated.yml +101 -0
  274. data/spec/spec_tests/data/sdam/rs/{primary_address_change.yml → ruby_primary_address_change.yml} +2 -0
  275. data/spec/spec_tests/data/sdam/rs/{secondary_wrong_set_name_with_primary_second.yml → ruby_secondary_wrong_set_name_with_primary_second.yml} +0 -0
  276. data/spec/spec_tests/data/sdam/sharded/ruby_discovered_single_mongos.yml +27 -0
  277. data/spec/spec_tests/data/sdam/sharded/{primary_address_change.yml → ruby_primary_different_address.yml} +1 -1
  278. data/spec/spec_tests/data/sdam/sharded/{primary_mismatched_me.yml → ruby_primary_mismatched_me.yml} +1 -1
  279. data/spec/spec_tests/data/sdam/single/{primary_address_change.yml → ruby_primary_different_address.yml} +1 -1
  280. data/spec/spec_tests/data/sdam/single/{primary_mismatched_me.yml → ruby_primary_mismatched_me.yml} +1 -1
  281. data/spec/spec_tests/data/sdam_monitoring/{replica_set_with_primary_change.yml → replica_set_primary_address_change.yml} +27 -5
  282. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_me_mismatch.yml +26 -74
  283. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_removal.yml +20 -16
  284. data/spec/spec_tests/data/sdam_monitoring/standalone_suppress_equal_description_changes.yml +73 -0
  285. data/spec/spec_tests/data/transactions/pin-mongos.yml +2 -3
  286. data/spec/spec_tests/data/uri_options/auth-options.yml +10 -0
  287. data/spec/spec_tests/data/uri_options/tls-options.yml +75 -4
  288. data/spec/spec_tests/read_write_concern_connection_string_spec.rb +1 -1
  289. data/spec/spec_tests/uri_options_spec.rb +6 -8
  290. data/spec/stress/connection_pool_timing_spec.rb +6 -3
  291. data/spec/support/certificates/README.md +4 -0
  292. data/spec/support/certificates/server-second-level-bundle.pem +77 -77
  293. data/spec/support/certificates/server-second-level.crt +52 -52
  294. data/spec/support/certificates/server-second-level.key +25 -25
  295. data/spec/support/certificates/server-second-level.pem +77 -77
  296. data/spec/support/client_registry.rb +19 -3
  297. data/spec/support/cluster_config.rb +9 -1
  298. data/spec/support/common_shortcuts.rb +12 -0
  299. data/spec/support/constraints.rb +16 -0
  300. data/spec/support/crypt.rb +140 -0
  301. data/spec/support/crypt/corpus/corpus-key-aws.json +33 -0
  302. data/spec/support/crypt/corpus/corpus-key-local.json +31 -0
  303. data/spec/support/crypt/corpus/corpus-schema.json +2057 -0
  304. data/spec/support/crypt/corpus/corpus.json +3657 -0
  305. data/spec/support/crypt/corpus/corpus_encrypted.json +4152 -0
  306. data/spec/support/crypt/data_keys/key_document_aws.json +34 -0
  307. data/spec/support/crypt/data_keys/key_document_local.json +31 -0
  308. data/spec/support/crypt/external/external-key.json +31 -0
  309. data/spec/support/crypt/external/external-schema.json +19 -0
  310. data/spec/support/crypt/limits/limits-doc.json +102 -0
  311. data/spec/support/crypt/limits/limits-key.json +31 -0
  312. data/spec/support/crypt/limits/limits-schema.json +1405 -0
  313. data/spec/support/crypt/schema_maps/schema_map_aws.json +17 -0
  314. data/spec/support/crypt/schema_maps/schema_map_aws_key_alt_names.json +12 -0
  315. data/spec/support/crypt/schema_maps/schema_map_local.json +18 -0
  316. data/spec/support/crypt/schema_maps/schema_map_local_key_alt_names.json +12 -0
  317. data/spec/support/lite_constraints.rb +17 -1
  318. data/spec/support/matchers.rb +19 -0
  319. data/spec/support/shared/protocol.rb +2 -0
  320. data/spec/support/spec_config.rb +43 -13
  321. data/spec/support/utils.rb +132 -10
  322. metadata +277 -81
  323. metadata.gz.sig +0 -0
  324. data/spec/integration/grid_fs_bucket_spec.rb +0 -48
  325. data/spec/integration/zlib_compression_spec.rb +0 -25
  326. data/spec/spec_tests/data/sdam/sharded/single_mongos.yml +0 -33
  327. data/spec/support/connection_string.rb +0 -354
@@ -94,6 +94,8 @@ module Mongo
94
94
 
95
95
  # The compressor is determined during the handshake, so it must be an attribute
96
96
  # of the connection.
97
+ #
98
+ # @deprecated
97
99
  def_delegators :connection, :compressor
98
100
 
99
101
  # @return [ Monitoring ] monitoring The monitoring.
@@ -19,7 +19,7 @@ module Mongo
19
19
  # This class models the monitor connections and their behavior.
20
20
  #
21
21
  # @since 2.0.0
22
- class Connection
22
+ class Connection < Server::ConnectionCommon
23
23
  include Retryable
24
24
  include Connectable
25
25
  include Loggable
@@ -64,12 +64,14 @@ module Mongo
64
64
  # Key for compression algorithms in the response from the server during handshake.
65
65
  #
66
66
  # @since 2.5.0
67
+ # @deprecated
67
68
  COMPRESSION = 'compression'.freeze
68
69
 
69
70
  # Warning message that the server has no compression algorithms in common with those requested
70
71
  # by the client.
71
72
  #
72
73
  # @since 2.5.0
74
+ # @deprecated
73
75
  COMPRESSION_WARNING = 'The server has no compression algorithms in common with those requested. ' +
74
76
  'Compression will not be used.'.freeze
75
77
 
@@ -125,11 +127,6 @@ module Mongo
125
127
  # @return [ Mongo::Address ] address The address to connect to.
126
128
  attr_reader :address
127
129
 
128
- # The compressor, which is determined during the handshake.
129
- #
130
- # @since 2.5.0
131
- attr_reader :compressor
132
-
133
130
  # Send the preserialized ismaster call.
134
131
  #
135
132
  # @example Send a preserialized ismaster message.
@@ -209,18 +206,6 @@ module Mongo
209
206
 
210
207
  private
211
208
 
212
- def set_compressor!(reply)
213
- server_compressors = reply[COMPRESSION]
214
-
215
- if options[:compressors]
216
- if intersection = (server_compressors & options[:compressors])
217
- @compressor = intersection[0]
218
- else
219
- log_warn(COMPRESSION_WARNING)
220
- end
221
- end
222
- end
223
-
224
209
  def handshake!(socket)
225
210
  if @app_metadata
226
211
  socket.write(@app_metadata.ismaster_bytes)
@@ -21,8 +21,9 @@ module Mongo
21
21
  class PendingConnection < ConnectionBase
22
22
  extend Forwardable
23
23
 
24
- def initialize(socket, server, monitoring, options = {})
24
+ def initialize(socket, description, server, monitoring, options = {})
25
25
  @socket = socket
26
+ @description = description
26
27
  @options = options
27
28
  @server = server
28
29
  @monitoring = monitoring
@@ -578,7 +578,7 @@ module Mongo
578
578
  txn_num: txn_num,
579
579
  write_concern: write_concern,
580
580
  }
581
- Operation::Command.new(spec).execute(server)
581
+ Operation::Command.new(spec).execute(server, client: @client)
582
582
  end
583
583
  end
584
584
  ensure
@@ -622,7 +622,7 @@ module Mongo
622
622
  db_name: 'admin',
623
623
  session: self,
624
624
  txn_num: txn_num
625
- ).execute(server)
625
+ ).execute(server, client: @client)
626
626
  end
627
627
  end
628
628
 
@@ -113,9 +113,14 @@ module Mongo
113
113
  def end_sessions
114
114
  while !@queue.empty?
115
115
  server = ServerSelector.get(mode: :primary_preferred).select_server(@cluster)
116
- Operation::Command.new(
117
- :selector => {endSessions: @queue.shift(10_000).collect { |s| s.session_id }},
118
- :db_name => Database::ADMIN).execute(server)
116
+ op = Operation::Command.new(
117
+ selector: {
118
+ endSessions: @queue.shift(10_000).map(&:session_id),
119
+ },
120
+ db_name: Database::ADMIN,
121
+ )
122
+ # end_sessions does not take a client as an argument
123
+ op.execute(server, client: nil)
119
124
  end
120
125
  rescue Mongo::Error, Error::AuthError
121
126
  end
@@ -40,6 +40,11 @@ module Mongo
40
40
  # @since 2.0.0
41
41
  TIMEOUT_PACK = 'l_2'.freeze
42
42
 
43
+ # Write data to the socket in chunks of this size.
44
+ #
45
+ # @api private
46
+ WRITE_CHUNK_SIZE = 65536
47
+
43
48
  # @return [ Integer ] family The type of host family.
44
49
  attr_reader :family
45
50
 
@@ -87,7 +92,7 @@ module Mongo
87
92
  #
88
93
  # @since 2.0.0
89
94
  def close
90
- @socket.close rescue true
95
+ @socket.close rescue nil
91
96
  true
92
97
  end
93
98
 
@@ -105,20 +110,6 @@ module Mongo
105
110
  handle_errors { @socket.gets(*args) }
106
111
  end
107
112
 
108
- # Create the new socket for the provided family - ipv4, piv6, or unix.
109
- #
110
- # @example Create a new ipv4 socket.
111
- # Socket.new(Socket::PF_INET)
112
- #
113
- # @param [ Integer ] family The socket domain.
114
- #
115
- # @since 2.0.0
116
- def initialize(family)
117
- @family = family
118
- @socket = ::Socket.new(family, SOCK_STREAM, 0)
119
- set_socket_options(@socket)
120
- end
121
-
122
113
  # Will read all data from the socket for the provided number of bytes.
123
114
  # If no data is returned, an exception will be raised.
124
115
  #
@@ -172,7 +163,29 @@ module Mongo
172
163
  #
173
164
  # @since 2.0.0
174
165
  def write(*args)
175
- handle_errors { @socket.write(*args) }
166
+ handle_errors do
167
+ # This method used to forward arguments to @socket.write in a
168
+ # single call like so:
169
+ #
170
+ # @socket.write(*args)
171
+ #
172
+ # Turns out, when each buffer to be written is large (e.g. 32 MiB),
173
+ # this write call would take an extremely long time (20+ seconds)
174
+ # while using 100% CPU. Splitting the writes into chunks produced
175
+ # massively better performance (0.05 seconds to write the 32 MiB of
176
+ # data on the same hardware). Unfortunately splitting the data,
177
+ # one would assume, results in it being copied, but this seems to be
178
+ # a much more minor issue compared to CPU cost of writing large buffers.
179
+ args.each do |buf|
180
+ buf = buf.to_s
181
+ i = 0
182
+ while i < buf.length
183
+ chunk = buf[i...i+WRITE_CHUNK_SIZE]
184
+ @socket.write(chunk)
185
+ i += WRITE_CHUNK_SIZE
186
+ end
187
+ end
188
+ end
176
189
  end
177
190
 
178
191
  # Tests if this socket has reached EOF. Primarily used for liveness checks.
@@ -46,12 +46,22 @@ module Mongo
46
46
  # @since 2.0.0
47
47
  def connect!
48
48
  Timeout.timeout(options[:connect_timeout], Error::SocketTimeoutError) do
49
- handle_errors { @tcp_socket.connect(::Socket.pack_sockaddr_in(port, host)) }
49
+ handle_errors do
50
+ @tcp_socket.connect(::Socket.pack_sockaddr_in(port, host))
51
+ end
50
52
  @socket = OpenSSL::SSL::SSLSocket.new(@tcp_socket, context)
51
- @socket.hostname = @host_name
52
- @socket.sync_close = true
53
- handle_errors { @socket.connect }
54
- verify_certificate!(@socket)
53
+ begin
54
+ @socket.hostname = @host_name
55
+ @socket.sync_close = true
56
+ handle_errors do
57
+ @socket.connect
58
+ end
59
+ verify_certificate!(@socket)
60
+ rescue
61
+ @socket.close
62
+ @socket = nil
63
+ raise
64
+ end
55
65
  self
56
66
  end
57
67
  end
@@ -76,9 +86,14 @@ module Mongo
76
86
  @context = create_context(options)
77
87
  @family = family
78
88
  @tcp_socket = ::Socket.new(family, SOCK_STREAM, 0)
79
- @tcp_socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
80
- set_socket_options(@tcp_socket)
81
- connect!
89
+ begin
90
+ @tcp_socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
91
+ set_socket_options(@tcp_socket)
92
+ connect!
93
+ rescue
94
+ @tcp_socket.close
95
+ raise
96
+ end
82
97
  end
83
98
 
84
99
  # Read a single byte from the socket.
@@ -40,7 +40,9 @@ module Mongo
40
40
  def connect!
41
41
  Timeout.timeout(options[:connect_timeout], Error::SocketTimeoutError) do
42
42
  socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
43
- handle_errors { socket.connect(::Socket.pack_sockaddr_in(port, host)) }
43
+ handle_errors do
44
+ socket.connect(::Socket.pack_sockaddr_in(port, host))
45
+ end
44
46
  self
45
47
  end
46
48
  end
@@ -63,8 +65,15 @@ module Mongo
63
65
  # @since 2.0.0
64
66
  def initialize(host, port, timeout, family, options = {})
65
67
  @host, @port, @timeout, @options = host, port, timeout, options
66
- super(family)
67
- connect!
68
+ @family = family
69
+ @socket = ::Socket.new(family, SOCK_STREAM, 0)
70
+ begin
71
+ set_socket_options(@socket)
72
+ connect!
73
+ rescue
74
+ @socket.close
75
+ raise
76
+ end
68
77
  end
69
78
 
70
79
  private
@@ -0,0 +1,49 @@
1
+ # Copyright (C) 2020 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module Timeout
17
+
18
+ # A wrapper around Ruby core's Timeout::timeout method that provides
19
+ # a standardized API for Ruby versions older and newer than 2.4.0,
20
+ # which is when the third argument was introduced.
21
+ #
22
+ # @param [ Numeric ] sec The number of seconds before timeout.
23
+ # @param [ Class ] klass The exception class to raise on timeout, optional.
24
+ # When no error exception is provided, Timeout::Error is raised.
25
+ # @param [ String ] message The error message passed to the exception raised
26
+ # on timeout, optional. When no error message is provided, the default
27
+ # error message is "execution expired".
28
+ #
29
+ # @note Ruby versions older than 2.4.0 do not support specifying a custom
30
+ # error message, and any error message passed in as an argument will be
31
+ # ignored.
32
+ def timeout(sec, klass=nil, message=nil)
33
+ if RUBY_VERSION < '2.4.0'
34
+ ::Timeout.timeout(sec, klass) do
35
+ yield
36
+ end
37
+ else
38
+ # Jruby Timeout::timeout method does not support passing nil arguments.
39
+ # Remove the nil arguments before passing them along to the core
40
+ # Timeout::timeout method.
41
+ optional_args = [klass, message].compact
42
+ ::Timeout.timeout(sec, *optional_args) do
43
+ yield
44
+ end
45
+ end
46
+ end
47
+ module_function :timeout
48
+ end
49
+ end
@@ -215,10 +215,22 @@ module Mongo
215
215
  # @example Get the uri object.
216
216
  # URI.get(string)
217
217
  #
218
+ # @param [ String ] string The URI to parse.
219
+ # @param [ Hash ] options The options.
220
+ #
221
+ # @option options [ Logger ] :logger A custom logger to use.
222
+ #
218
223
  # @return [URI, URI::SRVProtocol] The uri object.
219
224
  #
220
225
  # @since 2.5.0
221
226
  def self.get(string, opts = {})
227
+ unless string
228
+ raise Error::InvalidURI.new(string, 'URI must be a string, not nil.')
229
+ end
230
+ if string.empty?
231
+ raise Error::InvalidURI.new(string, 'Cannot parse an empty URI.')
232
+ end
233
+
222
234
  scheme, _, remaining = string.partition(SCHEME_DELIM)
223
235
  case scheme
224
236
  when MONGODB_SCHEME
@@ -257,13 +269,22 @@ module Mongo
257
269
  # @example Create the new URI.
258
270
  # URI.new('mongodb://localhost:27017')
259
271
  #
260
- # @param [ String ] string The uri string.
272
+ # @param [ String ] string The URI to parse.
261
273
  # @param [ Hash ] options The options.
262
274
  #
275
+ # @option options [ Logger ] :logger A custom logger to use.
276
+ #
263
277
  # @raise [ Error::InvalidURI ] If the uri does not match the spec.
264
278
  #
265
279
  # @since 2.0.0
266
280
  def initialize(string, options = {})
281
+ unless string
282
+ raise Error::InvalidURI.new(string, 'URI must be a string, not nil.')
283
+ end
284
+ if string.empty?
285
+ raise Error::InvalidURI.new(string, 'Cannot parse an empty URI.')
286
+ end
287
+
267
288
  @string = string
268
289
  @options = options
269
290
  parsed_scheme, _, remaining = string.partition(SCHEME_DELIM)
@@ -302,6 +323,14 @@ module Mongo
302
323
  if @uri_options[:ssl_cert]
303
324
  @uri_options[:ssl_key] = @uri_options[:ssl_cert]
304
325
  end
326
+
327
+ if uri_options[:write_concern] && !uri_options[:write_concern].empty?
328
+ begin
329
+ WriteConcern.get(uri_options[:write_concern])
330
+ rescue Error::InvalidWriteConcern => e
331
+ raise_invalid_error_no_fmt!("#{e.class}: #{e}")
332
+ end
333
+ end
305
334
  end
306
335
 
307
336
  # Get the credentials provided in the URI.
@@ -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.11.6'.freeze
20
+ VERSION = '2.12.0.rc0'.freeze
21
21
  end
@@ -40,5 +40,5 @@ Gem::Specification.new do |s|
40
40
 
41
41
  s.required_ruby_version = ">= 2.3"
42
42
 
43
- s.add_dependency 'bson', '>=4.4.2', '<5.0.0'
43
+ s.add_dependency 'bson', '>=4.8.2', '<5.0.0'
44
44
  end
@@ -20,8 +20,13 @@ configuration are given later in this document.
20
20
  ## MongoDB Server Deployment
21
21
 
22
22
  The tests require a running MongoDB deployment, configured and started
23
- externally to the test suite. Tests that are not appropriate for the running
24
- deployment will be skipped.
23
+ externally to the test suite.
24
+
25
+ Tests that are not appropriate for the running deployment will be skipped,
26
+ with one exception: the test suite assumes that fail points are enabled in
27
+ the deployment (see the Fail Points section below). Not every test uses fail
28
+ points, therefore it is possible to launch the server without fail points
29
+ being enabled and still pass many of the tests in the test suite.
25
30
 
26
31
  ## Starting MongoDB Deployment
27
32
 
@@ -41,7 +46,7 @@ launched as follows:
41
46
 
42
47
  # Launch mongod in one terminal
43
48
  mkdir /tmp/mdb
44
- mongod --dbpath /tmp/mdb
49
+ mongod --dbpath /tmp/mdb --setParameter enableTestCommands=1
45
50
 
46
51
  # Run tests in another terminal
47
52
  rake
@@ -133,7 +138,7 @@ the top of the driver source tree:
133
138
 
134
139
  mlaunch init --single --dir /tmp/mdb-ssl --sslMode requireSSL \
135
140
  --sslPEMKeyFile `pwd`/spec/support/certificates/server.pem \
136
- --sslCAFile `pwd`/spec/support/certificates/ca.pem \
141
+ --sslCAFile `pwd`/spec/support/certificates/ca.crt \
137
142
  --sslClientCertificate `pwd`/spec/support/certificates/client.pem
138
143
 
139
144
  To test that the driver works when the server's certificate is signed by an
@@ -142,7 +147,7 @@ server certificate bundle:
142
147
 
143
148
  mlaunch init --single --dir /tmp/mdb-ssl --sslMode requireSSL \
144
149
  --sslPEMKeyFile `pwd`/spec/support/certificates/server-second-level-bundle.pem \
145
- --sslCAFile `pwd`/spec/support/certificates/ca.pem \
150
+ --sslCAFile `pwd`/spec/support/certificates/ca.crt \
146
151
  --sslClientCertificate `pwd`/spec/support/certificates/client.pem
147
152
 
148
153
  The driver's test suite is configured to verify certificates by default.
@@ -168,7 +173,7 @@ case a standalone server can be started as follows:
168
173
 
169
174
  mlaunch init --single --dir /tmp/mdb-ssl --sslMode requireSSL \
170
175
  --sslPEMKeyFile `pwd`/spec/support/certificates/server.pem \
171
- --sslCAFile `pwd`/spec/support/certificates/ca.pem \
176
+ --sslCAFile `pwd`/spec/support/certificates/ca.crt \
172
177
  --sslAllowConnectionsWithoutCertificates \
173
178
  --sslAllowInvalidCertificates
174
179
 
@@ -264,9 +269,123 @@ To run the test suite against such a server, run:
264
269
 
265
270
  MONGODB_URI="mongodb://localhost:27017/?authMechanism=MONGODB-X509&tls=true&tlsCAFile=spec/support/certificates/ca.crt&tlsCertificateKeyFile=spec/support/certificates/client-x509.pem" rake
266
271
 
272
+ ## Kerberos
273
+
274
+ The Kerberos-related functionality is packaged in a separate gem,
275
+ `mongo_kerberos`. The driver test suite includes a number of unit tests that
276
+ are skipped by default. To run them as part of the test suite, use the
277
+ provided gemfile as follows:
278
+
279
+ export BUNDLE_GEMFILE=gemfiles/mongo_kerberos.gemfile
280
+ bundle install
281
+ export KERBEROS=1
282
+ rake
283
+
284
+ Note that running the full test suite requires a MongoDB deployment. It is
285
+ possible to run just the Kerberos-related unit tests without provisioning a
286
+ MongoDB deployment; consult .evergreen/run-tests-kerberos-unit.sh for the
287
+ full list of relevant test files.
288
+
289
+ ## Client-Side Encryption
290
+
291
+ Install libmongocrypt on your machine:
292
+
293
+ Option 1: Download a pre-built binary
294
+ - Download a tarball of all libmongocrypt variations from this link:
295
+ https://s3.amazonaws.com/mciuploads/libmongocrypt/all/master/latest/libmongocrypt-all.tar.gz
296
+ - Unzip the file you downloaded. You will see a list of folders, each
297
+ corresponding to an operating system. Find the folder that matches
298
+ your operating system and open it.
299
+ - Inside that folder, open the folder called "nocrypto." In either the
300
+ lib or lb64 folder, you will find the libmongocrypt.so or
301
+ libmongocrypt.dylib or libmongocrypt.dll file, depending on your OS.
302
+ - Move that file to wherever you want to keep it on your machine.
303
+
304
+ Option 2: Build from source
305
+ - To build libmongocrypt from source, follow the instructions in the README on the libmongocrypt GitHub repo: https://github.com/mongodb/libmongocrypt
306
+
307
+ Create AWS KMS keys
308
+ Many of the Client-Side Encryption tests require that you have an encryption
309
+ master key hosted on AWS's Key Management Service. Set up a master key by following
310
+ these steps:
311
+
312
+ 1. Sign up for an AWS account at this link if you don't already have one: https://aws.amazon.com/resources/create-account/
313
+
314
+ 2. Create a new IAM user that you want to have permissions to access your new
315
+ master key by following this guide: the "Creating an Administrator IAM User and Group (Console)"
316
+ section of this guide: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user.html
317
+
318
+ 3. Create an access key for your new IAM user and store the access key credentials
319
+ in environment variables on your local machine. Create an access key by following the
320
+ "Managing Access Keys (Console)" instructions in this guide:
321
+ https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html#Using_CreateAccessKey
322
+ Once an access key has been created, store the access key id and the access key
323
+ secret in environment variables. If you plan to frequently run Client-Side
324
+ Encryption tests, it may be a good idea to put these lines in your .bash_profile
325
+ or .bashrc file. Otherwise, you can run them in the terminal window where you
326
+ plan to run your tests.
327
+
328
+ ```
329
+ export MONGO_RUBY_DRIVER_AWS_KEY="YOUR-ACCESS-KEY-ID"
330
+ export MONGO_RUBY_DRIVER_AWS_SECRET="YOUR-ACCESS-KEY-SECRET"
331
+ ```
332
+
333
+ 4. Create a new symmetric Customer Master Key (CMK) by following the "Creating Symmetric CMKs (Console)"
334
+ section of this guide: https://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html
335
+
336
+ 5. Store information about your CMK in the following environment variables:
337
+
338
+ a. **Region:** Find your AWS region by following this guide: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#using-regions-availability-zones-describe
339
+ (for example, your region might be "us-east-1" or "ap-south-2").
340
+
341
+ b. **Amazon Resource Name (ARN):** Read the following guide to learn more about ARNs
342
+ and how to view your key's ARN: https://docs.aws.amazon.com/kms/latest/developerguide/viewing-keys-console.html
343
+
344
+ Store these two pieces of information in environment variables. If you plan
345
+ to frequently run Client-Side Encryption tests, it may be a good idea to put
346
+ these lines in your .bash_profile or .bashrc file. Otherwise, you can run
347
+ them in the terminal window where you plan to run your tests.
348
+
349
+ ```
350
+ export MONGO_RUBY_DRIVER_AWS_REGION="YOUR-AWS-REGION"
351
+ export MONGO_RUBY_DRIVER_AWS_ARN="YOUR-AWS-ARN"
352
+ ```
353
+
354
+ 6. Give your IAM user "Key administrator" and "Key user" privileges on your new CMK
355
+ by following the "Using the AWS Management Console Default View" section of this guide:
356
+ https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-modifying.html
357
+
358
+ In one terminal, launch MongoDB:
359
+
360
+ NOTE: You must be running MongoDB 4.2 or higher. All auto-encryption features
361
+ require an enterprise build of MongoDB, but you can still run
362
+ explicit encryption tests using the community edition of MongoDB.
363
+
364
+ Download different versions of MongoDB here: https://www.mongodb.com/download-center/enterprise
365
+
366
+ ```
367
+ mkdir /tmp/mdb
368
+ mongod --dbpath /tmp/mdb --setParameter enableTestCommands=1
369
+ ```
370
+
371
+ In another terminal run the tests, making sure to set the `LIBMONGOCRYPT_PATH`
372
+ environment variable to the full path to the .so/.dll/.dylib
373
+ ```
374
+ LIBMONGOCRYPT_PATH=/path/to/your/libmongocrypt/nocrypto/libmongocrypt.so bundle exec rake
375
+ ```
376
+
267
377
  ## Compression
268
378
 
269
- To be written.
379
+ To test compression, set the `compressors` URI option:
380
+
381
+ MONGODB_URI="mongodb://localhost:27017/?compressors=zlib" rake
382
+
383
+ Note that as of this writing, the driver only supports zlib compression.
384
+ Servers 4.2+ enable zlib by default; to test older servers, explicitly enable
385
+ zlib compression when launching the server:
386
+
387
+ mongod --dbpath /tmp/mdb --setParameter enableTestCommands=1 \
388
+ --networkMessageCompressors snappy,zlib
270
389
 
271
390
  ## Other Options
272
391
 
@@ -360,3 +479,11 @@ Then, any of the standard RSpec invocations will work:
360
479
  To have the test suite report its current configuration, run:
361
480
 
362
481
  rake spec:config
482
+
483
+ ## Color Output
484
+
485
+ The test suite uses color output by default. To view the output in `less`
486
+ with color, use the `-R` option:
487
+
488
+ rake 2>&1 | tee rake.log
489
+ less -R rake.log