mongoid 7.0.11 → 7.0.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ module Mrss
5
+ module LiteConstraints
6
+
7
+ # Constrain tests that use TimeoutInterrupt to MRI (and Unix).
8
+ def require_mri
9
+ before(:all) do
10
+ unless SpecConfig.instance.mri?
11
+ skip "MRI required, we have #{SpecConfig.instance.platform}"
12
+ end
13
+ end
14
+ end
15
+
16
+ def require_jruby
17
+ before(:all) do
18
+ unless BSON::Environment.jruby?
19
+ skip "JRuby required, we have #{SpecConfig.instance.platform}"
20
+ end
21
+ end
22
+ end
23
+
24
+ # This is for marking tests that fail on JRuby that should
25
+ # in principle work (as opposed to being fundamentally incompatible
26
+ # with JRuby).
27
+ # Often times these failures happen only in Evergreen.
28
+ def fails_on_jruby(version=nil)
29
+ before(:all) do
30
+ if BSON::Environment.jruby?
31
+ if version
32
+ min_parts = version.split('.').map(&:to_i)
33
+ actual_parts = JRUBY_VERSION.split('.').map(&:to_i)[0...min_parts.length]
34
+ actual = actual_parts.join('.')
35
+ if actual <= version
36
+ skip "Fails on jruby through #{version}"
37
+ end
38
+ else
39
+ skip "Fails on jruby"
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ # Indicates that the respective test uses the internet in some capacity,
46
+ # for example the test resolves SRV DNS records.
47
+ def require_external_connectivity
48
+ before(:all) do
49
+ if ENV['EXTERNAL_DISABLED']
50
+ skip "Test requires external connectivity"
51
+ end
52
+ end
53
+ end
54
+
55
+ def require_mongo_kerberos
56
+ before(:all) do
57
+ # TODO Use a more generic environment variable name if/when
58
+ # Mongoid tests get Kerberos configurations.
59
+ unless %w(1 yes true).include?(ENV['MONGO_RUBY_DRIVER_KERBEROS']&.downcase)
60
+ skip 'Set MONGO_RUBY_DRIVER_KERBEROS=1 in environment to run Kerberos unit tests'
61
+ end
62
+ require 'mongo_kerberos'
63
+ end
64
+ end
65
+
66
+ def require_linting
67
+ before(:all) do
68
+ unless Mongo::Lint.enabled?
69
+ skip "Linting is not enabled"
70
+ end
71
+ end
72
+ end
73
+
74
+ # Some tests will fail if linting is enabled:
75
+ # 1. Tests that pass invalid options to client, etc. which the linter
76
+ # rejects.
77
+ # 2. Tests that set expectations on topologies, server descriptions, etc.
78
+ # (since setting expectations requires mutating said objects, and when
79
+ # linting is on those objects are frozen).
80
+ def require_no_linting
81
+ before(:all) do
82
+ if Mongo::Lint.enabled?
83
+ skip "Linting is enabled"
84
+ end
85
+ end
86
+ end
87
+
88
+ def require_libmongocrypt
89
+ before(:all) do
90
+ unless ENV['LIBMONGOCRYPT_PATH']
91
+ skip 'Test requires path to libmongocrypt to be specified in LIBMONGOCRYPT_PATH env variable'
92
+ end
93
+ end
94
+ end
95
+
96
+ def require_no_libmongocrypt
97
+ before(:all) do
98
+ if ENV['LIBMONGOCRYPT_PATH']
99
+ skip 'Test requires libmongocrypt to not be configured'
100
+ end
101
+ end
102
+ end
103
+
104
+ def require_aws_auth
105
+ before(:all) do
106
+ unless (ENV['AUTH'] || '') =~ /^aws/
107
+ skip 'This test requires AUTH=aws* and an appropriately configured runtime environment'
108
+ end
109
+ end
110
+ end
111
+
112
+ def require_ec2_host
113
+ before(:all) do
114
+ if $have_aws.nil?
115
+ $have_aws = begin
116
+ require 'open-uri'
117
+ begin
118
+ Timeout.timeout(3.81) do
119
+ URI.parse('http://169.254.169.254/latest/meta-data/profile').open.read
120
+ end
121
+ true
122
+ # When trying to use the EC2 metadata endpoint on ECS:
123
+ # Errno::EINVAL: Failed to open TCP connection to 169.254.169.254:80 (Invalid argument - connect(2) for "169.254.169.254" port 80)
124
+ rescue Timeout::Error, Errno::ETIMEDOUT, Errno::EINVAL, OpenURI::HTTPError => $aws_error
125
+ false
126
+ end
127
+ end
128
+ end
129
+ unless $have_aws
130
+ skip "EC2 instance metadata is not available - assuming not running on an EC2 instance: #{$aws_error.class}: #{$aws_error}"
131
+ end
132
+ end
133
+ end
134
+
135
+ def require_stress
136
+ before(:all) do
137
+ if !SpecConfig.instance.stress?
138
+ skip 'Set STRESS=1 in environment to run stress tests'
139
+ end
140
+ end
141
+ end
142
+
143
+ def require_fork
144
+ before(:all) do
145
+ if !SpecConfig.instance.fork?
146
+ skip 'Set FORK=1 in environment to run fork tests'
147
+ end
148
+ end
149
+ end
150
+
151
+ def require_ocsp
152
+ before(:all) do
153
+ if !SpecConfig.instance.ocsp?
154
+ skip 'Set OCSP=1 in environment to run OCSP tests'
155
+ end
156
+ end
157
+ end
158
+
159
+ def require_ocsp_verifier
160
+ before(:all) do
161
+ if !SpecConfig.instance.ocsp_verifier?
162
+ skip 'Set OCSP_VERIFIER=1 in environment to run OCSP verifier tests'
163
+ end
164
+ end
165
+ end
166
+
167
+ def require_ocsp_connectivity
168
+ before(:all) do
169
+ if !SpecConfig.instance.ocsp_connectivity?
170
+ skip 'Set OCSP_CONNECTIVITY=pass or OCSP_CONNECTIVITY=fail in environment to run OCSP connectivity tests'
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,69 @@
1
+ autoload :JSON, 'json'
2
+ require 'open-uri'
3
+
4
+ module Mrss
5
+ class ServerVersionRegistry
6
+ def initialize(desired_version, arch)
7
+ @desired_version, @arch = desired_version, arch
8
+ end
9
+
10
+ attr_reader :desired_version, :arch
11
+
12
+ def download_url
13
+ @download_url ||= begin
14
+ info = JSON.load(uri_open('http://downloads.mongodb.org/current.json').read)
15
+ version = info['versions'].detect do |version|
16
+ version['version'].start_with?(desired_version) &&
17
+ !version['version'].include?('-') &&
18
+ # Sometimes the download situation is borked and there is a release
19
+ # with no downloads... skip those.
20
+ !version['downloads'].empty?
21
+ end
22
+ # Allow RC releases if there isn't a GA release.
23
+ version ||= info['versions'].detect do |version|
24
+ version['version'].start_with?(desired_version) &&
25
+ # Sometimes the download situation is borked and there is a release
26
+ # with no downloads... skip those.
27
+ !version['downloads'].empty?
28
+ end
29
+ if version.nil?
30
+ info = JSON.load(URI.parse('http://downloads.mongodb.org/full.json').open.read)
31
+ versions = info['versions'].select do |version|
32
+ version['version'].start_with?(desired_version) &&
33
+ !version['downloads'].empty?
34
+ end
35
+ # Get rid of rc, beta etc. versions if there is a GA release.
36
+ if versions.any? { |version| !version.include?('-') }
37
+ versions.delete_if do |version|
38
+ version['version'].include?('-')
39
+ end
40
+ end
41
+ # Versions are ordered with newest first, take the first one i.e. the most
42
+ # recent one.
43
+ version = versions.first
44
+ if version.nil?
45
+ STDERR.puts "Error: no version #{desired_version}"
46
+ exit 2
47
+ end
48
+ end
49
+ dl = version['downloads'].detect do |dl|
50
+ dl['archive']['url'].index("enterprise-#{arch}") &&
51
+ dl['arch'] == 'x86_64'
52
+ end
53
+ unless dl
54
+ STDERR.puts "Error: no download for #{arch} for #{version['version']}"
55
+ exit 2
56
+ end
57
+ url = dl['archive']['url']
58
+ end
59
+ end
60
+
61
+ def uri_open(*args)
62
+ if RUBY_VERSION < '2.5'
63
+ open(*args)
64
+ else
65
+ URI.open(*args)
66
+ end
67
+ end
68
+ end
69
+ end