mongoid 7.1.6 → 7.1.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) 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 +1 -1
  5. data/Rakefile +31 -0
  6. data/lib/mongoid.rb +1 -0
  7. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +1 -1
  8. data/lib/mongoid/association/proxy.rb +1 -1
  9. data/lib/mongoid/association/referenced/has_many/enumerable.rb +1 -1
  10. data/lib/mongoid/association/referenced/has_many/proxy.rb +1 -1
  11. data/lib/mongoid/attributes.rb +8 -1
  12. data/lib/mongoid/criteria.rb +1 -1
  13. data/lib/mongoid/criteria/queryable/selector.rb +0 -4
  14. data/lib/mongoid/document.rb +3 -2
  15. data/lib/mongoid/errors/mongoid_error.rb +1 -1
  16. data/lib/mongoid/interceptable.rb +4 -2
  17. data/lib/mongoid/reloadable.rb +5 -0
  18. data/lib/mongoid/validatable/associated.rb +1 -1
  19. data/lib/mongoid/validatable/presence.rb +3 -3
  20. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  21. data/lib/mongoid/version.rb +1 -1
  22. data/lib/rails/generators/mongoid/config/config_generator.rb +8 -1
  23. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +1 -1
  24. data/spec/app/models/address.rb +4 -0
  25. data/spec/app/models/customer.rb +11 -0
  26. data/spec/app/models/customer_address.rb +12 -0
  27. data/spec/app/models/dictionary.rb +6 -0
  28. data/spec/app/models/person.rb +9 -0
  29. data/spec/integration/app_spec.rb +178 -88
  30. data/spec/integration/callbacks_models.rb +49 -0
  31. data/spec/integration/callbacks_spec.rb +216 -0
  32. data/spec/integration/document_spec.rb +21 -0
  33. data/spec/lite_spec_helper.rb +6 -6
  34. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +50 -0
  35. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +17 -4
  36. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +17 -0
  37. data/spec/mongoid/atomic/paths_spec.rb +41 -0
  38. data/spec/mongoid/attributes_spec.rb +241 -0
  39. data/spec/mongoid/clients/options_spec.rb +2 -0
  40. data/spec/mongoid/contextual/atomic_spec.rb +17 -4
  41. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +36 -0
  42. data/spec/mongoid/criteria_spec.rb +4 -0
  43. data/spec/mongoid/document_query_spec.rb +51 -0
  44. data/spec/mongoid/errors/mongoid_error_spec.rb +20 -8
  45. data/spec/mongoid/factory_spec.rb +2 -2
  46. data/spec/mongoid/persistable/savable_spec.rb +4 -4
  47. data/spec/mongoid/persistable/settable_spec.rb +30 -0
  48. data/spec/mongoid/persistable_spec.rb +2 -2
  49. data/spec/shared/bin/get-mongodb-download-url +17 -0
  50. data/spec/shared/bin/s3-copy +45 -0
  51. data/spec/shared/bin/s3-upload +69 -0
  52. data/spec/shared/lib/mrss/cluster_config.rb +19 -4
  53. data/spec/shared/lib/mrss/constraints.rb +62 -6
  54. data/spec/shared/lib/mrss/docker_runner.rb +271 -0
  55. data/spec/shared/lib/mrss/lite_constraints.rb +16 -0
  56. data/spec/shared/lib/mrss/server_version_registry.rb +115 -0
  57. data/spec/shared/lib/mrss/spec_organizer.rb +32 -2
  58. data/spec/shared/lib/mrss/utils.rb +15 -0
  59. data/spec/shared/share/Dockerfile.erb +322 -0
  60. data/spec/shared/share/haproxy-1.conf +16 -0
  61. data/spec/shared/share/haproxy-2.conf +17 -0
  62. data/spec/shared/shlib/distro.sh +73 -0
  63. data/spec/shared/shlib/server.sh +317 -0
  64. data/spec/shared/shlib/set_env.sh +131 -0
  65. data/spec/spec_helper.rb +3 -1
  66. data/spec/support/constraints.rb +0 -226
  67. data/spec/support/spec_config.rb +8 -0
  68. metadata +542 -480
  69. metadata.gz.sig +0 -0
  70. data/spec/support/child_process_helper.rb +0 -76
  71. data/spec/support/lite_constraints.rb +0 -22
