mongo 2.14.0 → 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.tar.gz.sig +0 -0
  4. data/README.md +4 -1
  5. data/Rakefile +8 -15
  6. data/lib/mongo.rb +23 -0
  7. data/lib/mongo/auth/aws/conversation.rb +1 -4
  8. data/lib/mongo/auth/base.rb +13 -7
  9. data/lib/mongo/auth/conversation_base.rb +32 -0
  10. data/lib/mongo/auth/cr/conversation.rb +6 -29
  11. data/lib/mongo/auth/gssapi/conversation.rb +4 -15
  12. data/lib/mongo/auth/ldap/conversation.rb +3 -14
  13. data/lib/mongo/auth/sasl_conversation_base.rb +1 -13
  14. data/lib/mongo/auth/scram_conversation_base.rb +7 -34
  15. data/lib/mongo/auth/user/view.rb +16 -9
  16. data/lib/mongo/auth/x509/conversation.rb +4 -25
  17. data/lib/mongo/background_thread.rb +11 -0
  18. data/lib/mongo/bulk_write.rb +21 -18
  19. data/lib/mongo/client.rb +82 -6
  20. data/lib/mongo/cluster.rb +19 -28
  21. data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -2
  22. data/lib/mongo/cluster/sdam_flow.rb +14 -0
  23. data/lib/mongo/collection.rb +8 -6
  24. data/lib/mongo/collection/view/aggregation.rb +1 -1
  25. data/lib/mongo/collection/view/change_stream.rb +1 -1
  26. data/lib/mongo/collection/view/iterable.rb +1 -1
  27. data/lib/mongo/collection/view/map_reduce.rb +2 -2
  28. data/lib/mongo/collection/view/readable.rb +42 -20
  29. data/lib/mongo/collection/view/writable.rb +14 -14
  30. data/lib/mongo/cursor.rb +2 -2
  31. data/lib/mongo/database.rb +22 -5
  32. data/lib/mongo/database/view.rb +1 -1
  33. data/lib/mongo/error.rb +9 -1
  34. data/lib/mongo/error/bulk_write_error.rb +17 -3
  35. data/lib/mongo/error/internal_driver_error.rb +22 -0
  36. data/lib/mongo/error/operation_failure.rb +21 -2
  37. data/lib/mongo/error/parser.rb +65 -12
  38. data/lib/mongo/error/server_api_conflict.rb +23 -0
  39. data/lib/mongo/error/server_api_not_supported.rb +24 -0
  40. data/lib/mongo/error/unmet_dependency.rb +21 -0
  41. data/lib/mongo/grid/fs_bucket.rb +37 -37
  42. data/lib/mongo/index/view.rb +21 -11
  43. data/lib/mongo/monitoring.rb +13 -4
  44. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +27 -16
  45. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +26 -15
  46. data/lib/mongo/operation.rb +2 -2
  47. data/lib/mongo/operation/collections_info.rb +18 -1
  48. data/lib/mongo/operation/collections_info/command.rb +2 -2
  49. data/lib/mongo/operation/context.rb +99 -0
  50. data/lib/mongo/operation/indexes.rb +15 -1
  51. data/lib/mongo/operation/insert/command.rb +2 -2
  52. data/lib/mongo/operation/insert/legacy.rb +2 -2
  53. data/lib/mongo/operation/insert/op_msg.rb +2 -2
  54. data/lib/mongo/operation/list_collections/result.rb +4 -1
  55. data/lib/mongo/operation/result.rb +2 -0
  56. data/lib/mongo/operation/shared/executable.rb +24 -14
  57. data/lib/mongo/operation/shared/executable_no_validate.rb +2 -2
  58. data/lib/mongo/operation/shared/op_msg_or_command.rb +1 -7
  59. data/lib/mongo/operation/shared/op_msg_or_find_command.rb +1 -7
  60. data/lib/mongo/operation/shared/polymorphic_operation.rb +39 -0
  61. data/lib/mongo/operation/shared/response_handling.rb +23 -23
  62. data/lib/mongo/operation/shared/sessions_supported.rb +13 -2
  63. data/lib/mongo/operation/shared/write.rb +8 -18
  64. data/lib/mongo/protocol/compressed.rb +51 -5
  65. data/lib/mongo/protocol/message.rb +20 -2
  66. data/lib/mongo/protocol/msg.rb +36 -11
  67. data/lib/mongo/query_cache.rb +30 -0
  68. data/lib/mongo/retryable.rb +1 -1
  69. data/lib/mongo/server.rb +7 -15
  70. data/lib/mongo/server/app_metadata.rb +52 -18
  71. data/lib/mongo/server/connection.rb +5 -0
  72. data/lib/mongo/server/connection_base.rb +13 -10
  73. data/lib/mongo/server/connection_pool.rb +6 -4
  74. data/lib/mongo/server/description.rb +4 -0
  75. data/lib/mongo/server/description/features.rb +9 -8
  76. data/lib/mongo/server/monitor.rb +20 -1
  77. data/lib/mongo/server/monitor/app_metadata.rb +1 -1
  78. data/lib/mongo/server/monitor/connection.rb +9 -10
  79. data/lib/mongo/server/pending_connection.rb +24 -6
  80. data/lib/mongo/server/push_monitor.rb +11 -1
  81. data/lib/mongo/session.rb +2 -2
  82. data/lib/mongo/session/session_pool.rb +4 -2
  83. data/lib/mongo/socket.rb +29 -4
  84. data/lib/mongo/socket/ssl.rb +8 -0
  85. data/lib/mongo/srv/monitor.rb +0 -11
  86. data/lib/mongo/uri/options_mapper.rb +38 -0
  87. data/lib/mongo/utils.rb +15 -0
  88. data/lib/mongo/version.rb +1 -1
  89. data/spec/README.md +24 -1
  90. data/spec/integration/auth_spec.rb +25 -15
  91. data/spec/integration/bulk_write_error_message_spec.rb +41 -0
  92. data/spec/integration/change_stream_spec.rb +4 -4
  93. data/spec/integration/command_monitoring_spec.rb +2 -2
  94. data/spec/integration/connection_spec.rb +2 -0
  95. data/spec/integration/docs_examples_spec.rb +8 -1
  96. data/spec/integration/fork_reconnect_spec.rb +4 -1
  97. data/spec/integration/ocsp_verifier_spec.rb +13 -7
  98. data/spec/integration/operation_failure_code_spec.rb +1 -1
  99. data/spec/integration/operation_failure_message_spec.rb +90 -0
  100. data/spec/integration/reconnect_spec.rb +1 -1
  101. data/spec/integration/sdam_error_handling_spec.rb +1 -1
  102. data/spec/integration/sdam_events_spec.rb +3 -5
  103. data/spec/integration/snappy_compression_spec.rb +25 -0
  104. data/spec/integration/srv_monitoring_spec.rb +1 -1
  105. data/spec/integration/transactions_examples_spec.rb +6 -0
  106. data/spec/integration/zlib_compression_spec.rb +1 -1
  107. data/spec/integration/zstd_compression_spec.rb +26 -0
  108. data/spec/lite_spec_helper.rb +7 -1
  109. data/spec/mongo/address_spec.rb +15 -11
  110. data/spec/mongo/auth/ldap/conversation_spec.rb +1 -1
  111. data/spec/mongo/auth/ldap_spec.rb +5 -1
  112. data/spec/mongo/auth/scram_negotiation_spec.rb +1 -1
  113. data/spec/mongo/auth/scram_spec.rb +1 -1
  114. data/spec/mongo/auth/x509/conversation_spec.rb +3 -3
  115. data/spec/mongo/client_construction_spec.rb +207 -33
  116. data/spec/mongo/client_spec.rb +17 -0
  117. data/spec/mongo/cluster_spec.rb +3 -18
  118. data/spec/mongo/collection/view/explainable_spec.rb +1 -1
  119. data/spec/mongo/collection/view/readable_spec.rb +33 -19
  120. data/spec/mongo/collection_crud_spec.rb +4357 -0
  121. data/spec/mongo/collection_ddl_spec.rb +534 -0
  122. data/spec/mongo/collection_spec.rb +5 -4859
  123. data/spec/mongo/database_spec.rb +66 -4
  124. data/spec/mongo/error/bulk_write_error_spec.rb +3 -3
  125. data/spec/mongo/error/parser_spec.rb +37 -6
  126. data/spec/mongo/index/view_spec.rb +8 -2
  127. data/spec/mongo/monitoring/event/server_heartbeat_failed_spec.rb +1 -1
  128. data/spec/mongo/monitoring/event/server_heartbeat_succeeded_spec.rb +1 -1
  129. data/spec/mongo/operation/aggregate_spec.rb +2 -1
  130. data/spec/mongo/operation/collections_info_spec.rb +4 -1
  131. data/spec/mongo/operation/command_spec.rb +6 -3
  132. data/spec/mongo/operation/create_index_spec.rb +6 -3
  133. data/spec/mongo/operation/create_user_spec.rb +6 -3
  134. data/spec/mongo/operation/delete/bulk_spec.rb +9 -6
  135. data/spec/mongo/operation/delete_spec.rb +11 -7
  136. data/spec/mongo/operation/drop_index_spec.rb +6 -2
  137. data/spec/mongo/operation/find/legacy_spec.rb +3 -1
  138. data/spec/mongo/operation/get_more_spec.rb +3 -1
  139. data/spec/mongo/operation/indexes_spec.rb +5 -1
  140. data/spec/mongo/operation/insert/bulk_spec.rb +10 -7
  141. data/spec/mongo/operation/insert_spec.rb +15 -12
  142. data/spec/mongo/operation/map_reduce_spec.rb +5 -2
  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_spec.rb +2 -0
  152. data/spec/mongo/server/connection_pool/populator_spec.rb +3 -1
  153. data/spec/mongo/server/connection_pool_spec.rb +1 -1
  154. data/spec/mongo/server/connection_spec.rb +24 -17
  155. data/spec/mongo/server/monitor/connection_spec.rb +17 -7
  156. data/spec/mongo/server/monitor_spec.rb +9 -1
  157. data/spec/mongo/server_spec.rb +15 -2
  158. data/spec/mongo/socket/ssl_spec.rb +40 -0
  159. data/spec/mongo/socket_spec.rb +2 -2
  160. data/spec/mongo/tls_context_hooks_spec.rb +37 -0
  161. data/spec/runners/connection_string.rb +0 -4
  162. data/spec/runners/crud/requirement.rb +40 -3
  163. data/spec/runners/crud/verifier.rb +8 -0
  164. data/spec/runners/transactions/operation.rb +13 -2
  165. data/spec/runners/transactions/test.rb +1 -0
  166. data/spec/runners/unified.rb +96 -0
  167. data/spec/runners/unified/assertions.rb +249 -0
  168. data/spec/runners/unified/change_stream_operations.rb +26 -0
  169. data/spec/runners/unified/crud_operations.rb +199 -0
  170. data/spec/runners/unified/ddl_operations.rb +96 -0
  171. data/spec/runners/unified/entity_map.rb +39 -0
  172. data/spec/runners/unified/error.rb +25 -0
  173. data/spec/runners/unified/event_subscriber.rb +91 -0
  174. data/spec/runners/unified/exceptions.rb +21 -0
  175. data/spec/runners/unified/grid_fs_operations.rb +55 -0
  176. data/spec/runners/unified/support_operations.rb +250 -0
  177. data/spec/runners/unified/test.rb +393 -0
  178. data/spec/runners/unified/test_group.rb +28 -0
  179. data/spec/runners/unified/using_hash.rb +31 -0
  180. data/spec/shared/bin/get-mongodb-download-url +17 -0
  181. data/spec/shared/lib/mrss/cluster_config.rb +218 -0
  182. data/spec/shared/lib/mrss/constraints.rb +43 -0
  183. data/spec/shared/lib/mrss/docker_runner.rb +262 -0
  184. data/spec/shared/lib/mrss/server_version_registry.rb +112 -0
  185. data/spec/shared/lib/mrss/utils.rb +15 -0
  186. data/spec/shared/share/Dockerfile.erb +231 -0
  187. data/spec/shared/shlib/distro.sh +73 -0
  188. data/spec/shared/shlib/server.sh +290 -0
  189. data/spec/shared/shlib/set_env.sh +128 -0
  190. data/spec/solo/clean_exit_spec.rb +21 -0
  191. data/spec/spec_helper.rb +4 -1
  192. data/spec/spec_tests/crud_unified_spec.rb +10 -0
  193. data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
  194. data/spec/spec_tests/data/crud_unified/estimatedDocumentCount.yml +267 -0
  195. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-4.9.yml +60 -0
  196. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount.yml → estimatedDocumentCount-pre4.9.yml} +2 -0
  197. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors-4.9.yml +146 -0
  198. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount-serverErrors.yml → estimatedDocumentCount-serverErrors-pre4.9.yml} +2 -0
  199. data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +1 -1
  200. data/spec/spec_tests/data/unified/valid-fail/operation-failure.yml +31 -0
  201. data/spec/spec_tests/data/unified/valid-pass/poc-change-streams.yml +220 -0
  202. data/spec/spec_tests/data/unified/valid-pass/poc-command-monitoring.yml +102 -0
  203. data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +184 -0
  204. data/spec/spec_tests/data/unified/valid-pass/poc-gridfs.yml +155 -0
  205. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-reads.yml +193 -0
  206. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +210 -0
  207. data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +215 -0
  208. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +235 -0
  209. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +169 -0
  210. data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +170 -0
  211. data/spec/spec_tests/data/uri_options/compression-options.yml +1 -1
  212. data/spec/spec_tests/data/versioned_api/crud-api-version-1-strict.yml +416 -0
  213. data/spec/spec_tests/data/versioned_api/crud-api-version-1.yml +409 -0
  214. data/spec/spec_tests/data/versioned_api/runcommand-helper-no-api-version-declared.yml +67 -0
  215. data/spec/spec_tests/data/versioned_api/test-commands-deprecation-errors.yml +47 -0
  216. data/spec/spec_tests/data/versioned_api/test-commands-strict-mode.yml +44 -0
  217. data/spec/spec_tests/data/versioned_api/transaction-handling.yml +180 -0
  218. data/spec/spec_tests/unified_spec.rb +15 -0
  219. data/spec/spec_tests/uri_options_spec.rb +16 -0
  220. data/spec/spec_tests/versioned_api_spec.rb +10 -0
  221. data/spec/support/common_shortcuts.rb +15 -1
  222. data/spec/support/shared/session.rb +2 -2
  223. data/spec/support/spec_config.rb +46 -3
  224. data/spec/support/spec_setup.rb +48 -38
  225. data/spec/support/utils.rb +64 -3
  226. metadata +1104 -992
  227. metadata.gz.sig +0 -0
  228. data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +0 -58
  229. data/lib/mongo/operation/shared/op_msg_or_list_indexes_command.rb +0 -47
  230. data/spec/support/cluster_config.rb +0 -207
