bson 5.0.2 → 5.1.0

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -0
  3. data/ext/bson/extconf.rb +1 -1
  4. data/lib/bson/binary.rb +126 -4
  5. data/lib/bson/document.rb +8 -0
  6. data/lib/bson/object_id.rb +1 -1
  7. data/lib/bson/regexp.rb +3 -3
  8. data/lib/bson/vector.rb +44 -0
  9. data/lib/bson/version.rb +5 -16
  10. data/lib/bson.rb +1 -0
  11. data/spec/bson/document_as_spec.rb +14 -0
  12. data/spec/bson/vector_spec.rb +33 -0
  13. data/spec/runners/binary_vector.rb +78 -0
  14. data/spec/shared/LICENSE +20 -0
  15. data/spec/shared/bin/get-mongodb-download-url +17 -0
  16. data/spec/shared/bin/s3-copy +45 -0
  17. data/spec/shared/bin/s3-upload +69 -0
  18. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  19. data/spec/shared/lib/mrss/cluster_config.rb +231 -0
  20. data/spec/shared/lib/mrss/constraints.rb +378 -0
  21. data/spec/shared/lib/mrss/docker_runner.rb +298 -0
  22. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  23. data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
  24. data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
  25. data/spec/shared/lib/mrss/release/candidate.rb +284 -0
  26. data/spec/shared/lib/mrss/release/product_data.rb +144 -0
  27. data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
  28. data/spec/shared/lib/mrss/session_registry.rb +69 -0
  29. data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
  30. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  31. data/spec/shared/lib/mrss/utils.rb +37 -0
  32. data/spec/shared/lib/tasks/candidate.rake +64 -0
  33. data/spec/shared/share/Dockerfile.erb +251 -0
  34. data/spec/shared/share/haproxy-1.conf +16 -0
  35. data/spec/shared/share/haproxy-2.conf +17 -0
  36. data/spec/shared/shlib/config.sh +27 -0
  37. data/spec/shared/shlib/distro.sh +84 -0
  38. data/spec/shared/shlib/server.sh +423 -0
  39. data/spec/shared/shlib/set_env.sh +110 -0
  40. data/spec/spec_helper.rb +2 -0
  41. data/spec/spec_tests/binary_vector_spec.rb +82 -0
  42. data/spec/spec_tests/data/binary_vector/README.md +61 -0
  43. data/spec/spec_tests/data/binary_vector/float32.json +65 -0
  44. data/spec/spec_tests/data/binary_vector/int8.json +57 -0
  45. data/spec/spec_tests/data/binary_vector/packed_bit.json +83 -0
  46. data/spec/spec_tests/data/corpus/binary.json +30 -0
  47. metadata +70 -6
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ autoload :JSON, 'json'
5
+ require 'open-uri'
6
+
7
+ module Mrss
8
+ class ServerVersionRegistry
9
+ class Error < StandardError
10
+ end
11
+
12
+ class UnknownVersion < Error
13
+ end
14
+
15
+ class MissingDownloadUrl < Error
16
+ end
17
+
18
+ class BrokenDownloadUrl < Error
19
+ end
20
+
21
+ def initialize(desired_version, arch)
22
+ @desired_version, @arch = desired_version, arch.sub(/-arm$/, '')
23
+ end
24
+
25
+ attr_reader :desired_version, :arch
26
+
27
+ def target_arch
28
+ # can't use RbConfig::CONFIG["arch"] because JRuby doesn't
29
+ # return anything meaningful there.
30
+ #
31
+ # also, need to use `uname -a` instead of (e.g.) `uname -p`
32
+ # because debian (at least) does not return anything meaningful
33
+ # for `uname -p`.
34
+ uname = `uname -a`.strip
35
+ @target_arch ||= case uname
36
+ when /aarch/ then "aarch64"
37
+ when /x86/ then "x86_64"
38
+ else raise "unsupported architecture #{uname.inspect}"
39
+ end
40
+ end
41
+
42
+ def download_url
43
+ @download_url ||= begin
44
+ version, version_ok = detect_version(current_catalog)
45
+ if version.nil?
46
+ version, full_version_ok = detect_version(full_catalog)
47
+ version_ok ||= full_version_ok
48
+ end
49
+ if version.nil?
50
+ if version_ok
51
+ raise MissingDownloadUrl, "No downloads for version #{desired_version}"
52
+ else
53
+ raise UnknownVersion, "No version #{desired_version}"
54
+ end
55
+ end
56
+ dl = version['downloads'].detect do |dl|
57
+ dl['archive']['url'].index("enterprise-#{arch}") &&
58
+ dl['arch'] == target_arch
59
+ end
60
+ unless dl
61
+ raise MissingDownloadUrl, "No download for #{arch} for #{version['version']}"
62
+ end
63
+ url = dl['archive']['url']
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def uri_open(*args)
70
+ if RUBY_VERSION < '2.5'
71
+ open(*args)
72
+ else
73
+ URI.open(*args)
74
+ end
75
+ end
76
+
77
+ def detect_version(catalog)
78
+ candidate_versions = catalog['versions'].select do |version|
79
+ version['version'].start_with?(desired_version) &&
80
+ !version['version'].include?('-')
81
+ end
82
+ version_ok = !candidate_versions.empty?
83
+ # Sometimes the download situation is borked and there is a release
84
+ # with no downloads... skip those.
85
+ version = candidate_versions.detect do |version|
86
+ !version['downloads'].empty?
87
+ end
88
+ # Allow RC releases if there isn't a GA release.
89
+ if version.nil?
90
+ candidate_versions = catalog['versions'].select do |version|
91
+ version['version'].start_with?(desired_version)
92
+ end
93
+ version_ok ||= !candidate_versions.empty?
94
+ version = candidate_versions.detect do |version|
95
+ !version['downloads'].empty?
96
+ end
97
+ end
98
+ [version, version_ok]
99
+ end
100
+
101
+ def current_catalog
102
+ @current_catalog ||= begin
103
+ JSON.load(uri_open('http://downloads.mongodb.org/current.json').read)
104
+ end
105
+ end
106
+
107
+ def full_catalog
108
+ @full_catalog ||= begin
109
+ JSON.load(uri_open('http://downloads.mongodb.org/full.json').read)
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'singleton'
5
+
6
+ module Mrss
7
+
8
+ def self.patch_mongo_for_session_registry
9
+
10
+ Mongo::Client.class_eval do
11
+ alias :get_session_without_tracking :get_session
12
+
13
+ def get_session(options = {})
14
+ get_session_without_tracking(options).tap do |session|
15
+ SessionRegistry.instance.register(session) if session&.materialized?
16
+ end
17
+ end
18
+ end
19
+
20
+ Mongo::Session.class_eval do
21
+ alias :end_session_without_tracking :end_session
22
+
23
+ def end_session
24
+ SessionRegistry.instance.unregister(self)
25
+ end_session_without_tracking
26
+ end
27
+
28
+ alias :materialize_if_needed_without_tracking :materialize_if_needed
29
+
30
+ def materialize_if_needed
31
+ materialize_if_needed_without_tracking.tap do
32
+ SessionRegistry.instance.register(self)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ module Mrss
40
+ class SessionRegistry
41
+ include Singleton
42
+
43
+ def initialize
44
+ @registry = {}
45
+ end
46
+
47
+ def register(session)
48
+ @registry[session.session_id] = session if session
49
+ end
50
+
51
+ def unregister(session)
52
+ return if session.ended? || !session.materialized?
53
+ @registry.delete(session.session_id)
54
+ end
55
+
56
+ def verify_sessions_ended!
57
+ @registry.delete_if { |_, session| session.ended? }
58
+
59
+ unless @registry.empty?
60
+ sessions = @registry.map { |_, session| session }
61
+ raise "Session registry contains live sessions: #{sessions.join(', ')}"
62
+ end
63
+ end
64
+
65
+ def clear_registry
66
+ @registry = {}
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'singleton'
5
+
6
+ module Mrss
7
+
8
+ def self.patch_mongo_for_session_registry
9
+
10
+ Mongo::Client.class_eval do
11
+ alias :get_session_without_tracking :get_session
12
+
13
+ def get_session(options = {})
14
+ get_session_without_tracking(options).tap do |session|
15
+ SessionRegistry.instance.register(session)
16
+ end
17
+ end
18
+ end
19
+
20
+ Mongo::Session.class_eval do
21
+ alias :end_session_without_tracking :end_session
22
+
23
+ def end_session
24
+ SessionRegistry.instance.unregister(self)
25
+ end_session_without_tracking
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ module Mrss
32
+ class SessionRegistry
33
+ include Singleton
34
+
35
+ def initialize
36
+ @registry = {}
37
+ end
38
+
39
+ def register(session)
40
+ @registry[session.session_id] = session if session
41
+ end
42
+
43
+ def unregister(session)
44
+ @registry.delete(session.session_id) unless session.ended?
45
+ end
46
+
47
+ def verify_sessions_ended!
48
+ @registry.delete_if { |_, session| session.ended? }
49
+
50
+ unless @registry.empty?
51
+ sessions = @registry.map { |_, session| session }
52
+ raise "Session registry contains live sessions: #{sessions.join(', ')}"
53
+ end
54
+ end
55
+
56
+ def clear_registry
57
+ @registry = {}
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,179 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ autoload :JSON, 'json'
5
+ autoload :FileUtils, 'fileutils'
6
+ autoload :Find, 'find'
7
+
8
+ module Mrss
9
+
10
+ autoload :ChildProcessHelper, 'mrss/child_process_helper'
11
+
12
+ # Organizes and runs all of the tests in the test suite in batches.
13
+ #
14
+ # Organizing the tests in batches serves two purposes:
15
+ #
16
+ # 1. This allows running unit tests before integration tests, therefore
17
+ # in theory revealing failures quicker on average.
18
+ # 2. This allows running some tests that have high intermittent failure rate
19
+ # in their own test process.
20
+ #
21
+ # This class aggregates RSpec results after the test runs.
22
+ class SpecOrganizer
23
+
24
+ class BucketsNotPrioritized < StandardError
25
+ end
26
+
27
+ def initialize(root: nil, classifiers:, priority_order:,
28
+ spec_root: nil, rspec_json_path: nil, rspec_all_json_path: nil,
29
+ randomize: false
30
+ )
31
+ @spec_root = spec_root || File.join(root, 'spec')
32
+ @classifiers = classifiers
33
+ @priority_order = priority_order
34
+ @rspec_json_path = rspec_json_path || File.join(root, 'tmp/rspec.json')
35
+ @rspec_all_json_path = rspec_all_json_path || File.join(root, 'tmp/rspec-all.json')
36
+ @randomize = !!randomize
37
+ end
38
+
39
+ attr_reader :spec_root, :classifiers, :priority_order
40
+ attr_reader :rspec_json_path, :rspec_all_json_path
41
+
42
+ def randomize?
43
+ @randomize
44
+ end
45
+
46
+ def seed
47
+ @seed ||= (rand * 100_000).to_i
48
+ end
49
+
50
+ def buckets
51
+ @buckets ||= {}.tap do |buckets|
52
+ Find.find(spec_root) do |path|
53
+ next unless File.file?(path)
54
+ next unless path =~ /_spec\.rb\z/
55
+ rel_path = path[(spec_root.length + 1)..path.length]
56
+
57
+ found = false
58
+ classifiers.each do |(regexp, category)|
59
+ if regexp =~ rel_path
60
+ buckets[category] ||= []
61
+ buckets[category] << File.join('spec', rel_path)
62
+ found = true
63
+ break
64
+ end
65
+ end
66
+
67
+ unless found
68
+ buckets[nil] ||= []
69
+ buckets[nil] << File.join('spec', rel_path)
70
+ end
71
+ end
72
+ end.freeze
73
+ end
74
+
75
+ def ordered_buckets
76
+ @ordered_buckets ||= {}.tap do |ordered_buckets|
77
+ buckets = self.buckets.dup
78
+ priority_order.each do |category|
79
+ files = buckets.delete(category)
80
+ ordered_buckets[category] = files
81
+ end
82
+
83
+ if files = buckets.delete(nil)
84
+ ordered_buckets[nil] = files
85
+ end
86
+
87
+ unless buckets.empty?
88
+ raise BucketsNotPrioritized, "Some buckets were not prioritized: #{buckets.keys.map(&:to_s).join(', ')}"
89
+ end
90
+ end.freeze
91
+ end
92
+
93
+ def run
94
+ run_buckets(*buckets.keys)
95
+ end
96
+
97
+ def run_buckets(*buckets)
98
+ FileUtils.rm_f(rspec_all_json_path)
99
+
100
+ buckets.each do |bucket|
101
+ if bucket && !self.buckets[bucket]
102
+ raise "Unknown bucket #{bucket}"
103
+ end
104
+ end
105
+ buckets = Hash[self.buckets.select { |k, v| buckets.include?(k) }]
106
+
107
+ failed = []
108
+
109
+ priority_order.each do |category|
110
+ if files = buckets.delete(category)
111
+ unless run_files(category, files)
112
+ failed << category
113
+ end
114
+ end
115
+ end
116
+ if files = buckets.delete(nil)
117
+ unless run_files('remaining', files)
118
+ failed << 'remaining'
119
+ end
120
+ end
121
+
122
+ unless buckets.empty?
123
+ raise "Some buckets were not executed: #{buckets.keys.map(&:to_s).join(', ')}"
124
+ end
125
+
126
+ if failed.any?
127
+ raise "The following buckets failed: #{failed.map(&:to_s).join(', ')}"
128
+ end
129
+ end
130
+
131
+ def run_files(category, paths)
132
+ puts "Running #{category.to_s.gsub('_', ' ')} tests"
133
+ FileUtils.rm_f(rspec_json_path)
134
+ cmd = %w(rspec) + paths
135
+ if randomize?
136
+ cmd += %W(--order rand:#{seed})
137
+ end
138
+
139
+ begin
140
+ puts "Running #{cmd.join(' ')}"
141
+ ChildProcessHelper.check_call(cmd)
142
+ ensure
143
+ if File.exist?(rspec_json_path)
144
+ if File.exist?(rspec_all_json_path)
145
+ merge_rspec_results
146
+ else
147
+ FileUtils.cp(rspec_json_path, rspec_all_json_path)
148
+ end
149
+ end
150
+ end
151
+
152
+ true
153
+ rescue ChildProcessHelper::SpawnError
154
+ false
155
+ end
156
+
157
+ def merge_rspec_results
158
+ all = JSON.parse(File.read(rspec_all_json_path))
159
+ new = JSON.parse(File.read(rspec_json_path))
160
+ all['examples'] += new.delete('examples')
161
+ new.delete('summary').each do |k, v|
162
+ all['summary'][k] += v
163
+ end
164
+ new.delete('version')
165
+ new.delete('summary_line')
166
+ # The spec organizer runs all buckets with the same seed, hence
167
+ # we can drop the seed from new results.
168
+ new.delete('seed')
169
+ unless new.empty?
170
+ raise "Unhandled rspec results keys: #{new.keys.join(', ')}"
171
+ end
172
+ # We do not merge summary lines, delete them from aggregated results
173
+ all.delete('summary_line')
174
+ File.open(rspec_all_json_path, 'w') do |f|
175
+ f << JSON.dump(all)
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ module Mrss
5
+ module Utils
6
+ extend self
7
+
8
+ def print_backtrace(dest=STDERR)
9
+ raise
10
+ rescue => e
11
+ dest.puts e.backtrace.join("\n")
12
+ end
13
+
14
+ # Parses the given version string, accounting for suffix information that
15
+ # Gem::Version cannot successfully parse.
16
+ #
17
+ # @param [ String ] version the version to parse
18
+ #
19
+ # @return [ Gem::Version ] the parsed version
20
+ #
21
+ # @raise [ ArgumentError ] if the string cannot be parsed.
22
+ def parse_version(version)
23
+ Gem::Version.new(version)
24
+ rescue ArgumentError
25
+ match = version.match(/\A(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?(-[A-Za-z\+\d]+)?\z/)
26
+ raise ArgumentError.new("Malformed version number string #{version}") if match.nil?
27
+
28
+ Gem::Version.new(
29
+ [
30
+ match[:major],
31
+ match[:minor],
32
+ match[:patch]
33
+ ].join('.')
34
+ )
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../mrss/release/candidate'
4
+
5
+ namespace :candidate do
6
+ desc 'Initialize a new product.yml file'
7
+ task :init do
8
+ Mrss::Release::ProductData.init!
9
+ puts "product.yml file created"
10
+ end
11
+
12
+ desc 'Print the release notes for the next candidate release'
13
+ task :preview do
14
+ Mrss::Release::Candidate.instance do |candidate|
15
+ # load the pending changes before bumping the version, since it
16
+ # depends on the value of the current version.
17
+ candidate.pending_changes
18
+ candidate.bump_version
19
+ puts candidate.release_notes
20
+ end
21
+ end
22
+
23
+ desc 'List the pull requests to be included in the next release'
24
+ task :prs do
25
+ Mrss::Release::Candidate.instance.decorated_prs.each do |pr|
26
+ print "\##{pr['number']}[#{pr['type-code']}] "
27
+ print "#{pr['jira']} " if pr['jira']
28
+ puts pr['short-title']
29
+ end
30
+ end
31
+
32
+ desc 'Create a new branch and pull request for the candidate'
33
+ task create: :check_branch_status do
34
+ Mrss::Release::Candidate.instance do |candidate|
35
+ origin = `git config get remote.origin.url`
36
+ match = origin.match(/:(.*?)\//) or raise "origin url is not in expected format: #{origin.inspect}"
37
+ user = match[1]
38
+
39
+ puts 'gathering candidate info and bumping version...'
40
+ candidate.bump_version!
41
+
42
+ puts 'writing release notes to /tmp/pr-body.md...'
43
+ File.write('/tmp/pr-body.md', candidate.release_notes)
44
+
45
+ sh 'git', 'checkout', '-b', candidate.branch_name
46
+ sh 'git', 'commit', '-am', "Bump version to #{candidate.product.version}"
47
+ sh 'git', 'push', 'origin', candidate.branch_name
48
+
49
+ sh 'gh', 'pr', 'create',
50
+ '--head', "#{user}:#{candidate.branch_name}",
51
+ '--base', candidate.product.base_branch,
52
+ '--title', "Release candidate for #{candidate.product.version}",
53
+ '--label', 'release-candidate',
54
+ '--body-file', '/tmp/pr-body.md'
55
+ end
56
+ end
57
+
58
+ # Ensures the current branch is up-to-date with no uncommitted changes
59
+ task :check_branch_status do
60
+ sh 'git pull >/dev/null', verbose: false
61
+ changes = `git status --short --untracked-files=no`.strip
62
+ abort "There are uncommitted changes. Commit (or revert) the changes and try again." if changes.length > 0
63
+ end
64
+ end