@@ -109,11 +109,11 @@ describe Mongoid::Factory do
109
109
  context "when the attributes are nil" do
110
110
 
111
111
  let(:document) do
112
- described_class.from_db(Address, nil)
112
+ described_class.from_db(Animal, nil)
113
113
  end
114
114
 
115
115
  it "generates based on the provided class" do
116
- expect(document).to be_a(Address)
116
+ expect(document).to be_a(Animal)
117
117
  end
118
118
 
119
119
  it "sets the attributes to empty" do
@@ -169,8 +169,8 @@ describe Mongoid::Persistable::Savable do
169
169
  Person.create(title: "Blah")
170
170
  end
171
171
 
172
- let!(:address) do
173
- person.addresses.build(street: "Bond St")
172
+ let!(:symptom) do
173
+ person.symptoms.build(name: "Headache")
174
174
  end
175
175
 
176
176
  let!(:name) do
@@ -193,7 +193,7 @@ describe Mongoid::Persistable::Savable do
193
193
  "name.first_name" => "Ryan"
194
194
  },
195
195
  "$push"=> {
196
- "addresses" => { '$each' => [ { "_id" => address.id, "street" => "Bond St" } ] }
196
+ "symptoms" => { '$each' => [ { "_id" => symptom.id, "name" => "Headache" } ] }
197
197
  }
198
198
  })
199
199
  end
@@ -205,7 +205,7 @@ describe Mongoid::Persistable::Savable do
205
205
  end
206
206
 
207
207
  it "saves embedded many relations" do
208
- expect(person.addresses.first.street).to eq("Bond St")
208
+ expect(person.symptoms.first.name).to eq("Headache")
209
209
  end
210
210
 
211
211
  it "saves embedded one relations" do
@@ -512,4 +512,34 @@ describe Mongoid::Persistable::Settable do
512
512
  end
513
513
  end
514
514
  end
515
+
516
+ context "when the field being set was projected out" do
517
+ let(:full_agent) do
518
+ Agent.create!(title: "Double-Oh Eight")
519
+ end
520
+
521
+ let(:agent) do
522
+ Agent.where(_id: full_agent.id).only(:dob).first
523
+ end
524
+
525
+ context 'field exists in database' do
526
+ it "raises MissingAttributeError" do
527
+ lambda do
528
+ agent.set(title: '008')
529
+ end.should raise_error(ActiveModel::MissingAttributeError)
530
+
531
+ expect(agent.reload.title).to eq 'Double-Oh Eight'
532
+ end
533
+ end
534
+
535
+ context 'field does not exist in database' do
536
+ it "raises MissingAttributeError" do
537
+ lambda do
538
+ agent.set(number: '008')
539
+ end.should raise_error(ActiveModel::MissingAttributeError)
540
+
541
+ expect(agent.reload.read_attribute(:number)).to be nil
542
+ end
543
+ end
544
+ end
515
545
  end
@@ -178,8 +178,8 @@ describe Mongoid::Persistable do
178
178
 
179
179
  before do
180
180
  class Band
181
- def my_updates(*args)
182
- atomically(*args) do |d|
181
+ def my_updates(**args)
182
+ atomically(**args) do |d|
183
183
  d.set(name: "Placebo")
184
184
  d.unset(:origin)