@@ -0,0 +1,28 @@
1
+ module Unified
2
+
3
+ class TestGroup
4
+ def initialize(path, **opts)
5
+ if String === path
6
+ data = YAML.load(File.read(path))
7
+ else
8
+ data = path
9
+ end
10
+ @spec = BSON::ExtJSON.parse_obj(data)
11
+ @options = opts
12
+ end
13
+
14
+ attr_reader :options
15
+
16
+ def tests
17
+ reqs = @spec['runOnRequirements']
18
+
19
+ @spec.fetch('tests').map do |test|
20
+ sub = @spec.dup
21
+ sub.delete('tests')
22
+ sub['test'] = test
23
+ sub['group_runOnRequirements'] = reqs
24
+ Test.new(sub, **options)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ module Unified
2
+
3
+ class UsingHash < Hash
4
+ def use(key)
5
+ wrap(self[key]).tap do
6
+ delete(key)
7
+ end
8
+ end
9
+
10
+ def use!(key)
11
+ wrap(fetch(key)).tap do
12
+ delete(key)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def wrap(v)
19
+ case v
20
+ when Hash
21
+ self.class[v]
22
+ when Array
23
+ v.map do |subv|
24
+ wrap(subv)
25
+ end
26
+ else
27
+ v
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ desired_version, arch = ARGV
4
+ if arch.nil?
5
+ STDERR.puts "Usage: get-mongodb-download-url desired-version arch"
6
+ exit 1
7
+ end
8
+
9
+ $: << File.join(File.dirname(__FILE__), '../lib')
10
+ require 'mrss/server_version_registry'
11
+
12
+ begin
13
+ puts Mrss::ServerVersionRegistry.new(desired_version, arch).download_url
14
+ rescue Mrss::ServerVersionRegistry::Error => exc
15
+ STDERR.puts "Error: #{exc}"
16
+ exit 2
17
+ end
@@ -0,0 +1,218 @@
1
+ # ClusterConfig requires ClientRegistry class provided by the host project.
2
+
3
+ require 'singleton'
4
+
5
+ module Mrss
6
+ class ClusterConfig
7
+ include Singleton
8
+ include RSpec::Core::Pending
9
+
10
+ def single_server?
11
+ determine_cluster_config
12
+ @single_server
13
+ end
14
+
15
+ def replica_set_name
16
+ determine_cluster_config
17
+ @replica_set_name
18
+ end
19
+
20
+ def server_version
21
+ determine_cluster_config
22
+ @server_version
23
+ end
24
+
25
+ def enterprise?
26
+ determine_cluster_config
27
+ @enterprise
28
+ end
29
+
30
+ def short_server_version
31
+ server_version.split('.')[0..1].join('.')
32
+ end
33
+
34
+ def fcv
35
+ determine_cluster_config
36
+ @fcv
37
+ end
38
+
39
+ # Per https://jira.mongodb.org/browse/SERVER-39052, working with FCV
40
+ # in sharded topologies is annoying. Also, FCV doesn't exist in servers
41
+ # less than 3.4. This method returns FCV on 3.4+ servers when in single
42
+ # or RS topologies, and otherwise returns the major.minor server version.
43
+ def fcv_ish
44
+ if server_version.nil?
45
+ raise "Deployment server version not known - check that connection to deployment succeeded"
46
+ end
47
+
48
+ if server_version >= '3.4' && topology != :sharded
49
+ fcv
50
+ else
51
+ if short_server_version == '4.1'
52
+ '4.2'
53
+ else
54
+ short_server_version
55
+ end
56
+ end
57
+ end
58
+
59
+ # @return [ Mongo::Address ] The address of the primary in the deployment.
60
+ def primary_address
61
+ determine_cluster_config
62
+ @primary_address
63
+ end
64
+
65
+ def primary_address_str
66
+ determine_cluster_config
67
+ @primary_address.seed
68
+ end
69
+
70
+ def primary_address_host
71
+ both = primary_address_str
72
+ both.split(':').first
73
+ end
74
+
75
+ def primary_address_port
76
+ both = primary_address_str
77
+ both.split(':')[1] || 27017
78
+ end
79
+
80
+ def primary_description
81
+ determine_cluster_config
82
+ @primary_description
83
+ end
84
+
85
+ def server_parameters
86
+ determine_cluster_config
87
+ @server_parameters
88
+ end
89
+
90
+ # Try running a command on the admin database to see if the mongod was
91
+ # started with auth.
92
+ def auth_enabled?
93
+ if @auth_enabled.nil?
94
+ @auth_enabled = begin
95
+ basic_client.use(:admin).command(getCmdLineOpts: 1).first["argv"].include?("--auth")
96
+ rescue => e
97
+ e.message =~ /(not authorized)|(unauthorized)|(no users authenticated)|(requires authentication)/
98
+ end
99
+ end
100
+ @auth_enabled
101
+ end
102
+
103
+ def topology
104
+ determine_cluster_config
105
+ @topology
106
+ end
107
+
108
+ def storage_engine
109
+ @storage_engine ||= begin
110
+ # 2.6 does not have wired tiger
111
+ if short_server_version == '2.6'
112
+ :mmapv1
113
+ else
114
+ client = ClientRegistry.instance.global_client('root_authorized')
115
+ if topology == :sharded
116
+ shards = client.use(:admin).command(listShards: 1).first
117
+ if shards['shards'].empty?
118
+ raise 'Shards are empty'
119
+ end
120
+ shard = shards['shards'].first
121
+ address_str = shard['host'].sub(/^.*\//, '').sub(/,.*/, '')
122
+ client = ClusterTools.instance.direct_client(address_str,
123
+ SpecConfig.instance.test_options.merge(SpecConfig.instance.auth_options).merge(connect: :direct))
124
+ end
125
+ rv = client.use(:admin).command(serverStatus: 1).first
126
+ rv = rv['storageEngine']['name']
127
+ rv_map = {
128
+ 'wiredTiger' => :wired_tiger,
129
+ 'mmapv1' => :mmapv1,
130
+ }
131
+ rv_map[rv] || rv
132
+ end
133
+ end
134
+ end
135
+
136
+ # This method returns an alternate address for connecting to the configured
137
+ # deployment. For example, if the replica set is configured with nodes at
138
+ # of localhost:27017 and so on, this method will return 127.0.0.:27017.
139
+ #
140
+ # Note that the "alternate" refers to replica set configuration, not the
141
+ # addresses specified in test suite configuration. If the deployment topology
142
+ # is not a replica set, "alternate" refers to test suite configuration as
143
+ # this is the only configuration available.
144
+ def alternate_address
145
+ @alternate_address ||= begin
146
+ address = primary_address_host
147
+ str = case address
148
+ when '127.0.0.1'
149
+ 'localhost'
150
+ when /^(\d+\.){3}\d+$/
151
+ skip 'This test requires a hostname or 127.0.0.1 as address'
152
+ else
153
+ # We don't know if mongod is listening on ipv4 or ipv6, in principle.
154
+ # Our tests use ipv4, so hardcode that for now.
155
+ # To support both we need to try both addresses which will make this
156
+ # test more complicated.
157
+ #
158
+ # JRuby chokes on primary_address_port as the port (e.g. 27017).
159
+ # Since the port does not actually matter, use a common port like 80.
160
+ resolved_address = Addrinfo.getaddrinfo(address, 80, Socket::PF_INET).first.ip_address
161
+ if resolved_address.include?(':')
162
+ "[#{resolved_address}]"
163
+ else
164
+ resolved_address
165
+ end
166
+ end + ":#{primary_address_port}"
167
+ Mongo::Address.new(str)
168
+ end
169
+ end
170
+
171
+ private
172
+
173
+ def determine_cluster_config
174
+ return if @primary_address
175
+
176
+ # Run all commands to figure out the cluster configuration from the same
177
+ # client. This is somewhat wasteful when running a single test, but reduces
178
+ # test runtime for the suite overall because all commands are sent on the
179
+ # same connection rather than each command connecting to the cluster by
180
+ # itself.
181
+ client = ClientRegistry.instance.global_client('root_authorized')
182
+
183
+ primary = client.cluster.next_primary
184
+ @primary_address = primary.address
185
+ @primary_description = primary.description
186
+ @replica_set_name = client.cluster.topology.replica_set_name
187
+
188
+ @topology ||= begin
189
+ topology = client.cluster.topology.class.name.sub(/.*::/, '')
190
+ topology = topology.gsub(/([A-Z])/) { |match| '_' + match.downcase }.sub(/^_/, '')
191
+ if topology =~ /^replica_set/
192
+ topology = 'replica_set'
193
+ end
194
+ topology.to_sym
195
+ end
196
+
197
+ @single_server = client.cluster.servers_list.length == 1
198
+
199
+ build_info = client.database.command(buildInfo: 1).first
200
+
201
+ @server_version = build_info['version']
202
+ @enterprise = build_info['modules'] && build_info['modules'].include?('enterprise')
203
+
204
+ @server_parameters = client.use(:admin).command(getParameter: '*').first
205
+
206
+ if @topology != :sharded && short_server_version >= '3.4'
207
+ rv = @server_parameters['featureCompatibilityVersion']
208
+ @fcv = rv['version'] || rv
209
+ end
210
+ end
211
+
212
+ def basic_client
213
+ # Do not cache the result here so that if the client gets closed,
214
+ # client registry reconnects it in subsequent tests
215
+ ClientRegistry.instance.global_client('basic')
216
+ end
217
+ end
218
+ end
@@ -147,6 +147,33 @@ module Mrss
147
147
  end
148
148
  end
149
149
 
150
+ def require_no_snappy_compression
151
+ before(:all) do
152
+ compressors = SpecConfig.instance.compressors
153
+ if compressors && compressors.include?('snappy')
154
+ skip "Snappy compression is enabled"
155
+ end
156
+ end
157
+ end
158
+
159
+ def require_zstd_compression
160
+ before(:all) do
161
+ compressors = SpecConfig.instance.compressors
162
+ unless compressors && compressors.include?('zstd')
163
+ skip "Zstd compression is not enabled"
164
+ end
165
+ end
166
+ end
167
+
168
+ def require_no_zstd_compression
169
+ before(:all) do
170
+ compressors = SpecConfig.instance.compressors
171
+ if compressors && compressors.include?('zstd')
172
+ skip "Zstd compression is enabled"
173
+ end
174
+ end
175
+ end
176
+
150
177
  def require_no_compression
151
178
  before(:all) do
152
179
  if SpecConfig.instance.compressors
@@ -299,5 +326,21 @@ module Mrss
299
326
  end
300
327
  end
301
328
  end
329
+
330
+ def require_required_api_version
331
+ before(:all) do
332
+ unless ENV['API_VERSION_REQUIRED'] == '1'
333
+ skip 'Set API_VERSION_REQUIRED=1 to run this test'
334
+ end
335
+ end
336
+ end
337
+
338
+ def require_no_required_api_version
339
+ before(:all) do
340
+ if ENV['API_VERSION_REQUIRED'] == '1'
341
+ skip 'Cannot have API_VERSION_REQUIRED=1 to run this test'
342
+ end
343
+ end
344
+ end
302
345
  end
303
346
  end
@@ -0,0 +1,262 @@
1
+ require 'optparse'
2
+ require 'erb'
3
+ autoload :Dotenv, 'dotenv'
4
+
5
+ module Mrss
6
+ autoload :ServerVersionRegistry, 'mrss/server_version_registry'
7
+
8
+ class DockerRunner
9
+ def initialize(**opts)
10
+ # These options are required:
11
+ opts.fetch(:image_tag)
12
+ opts.fetch(:dockerfile_path)
13
+ opts.fetch(:default_script)
14
+ opts.fetch(:project_lib_subdir)
15
+
16
+ @options = opts
17
+ end
18
+
19
+ attr_reader :options
20
+
21
+ def run
22
+ process_arguments
23
+ unless @options[:exec_only]
24
+ create_dockerfile
25
+ create_image
26
+ end
27
+ if @options[:mongo_only]
28
+ run_deployment
29
+ else
30
+ run_tests
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def process_arguments
37
+ #@options = {}
38
+ OptionParser.new do |opts|
39
+ opts.banner = "Usage: test-on-docker [-d distro] [evergreen_key=value ...]"
40
+
41
+ opts.on("-a", "--add-env=PATH", "Load environment variables from PATH in .env format") do |path|
42
+ @options[:extra_env] ||= {}
43
+ unless File.exist?(path)
44
+ raise "-a option references nonexistent file #{path}"
45
+ end
46
+ Dotenv.parse(path).each do |k, v|
47
+ @options[:extra_env][k] = v
48
+ end
49
+ end
50
+
51
+ opts.on("-d", "--distro=DISTRO", "Distro to use") do |v|
52
+ @options[:distro] = v
53
+ end
54
+
55
+ opts.on('-e', '--exec-only', 'Execute tests using existing Dockerfile (for offline user)') do |v|
56
+ @options[:exec_only] = v
57
+ end
58
+
59
+ opts.on('-m', '--mongo-only=PORT', 'Start the MongoDB deployment and expose it to host on ports starting with PORT') do |v|
60
+ @options[:mongo_only] = v.to_i
61
+ end
62
+
63
+ opts.on('-p', '--preload', 'Preload Ruby toolchain and server binaries in docker') do |v|
64
+ @options[:preload] = v
65
+ end
66
+
67
+ opts.on('-s', '--script=SCRIPT', 'Test script to invoke') do |v|
68
+ @options[:script] = v
69
+ end
70
+
71
+ opts.on('-i', '--interactive', 'Interactive mode - disable per-test timeouts') do |v|
72
+ @options[:interactive] = v
73
+ end
74
+ end.parse!
75
+
76
+ @env = Hash[ARGV.map do |arg|
77
+ arg.split('=', 2)
78
+ end]
79
+
80
+ @env['RVM_RUBY'] ||= 'ruby-2.7'
81
+ unless ruby =~ /^j?ruby-/
82
+ raise "RVM_RUBY option is not in expected format: #{ruby}"
83
+ end
84
+
85
+ @env['MONGODB_VERSION'] ||= '4.4'
86
+ end
87
+
88
+ def create_dockerfile
89
+ template_path = File.join(File.dirname(__FILE__), '../../share/Dockerfile.erb')
90
+ result = ERB.new(File.read(template_path)).result(binding)
91
+ File.open(dockerfile_path, 'w') do |f|
92
+ f << result
93
+ end
94
+ end
95
+
96
+ def image_tag
97
+ options.fetch(:image_tag)
98
+ end
99
+
100
+ def dockerfile_path
101
+ options.fetch(:dockerfile_path)
102
+ end
103
+
104
+ def create_image
105
+ run_command(['docker', 'build',
106
+ '-t', image_tag,
107
+ '-f', dockerfile_path,
108
+ '.'])
109
+ end
110
+
111
+ BASE_TEST_COMMAND = %w(docker run -i --tmpfs /tmpfs:exec).freeze
112
+
113
+ def run_tests
114
+ run_command(BASE_TEST_COMMAND + tty_arg + extra_env + [image_tag] +
115
+ script.split(/\s+/))
116
+ end
117
+
118
+ def run_deployment
119
+ run_command(BASE_TEST_COMMAND + tty_arg + extra_env + [
120
+ '-e', %q`TEST_CMD=watch -x bash -c "ps awwxu |egrep 'mongo|ocsp'"`,
121
+ '-e', 'BIND_ALL=true',
122
+ ] + port_forwards + [image_tag] + script.split(/\s+/))
123
+ end
124
+
125
+ def tty_arg
126
+ tty = File.open('/dev/stdin') do |f|
127
+ f.isatty
128
+ end
129
+ if tty
130
+ %w(-t --init)
131
+ else
132
+ []
133
+ end
134
+ end
135
+
136
+ def extra_env
137
+ if @options[:extra_env]
138
+ @options[:extra_env].map do |k, v|
139
+ # Here the value must not be escaped
140
+ ['-e', "#{k}=#{v}"]
141
+ end.flatten
142
+ else
143
+ []
144
+ end
145
+ end
146
+
147
+ def port_forwards
148
+ args = (0...num_exposed_ports).map do |i|
149
+ host_port = @options[:mongo_only] + i
150
+ container_port = 27017 + i
151
+ ['-p', "#{host_port}:#{container_port}"]
152
+ end.flatten
153
+
154
+ if @env['OCSP_ALGORITHM'] && !@env['OCSP_VERIFIER']
155
+ args += %w(-p 8100:8100)
156
+ end
157
+
158
+ args
159
+ end
160
+
161
+ def run_command(cmd)
162
+ if pid = fork
163
+ Process.wait(pid)
164
+ unless $?.exitstatus == 0
165
+ raise "Process exited with code #{$?.exitstatus}"
166
+ end
167
+ else
168
+ exec(*cmd)
169
+ end
170
+ end
171
+
172
+ def distro
173
+ @options[:distro] || 'ubuntu1604'
174
+ end
175
+
176
+ BASE_IMAGES = {
177
+ 'debian81' => 'debian:jessie',
178
+ 'debian92' => 'debian:stretch',
179
+ 'ubuntu1404' => 'ubuntu:trusty',
180
+ 'ubuntu1604' => 'ubuntu:xenial',
181
+ 'ubuntu1804' => 'ubuntu:bionic',
182
+ 'rhel62' => 'centos:6',
183
+ 'rhel70' => 'centos:7',
184
+ }.freeze
185
+
186
+ def base_image
187
+ BASE_IMAGES[distro] or raise "Unknown distro: #{distro}"
188
+ end
189
+
190
+ def ruby
191
+ @env['RVM_RUBY']
192
+ end
193
+
194
+ def ruby_head?
195
+ ruby == 'ruby-head'
196
+ end
197
+
198
+ def server_version
199
+ @env['MONGODB_VERSION']
200
+ end
201
+
202
+ def script
203
+ @options[:script] || options.fetch(:default_script)
204
+ end
205
+
206
+ def debian?
207
+ distro =~ /debian|ubuntu/
208
+ end
209
+
210
+ def preload?
211
+ !!@options[:preload]
212
+ end
213
+
214
+ def interactive?
215
+ !!@options[:interactive]
216
+ end
217
+
218
+ def project_lib_subdir
219
+ options.fetch(:project_lib_subdir)
220
+ end
221
+
222
+ def server_download_url
223
+ @server_download_url ||= ServerVersionRegistry.new(server_version, distro).download_url
224
+ end
225
+
226
+ def libmongocrypt_path
227
+ case distro
228
+ when /ubuntu1604/
229
+ "./ubuntu1604/nocrypto/lib64/libmongocrypt.so"
230
+ when /ubuntu1804/
231
+ "./ubuntu1804-64/nocrypto/lib64/libmongocrypt.so"
232
+ when /debian92/
233
+ "./debian92/nocrypto/lib64/libmongocrypt.so"
234
+ else
235
+ raise "This script does not support running FLE tests on #{distro}. Use ubuntu1604, ubuntu1804 or debian92 instead"
236
+ end
237
+ end
238
+
239
+ def expose?
240
+ !!@options[:mongo_only]
241
+ end
242
+
243
+ def fle?
244
+ %w(1 true yes).include?(@env['FLE']&.downcase)
245
+ end
246
+
247
+ def num_exposed_ports
248
+ case @env['TOPOLOGY'] || 'standalone'
249
+ when 'standalone'
250
+ 1
251
+ when 'replica-set'
252
+ 3
253
+ when 'sharded-cluster'
254
+ if @env['SINGLE_MONGOS']
255
+ 1
256
+ else
257
+ 2
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end