mongoid 7.1.6 → 7.1.10
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +1 -1
- data/Rakefile +31 -0
- data/lib/mongoid.rb +1 -0
- data/lib/mongoid/association/embedded/embeds_many/proxy.rb +1 -1
- data/lib/mongoid/association/proxy.rb +1 -1
- data/lib/mongoid/association/referenced/has_many/enumerable.rb +1 -1
- data/lib/mongoid/association/referenced/has_many/proxy.rb +1 -1
- data/lib/mongoid/attributes.rb +8 -1
- data/lib/mongoid/criteria.rb +1 -1
- data/lib/mongoid/criteria/queryable/selector.rb +0 -4
- data/lib/mongoid/document.rb +3 -2
- data/lib/mongoid/errors/mongoid_error.rb +1 -1
- data/lib/mongoid/interceptable.rb +4 -2
- data/lib/mongoid/reloadable.rb +5 -0
- data/lib/mongoid/validatable/associated.rb +1 -1
- data/lib/mongoid/validatable/presence.rb +3 -3
- data/lib/mongoid/validatable/uniqueness.rb +1 -1
- data/lib/mongoid/version.rb +1 -1
- data/lib/rails/generators/mongoid/config/config_generator.rb +8 -1
- data/lib/rails/generators/mongoid/config/templates/mongoid.yml +1 -1
- data/spec/app/models/address.rb +4 -0
- data/spec/app/models/customer.rb +11 -0
- data/spec/app/models/customer_address.rb +12 -0
- data/spec/app/models/dictionary.rb +6 -0
- data/spec/app/models/person.rb +9 -0
- data/spec/integration/app_spec.rb +178 -88
- data/spec/integration/callbacks_models.rb +49 -0
- data/spec/integration/callbacks_spec.rb +216 -0
- data/spec/integration/document_spec.rb +21 -0
- data/spec/lite_spec_helper.rb +6 -6
- data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +50 -0
- data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +17 -4
- data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +17 -0
- data/spec/mongoid/atomic/paths_spec.rb +41 -0
- data/spec/mongoid/attributes_spec.rb +241 -0
- data/spec/mongoid/clients/options_spec.rb +2 -0
- data/spec/mongoid/contextual/atomic_spec.rb +17 -4
- data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +36 -0
- data/spec/mongoid/criteria_spec.rb +4 -0
- data/spec/mongoid/document_query_spec.rb +51 -0
- data/spec/mongoid/errors/mongoid_error_spec.rb +20 -8
- data/spec/mongoid/factory_spec.rb +2 -2
- data/spec/mongoid/persistable/savable_spec.rb +4 -4
- data/spec/mongoid/persistable/settable_spec.rb +30 -0
- data/spec/mongoid/persistable_spec.rb +2 -2
- data/spec/shared/bin/get-mongodb-download-url +17 -0
- data/spec/shared/bin/s3-copy +45 -0
- data/spec/shared/bin/s3-upload +69 -0
- data/spec/shared/lib/mrss/cluster_config.rb +19 -4
- data/spec/shared/lib/mrss/constraints.rb +62 -6
- data/spec/shared/lib/mrss/docker_runner.rb +271 -0
- data/spec/shared/lib/mrss/lite_constraints.rb +16 -0
- data/spec/shared/lib/mrss/server_version_registry.rb +115 -0
- data/spec/shared/lib/mrss/spec_organizer.rb +32 -2
- data/spec/shared/lib/mrss/utils.rb +15 -0
- data/spec/shared/share/Dockerfile.erb +322 -0
- data/spec/shared/share/haproxy-1.conf +16 -0
- data/spec/shared/share/haproxy-2.conf +17 -0
- data/spec/shared/shlib/distro.sh +73 -0
- data/spec/shared/shlib/server.sh +317 -0
- data/spec/shared/shlib/set_env.sh +131 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/support/constraints.rb +0 -226
- data/spec/support/spec_config.rb +8 -0
- metadata +542 -480
- metadata.gz.sig +0 -0
- data/spec/support/child_process_helper.rb +0 -76
- 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(
|
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(
|
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!(:
|
173
|
-
person.
|
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
|
-
"
|
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.
|
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
|
@@ -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' &&
|
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
|
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
|
-
|
200
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|