185
185
  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,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aws-sdk-s3'
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: s3-copy options"
9
+
10
+ opts.on("-r", "--region=REGION", "AWS region to use (default us-east-1)") do |v|
11
+ options[:region] = v
12
+ end
13
+
14
+ opts.on("-p", "--param=KEY=VALUE", "Specify parameter for new files") do |v|
15
+ options[:params] ||= {}
16
+ k, v = v.split('=', 2)
17
+ options[:params][k.to_sym] = v
18
+ end
19
+
20
+ opts.on("-f", "--from=BUCKET:PATH", "Bucket name and key (or path) to copy from") do |v|
21
+ options[:from] = v
22
+ end
23
+
24
+ opts.on("-t", "--to=BUCKET:PATH", "Bucket name and key (or path) to write to (may be specified more than once)") do |v|
25
+ options[:to] ||= []
26
+ options[:to] << v
27
+ end
28
+ end.parse!
29
+
30
+ ENV['AWS_REGION'] ||= options[:region] || 'us-east-1'
31
+
32
+ bucket, key = options.fetch(:from).split(':', 2)
33
+
34
+ s3 = Aws::S3::Client.new
35
+
36
+ options.fetch(:to).each do |dest|
37
+ STDERR.puts "Copying to #{dest}"
38
+ dbucket, dkey = dest.split(':', 2)
39
+ s3.copy_object(
40
+ bucket: dbucket,
41
+ key: dkey,
42
+ copy_source: "/#{bucket}/#{key}",
43
+ **options[:params] || {},
44
+ )
45
+ end
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'aws-sdk-s3'
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: s3-upload options"
9
+
10
+ opts.on("-r", "--region=REGION", "AWS region to use (default us-east-1)") do |v|
11
+ options[:region] = v
12
+ end
13
+
14
+ opts.on("-p", "--param=KEY=VALUE", "Specify parameter for S3 upload") do |v|
15
+ options[:params] ||= {}
16
+ k, v = v.split('=', 2)
17
+ options[:params][k.to_sym] = v
18
+ end
19
+
20
+ opts.on("-f", "--file=PATH", "Path to the file to upload, - to upload standard input") do |v|
21
+ options[:file] = v
22
+ end
23
+
24
+ opts.on("-w", "--write=BUCKET:PATH", "Bucket name and key (or path) to upload to") do |v|
25
+ options[:write] = v
26
+ end
27
+
28
+ opts.on("-c", "--copy=BUCKET:PATH", "Bucket name and key (or path) to copy to (may be specified more than once)") do |v|
29
+ options[:copy] ||= []
30
+ options[:copy] << v
31
+ end
32
+ end.parse!
33
+
34
+ ENV['AWS_REGION'] ||= options[:region] || 'us-east-1'
35
+
36
+ def upload(f, options)
37
+ s3 = Aws::S3::Client.new
38
+ write = options.fetch(:write)
39
+ STDERR.puts "Writing #{write}"
40
+ bucket, key = write.split(':', 2)
41
+ s3.put_object(
42
+ body: f.read,
43
+ bucket: bucket,
44
+ key: key,
45
+ **options[:params] || {},
46
+ )
47
+ if copy = options[:copy]
48
+ copy.each do |dest|
49
+ STDERR.puts "Copying to #{dest}"
50
+ dbucket, dkey = dest.split(':', 2)
51
+ s3.copy_object(
52
+ bucket: dbucket,
53
+ key: dkey,
54
+ copy_source: "/#{bucket}/#{key}",
55
+ **options[:params] || {},
56
+ )
57
+ end
58
+ end
59
+ end
60
+
61
+ if options[:file] == '-'
62
+ upload(STDIN, options)
63
+ elsif options[:file]
64
+ File.open(options[:file]) do |f|
65
+ upload(f, options)
66
+ end
67
+ else
68
+ upload(STDIN, options)
69
+ end
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
1
4
  # ClusterConfig requires ClientRegistry class provided by the host project.
2
5
 
3
6
  require 'singleton'
@@ -12,6 +15,11 @@ module Mrss
12
15
  @single_server
13
16
  end
14
17
 
18
+ def sharded_ish?
19
+ determine_cluster_config
20
+ @topology == :sharded || @topology == :load_balanced
21
+ end
22
+
15
23
  def replica_set_name
16
24
  determine_cluster_config
17
25
  @replica_set_name
@@ -45,7 +53,7 @@ module Mrss
45
53
  raise "Deployment server version not known - check that connection to deployment succeeded"
46
54
  end
47
55
 
48
- if server_version >= '3.4' && topology != :sharded
56
+ if server_version >= '3.4' && !sharded_ish?
49
57
  fcv
50
58
  else
51
59
  if short_server_version == '4.1'
@@ -82,6 +90,11 @@ module Mrss
82
90
  @primary_description
83
91
  end
84
92
 
93
+ def server_parameters
94
+ determine_cluster_config
95
+ @server_parameters
96
+ end
97
+
85
98
  # Try running a command on the admin database to see if the mongod was
86
99
  # started with auth.
87
100
  def auth_enabled?
@@ -107,7 +120,7 @@ module Mrss
107
120
  :mmapv1
108
121
  else
109
122
  client = ClientRegistry.instance.global_client('root_authorized')
110
- if topology == :sharded
123
+ if sharded_ish?
111
124
  shards = client.use(:admin).command(listShards: 1).first
112
125
  if shards['shards'].empty?
113
126
  raise 'Shards are empty'
@@ -196,8 +209,10 @@ module Mrss
196
209
  @server_version = build_info['version']
197
210
  @enterprise = build_info['modules'] && build_info['modules'].include?('enterprise')
198
211
 
199
- if @topology != :sharded && short_server_version >= '3.4'
200
- rv = client.use(:admin).command(getParameter: 1, featureCompatibilityVersion: 1).first['featureCompatibilityVersion']
212
+ @server_parameters = client.use(:admin).command(getParameter: '*').first
213
+
214
+ if !sharded_ish? && short_server_version >= '3.4'
215
+ rv = @server_parameters['featureCompatibilityVersion']
201
216
  @fcv = rv['version'] || rv
202
217
  end
203
218
  end
@@ -52,7 +52,7 @@ module Mrss
52
52
  end
53
53
 
54
54
  def require_topology(*topologies)
55
- invalid_topologies = topologies - [:single, :replica_set, :sharded]
55
+ invalid_topologies = topologies - [:single, :replica_set, :sharded, :load_balanced]
56
56
 
57
57
  unless invalid_topologies.empty?
58
58
  raise ArgumentError, "Invalid topologies requested: #{invalid_topologies.join(', ')}"
@@ -82,7 +82,7 @@ module Mrss
82
82
  unless ClusterConfig.instance.server_version >= '4.0'
83
83
  skip 'Transactions tests in a replica set topology require server 4.0+'
84
84
  end
85
- when :sharded
85
+ when :sharded, :load_balanced
86
86
  unless ClusterConfig.instance.server_version >= '4.2'
87
87
  skip 'Transactions tests in a sharded cluster topology require server 4.2+'
88
88
  end
@@ -113,6 +113,14 @@ module Mrss
113
113
  end
114
114
  end
115
115
 
116
+ def require_retry_writes
117
+ before(:all) do
118
+ unless SpecConfig.instance.retry_writes?
119
+ skip "Retry writes is disabled"
120
+ end
121
+ end
122
+ end
123
+
116
124
  def require_no_retry_writes
117
125
  before(:all) do
118
126
  if SpecConfig.instance.retry_writes?
@@ -156,6 +164,24 @@ module Mrss
156
164
  end
157
165
  end
158
166
 
167
+ def require_zstd_compression
168
+ before(:all) do
169
+ compressors = SpecConfig.instance.compressors
170
+ unless compressors && compressors.include?('zstd')
171
+ skip "Zstd compression is not enabled"
172
+ end
173
+ end
174
+ end
175
+
176
+ def require_no_zstd_compression
177
+ before(:all) do
178
+ compressors = SpecConfig.instance.compressors
179
+ if compressors && compressors.include?('zstd')
180
+ skip "Zstd compression is enabled"
181
+ end
182
+ end
183
+ end
184
+
159
185
  def require_no_compression
160
186
  before(:all) do
161
187
  if SpecConfig.instance.compressors
@@ -223,7 +249,9 @@ module Mrss
223
249
  # (mongos 4.0+ overrides the write concern)
224
250
  def require_set_write_concern
225
251
  before(:all) do
226
- if ClusterConfig.instance.topology == :sharded && ClusterConfig.instance.short_server_version >= '4.0'
252
+ if %i(sharded load_balanced).include?(ClusterConfig.instance.topology) &&
253
+ ClusterConfig.instance.short_server_version >= '4.0'
254
+ then
227
255
  skip "mongos 4.0+ overrides write concern"
228
256
  end
229
257
  end
@@ -247,7 +275,9 @@ module Mrss
247
275
 
248
276
  def require_wired_tiger
249
277
  before(:all) do
250
- if ClusterConfig.instance.storage_engine != :wired_tiger
278
+ # Storage detection fails for serverless instances. However, it is safe to
279
+ # assume that a serverless instance uses WiredTiger Storage Engine.
280
+ if !SpecConfig.instance.serverless? && ClusterConfig.instance.storage_engine != :wired_tiger
251
281
  skip 'Test requires WiredTiger storage engine'
252
282
  end
253
283
  end
@@ -256,7 +286,9 @@ module Mrss
256
286
  def require_wired_tiger_on_36
257
287
  before(:all) do
258
288
  if ClusterConfig.instance.short_server_version >= '3.6'
259
- if ClusterConfig.instance.storage_engine != :wired_tiger
289
+ # Storage detection fails for serverless instances. However, it is safe to
290
+ # assume that a serverless instance uses WiredTiger Storage Engine.
291
+ if !SpecConfig.instance.serverless? && ClusterConfig.instance.storage_engine != :wired_tiger
260
292
  skip 'Test requires WiredTiger storage engine on 3.6+ servers'
261
293
  end
262
294
  end
@@ -265,7 +297,7 @@ module Mrss
265
297
 
266
298
  def require_mmapv1
267
299
  before(:all) do
268
- if ClusterConfig.instance.storage_engine != :mmapv1
300
+ if SpecConfig.instance.serverless? || ClusterConfig.instance.storage_engine != :mmapv1
269
301
  skip 'Test requires MMAPv1 storage engine'
270
302
  end
271
303
  end
@@ -308,5 +340,29 @@ module Mrss
308
340
  end
309
341
  end
310
342
  end
343
+
344
+ def require_required_api_version
345
+ before(:all) do
346
+ unless ENV['API_VERSION_REQUIRED'] == '1'
347
+ skip 'Set API_VERSION_REQUIRED=1 to run this test'
348
+ end
349
+ end
350
+ end
351
+
352
+ def require_no_required_api_version
353
+ before(:all) do
354
+ if ENV['API_VERSION_REQUIRED'] == '1'
355
+ skip 'Cannot have API_VERSION_REQUIRED=1 to run this test'
356
+ end
357
+ end
358
+ end
359
+
360
+ def require_unix_socket
361
+ before(:all) do
362
+ if ENV['TOPOLOGY'] == 'load-balanced'
363
+ skip 'Load balancer does not listen on Unix sockets'
364
+ end
365
+ end
366
+ end
311
367
  end
312
368
  end
@@ -0,0 +1,271 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'optparse'
5
+ require 'erb'
6
+ autoload :Dotenv, 'dotenv'
7
+
8
+ module Mrss
9
+ autoload :ServerVersionRegistry, 'mrss/server_version_registry'
10
+
11
+ class DockerRunner
12
+ def initialize(**opts)
13
+ # These options are required:
14
+ opts.fetch(:image_tag)
15
+ opts.fetch(:dockerfile_path)
16
+ opts.fetch(:default_script)
17
+ opts.fetch(:project_lib_subdir)
18
+
19
+ @options = opts
20
+ end
21
+
22
+ attr_reader :options
23
+
24
+ def run
25
+ process_arguments
26
+ unless @options[:exec_only]
27
+ create_dockerfile
28
+ create_image
29
+ end
30
+ if @options[:mongo_only]
31
+ run_deployment
32
+ else
33
+ run_tests
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def process_arguments
40
+ #@options = {}
41
+ OptionParser.new do |opts|
42
+ opts.banner = "Usage: test-on-docker [-d distro] [evergreen_key=value ...]"
43
+
44
+ opts.on("-a", "--add-env=PATH", "Load environment variables from PATH in .env format") do |path|
45
+ @options[:extra_env] ||= {}
46
+ unless File.exist?(path)
47
+ raise "-a option references nonexistent file #{path}"
48
+ end
49
+ Dotenv.parse(path).each do |k, v|
50
+ @options[:extra_env][k] = v
51
+ end
52
+ end
53
+
54
+ opts.on("-d", "--distro=DISTRO", "Distro to use") do |v|
55
+ @options[:distro] = v
56
+ end
57
+
58
+ opts.on('-e', '--exec-only', 'Execute tests using existing Dockerfile (for offline user)') do |v|
59
+ @options[:exec_only] = v
60
+ end
61
+
62
+ opts.on('-m', '--mongo-only=PORT', 'Start the MongoDB deployment and expose it to host on ports starting with PORT') do |v|
63
+ @options[:mongo_only] = v.to_i
64
+ end
65
+
66
+ opts.on('-p', '--preload', 'Preload Ruby toolchain and server binaries in docker') do |v|
67
+ @options[:preload] = v
68
+ end
69
+
70
+ opts.on('-s', '--script=SCRIPT', 'Test script to invoke') do |v|
71
+ @options[:script] = v
72
+ end
73
+
74
+ opts.on('-i', '--interactive', 'Interactive mode - disable per-test timeouts') do |v|
75
+ @options[:interactive] = v
76
+ end
77
+ end.parse!
78
+
79
+ @env = Hash[ARGV.map do |arg|
80
+ arg.split('=', 2)
81
+ end]
82
+
83
+ @env['RVM_RUBY'] ||= 'ruby-2.7'
84
+ unless ruby =~ /^j?ruby-/
85
+ raise "RVM_RUBY option is not in expected format: #{ruby}"
86
+ end
87
+
88
+ @env['MONGODB_VERSION'] ||= '4.4'
89
+ end
90
+
91
+ def create_dockerfile
92
+ template_path = File.join(File.dirname(__FILE__), '../../share/Dockerfile.erb')
93
+ result = ERB.new(File.read(template_path)).result(binding)
94
+ File.open(dockerfile_path, 'w') do |f|
95
+ f << result
96
+ end
97
+ end
98
+
99
+ def image_tag
100
+ options.fetch(:image_tag)
101
+ end
102
+
103
+ def dockerfile_path
104
+ options.fetch(:dockerfile_path)
105
+ end
106
+
107
+ def create_image
108
+ run_command(['docker', 'build',
109
+ '-t', image_tag,
110
+ '-f', dockerfile_path,
111
+ '.'])
112
+ end
113
+
114
+ BASE_TEST_COMMAND = %w(docker run --rm -i --tmpfs /tmpfs:exec).freeze
115
+
116
+ def run_tests
117
+ run_command(BASE_TEST_COMMAND + tty_arg + extra_env + [image_tag] +
118
+ script.split(/\s+/))
119
+ end
120
+
121
+ def run_deployment
122
+ run_command(BASE_TEST_COMMAND + tty_arg + extra_env + [
123
+ '-e', %q`TEST_CMD=watch -x bash -c "ps awwxu |egrep 'mongo|ocsp'"`,
124
+ '-e', 'BIND_ALL=true',
125
+ ] + port_forwards + [image_tag] + script.split(/\s+/))
126
+ end
127
+
128
+ def tty_arg
129
+ tty = File.open('/dev/stdin') do |f|
130
+ f.isatty
131
+ end
132
+ if tty
133
+ %w(-t --init)
134
+ else
135
+ []
136
+ end
137
+ end
138
+
139
+ def extra_env
140
+ if @options[:extra_env]
141
+ @options[:extra_env].map do |k, v|
142
+ # Here the value must not be escaped
143
+ ['-e', "#{k}=#{v}"]
144
+ end.flatten
145
+ else
146
+ []
147
+ end
148
+ end
149
+
150
+ def port_forwards
151
+ args = (0...num_exposed_ports).map do |i|
152
+ host_port = @options[:mongo_only] + i
153
+ container_port = 27017 + i
154
+ ['-p', "#{host_port}:#{container_port}"]
155
+ end.flatten
156
+
157
+ if @env['OCSP_ALGORITHM'] && !@env['OCSP_VERIFIER']
158
+ args += %w(-p 8100:8100)
159
+ end
160
+
161
+ args
162
+ end
163
+
164
+ def run_command(cmd)
165
+ if pid = fork
166
+ Process.wait(pid)
167
+ unless $?.exitstatus == 0
168
+ raise "Process exited with code #{$?.exitstatus}"
169
+ end
170
+ else
171
+ exec(*cmd)
172
+ end
173
+ end
174
+
175
+ def distro
176
+ @options[:distro] || 'ubuntu1604'
177
+ end
178
+
179
+ BASE_IMAGES = {
180
+ 'debian81' => 'debian:jessie',
181
+ 'debian92' => 'debian:stretch',
182
+ 'debian10' => 'debian:buster',
183
+ 'ubuntu1404' => 'ubuntu:trusty',
184
+ 'ubuntu1604' => 'ubuntu:xenial',
185
+ 'ubuntu1804' => 'ubuntu:bionic',
186
+ 'ubuntu2004' => 'ubuntu:focal',
187
+ 'rhel62' => 'centos:6',
188
+ 'rhel70' => 'centos:7',
189
+ }.freeze
190
+
191
+ def base_image
192
+ BASE_IMAGES[distro] or raise "Unknown distro: #{distro}"
193
+ end
194
+
195
+ def ruby
196
+ @env['RVM_RUBY']
197
+ end
198
+
199
+ def ruby_head?
200
+ ruby == 'ruby-head'
201
+ end
202
+
203
+ def system_ruby?
204
+ %w(1 true yes).include?(@env['SYSTEM_RUBY']&.downcase)
205
+ end
206
+
207
+ def server_version
208
+ @env['MONGODB_VERSION']
209
+ end
210
+
211
+ def script
212
+ @options[:script] || options.fetch(:default_script)
213
+ end
214
+
215
+ def debian?
216
+ distro =~ /debian|ubuntu/
217
+ end
218
+
219
+ def preload?
220
+ !!@options[:preload]
221
+ end
222
+
223
+ def interactive?
224
+ !!@options[:interactive]
225
+ end
226
+
227
+ def project_lib_subdir
228
+ options.fetch(:project_lib_subdir)
229
+ end
230
+
231
+ def server_download_url
232
+ @server_download_url ||= ServerVersionRegistry.new(server_version, distro).download_url
233
+ end
234
+
235
+ def libmongocrypt_path
236
+ case distro
237
+ when /ubuntu1604/
238
+ "./ubuntu1604/nocrypto/lib64/libmongocrypt.so"
239
+ when /ubuntu1804/
240
+ "./ubuntu1804-64/nocrypto/lib64/libmongocrypt.so"
241
+ when /debian92/
242
+ "./debian92/nocrypto/lib64/libmongocrypt.so"
243
+ else
244
+ raise "This script does not support running FLE tests on #{distro}. Use ubuntu1604, ubuntu1804 or debian92 instead"
245
+ end
246
+ end
247
+
248
+ def expose?
249
+ !!@options[:mongo_only]
250
+ end
251
+
252
+ def fle?
253
+ %w(1 true yes).include?(@env['FLE']&.downcase)
254
+ end
255
+
256
+ def num_exposed_ports
257
+ case @env['TOPOLOGY'] || 'standalone'
258
+ when 'standalone'
259
+ 1
260
+ when 'replica-set'
261
+ 3
262
+ when 'sharded-cluster'
263
+ if @env['SINGLE_MONGOS']
264
+ 1
265
+ else
266
+ 2
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end