From e0b99dc0d82d5edd056d9e49760afbf681c63b57 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Wed, 23 Oct 2024 15:18:26 -0600 Subject: [PATCH 1/4] RUBY-1934 clean up DNS server termination The ticket originally called for pulling in another dependency to manage the DNS server, but I don't think it's necessary. The async-container code would be doing essentially what we already had, just with a cleaner technique for sending the termination signal. --- spec/integration/reconnect_spec.rb | 2 - spec/integration/srv_monitoring_spec.rb | 4 -- spec/integration/srv_spec.rb | 4 -- spec/support/common_shortcuts.rb | 65 ++++++++++--------------- spec/support/dns.rb | 16 ------ 5 files changed, 27 insertions(+), 64 deletions(-) delete mode 100644 spec/support/dns.rb diff --git a/spec/integration/reconnect_spec.rb b/spec/integration/reconnect_spec.rb index f478e233dd..ad4f0e5647 100644 --- a/spec/integration/reconnect_spec.rb +++ b/spec/integration/reconnect_spec.rb @@ -181,8 +181,6 @@ end around do |example| - require 'support/dns' - rules = [ ['_mongodb._tcp.test-fake.test.build.10gen.cc', :srv, [0, 0, 2799, 'localhost.test.build.10gen.cc'], diff --git a/spec/integration/srv_monitoring_spec.rb b/spec/integration/srv_monitoring_spec.rb index 6d7da1f730..857ddd1584 100644 --- a/spec/integration/srv_monitoring_spec.rb +++ b/spec/integration/srv_monitoring_spec.rb @@ -76,10 +76,6 @@ # NotImplementedError: recvmsg_nonblock is not implemented fails_on_jruby - before(:all) do - require 'support/dns' - end - around do |example| # Speed up the tests by listening on the fake ports we are using. done = false diff --git a/spec/integration/srv_spec.rb b/spec/integration/srv_spec.rb index da3d529339..594ff9e69b 100644 --- a/spec/integration/srv_spec.rb +++ b/spec/integration/srv_spec.rb @@ -12,10 +12,6 @@ # NotImplementedError: recvmsg_nonblock is not implemented fails_on_jruby - before(:all) do - require 'support/dns' - end - let(:uri) do "mongodb+srv://test-fake.test.build.10gen.cc/?tls=#{SpecConfig.instance.ssl?}&tlsInsecure=true" end diff --git a/spec/support/common_shortcuts.rb b/spec/support/common_shortcuts.rb index 8eacdf2c7c..9357fe1256 100644 --- a/spec/support/common_shortcuts.rb +++ b/spec/support/common_shortcuts.rb @@ -337,52 +337,41 @@ def stop_monitoring(*clients) [:tcp, "0.0.0.0", 5300], ] - def mock_dns(config) - semaphore = Mongo::Semaphore.new - - thread = Thread.new do - RubyDNS::run_server(DNS_INTERFACES) do - config.each do |(query, type, *answers)| - - resource_cls = Resolv::DNS::Resource::IN.const_get(type.to_s.upcase) - resources = answers.map do |answer| - resource_cls.new(*answer) - end - match(query, resource_cls) do |req| - req.add(resources) - end + # A signal class for a DNS server to stop + class TerminateDNSServer < RuntimeError; end + + def run_dns_server(config, semaphore) + server = RubyDNS::run_server(DNS_INTERFACES) do + config.each do |(query, type, *answers)| + resource_cls = Resolv::DNS::Resource::IN.const_get(type.to_s.upcase) + resources = answers.map do |answer| + resource_cls.new(*answer) end - semaphore.signal + match(query, resource_cls) do |req| + req.add(resources) + end end + + semaphore.signal end + rescue TerminateDNSServer + server&.stop + end - semaphore.wait + def mock_dns(config) + semaphore = Mongo::Semaphore.new + thread = Thread.new { run_dns_server(config, semaphore) } - begin - yield - ensure - 10.times do - if $last_async_task - break - end - sleep 0.5 - end + # wait for the server to spin up + semaphore.wait - # Hack to stop the server - https://github.com/socketry/rubydns/issues/75 - if $last_async_task.nil? - STDERR.puts "No async task - server never started?" - else - begin - $last_async_task.stop - rescue NoMethodError => e - STDERR.puts "Error stopping async task: #{e}" - end - end + yield + ensure + return unless thread - thread.kill - thread.join - end + thread.raise(TerminateDNSServer) + thread.join end # Wait for snapshot reads to become available to prevent this error: diff --git a/spec/support/dns.rb b/spec/support/dns.rb deleted file mode 100644 index d868f7bc3b..0000000000 --- a/spec/support/dns.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true -# rubocop:todo all - -require 'rubydns' - -# Hack to stop the server - https://github.com/socketry/rubydns/issues/75 -module Async - class Task - alias :run_without_record :run - def run(*args) - run_without_record.tap do - $last_async_task = self - end - end - end -end From 3b6ea3b909b1cdd931a26a0afcf5c16470b4fff0 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Wed, 23 Oct 2024 15:56:27 -0600 Subject: [PATCH 2/4] need to require rubydns --- spec/support/common_shortcuts.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/support/common_shortcuts.rb b/spec/support/common_shortcuts.rb index 9357fe1256..bd3556a90b 100644 --- a/spec/support/common_shortcuts.rb +++ b/spec/support/common_shortcuts.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true # rubocop:todo all +require 'rubydns' + module CommonShortcuts module ClassMethods # Declares a topology double, which is configured to accept summary From e608ebe36b46d4a0f115b7b087386c014ed465b6 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Wed, 23 Oct 2024 16:33:47 -0600 Subject: [PATCH 3/4] even cleaner --- spec/support/common_shortcuts.rb | 37 ++++++++++++-------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/spec/support/common_shortcuts.rb b/spec/support/common_shortcuts.rb index bd3556a90b..bdb4c8511f 100644 --- a/spec/support/common_shortcuts.rb +++ b/spec/support/common_shortcuts.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true # rubocop:todo all -require 'rubydns' - module CommonShortcuts module ClassMethods # Declares a topology double, which is configured to accept summary @@ -339,11 +337,10 @@ def stop_monitoring(*clients) [:tcp, "0.0.0.0", 5300], ] - # A signal class for a DNS server to stop - class TerminateDNSServer < RuntimeError; end - - def run_dns_server(config, semaphore) - server = RubyDNS::run_server(DNS_INTERFACES) do + # Starts the DNS server and returns it; should be run from within an + # Async block. Prefer #mock_dns instead, which does the setup for you. + def start_dns_server(config) + RubyDNS::run_server(DNS_INTERFACES) do config.each do |(query, type, *answers)| resource_cls = Resolv::DNS::Resource::IN.const_get(type.to_s.upcase) resources = answers.map do |answer| @@ -354,26 +351,20 @@ def run_dns_server(config, semaphore) req.add(resources) end end - - semaphore.signal end - rescue TerminateDNSServer - server&.stop end + # Starts and runs a DNS server, then yields to the attached block. def mock_dns(config) - semaphore = Mongo::Semaphore.new - thread = Thread.new { run_dns_server(config, semaphore) } - - # wait for the server to spin up - semaphore.wait - - yield - ensure - return unless thread - - thread.raise(TerminateDNSServer) - thread.join + # only require rubydns when we need it; it's MRI-only. + require 'rubydns' + + Async do |task| + server = start_dns_server(config) + yield + ensure + server.stop + end end # Wait for snapshot reads to become available to prevent this error: From 3179733e19ce866c4d0c412d2175cd7e077bafdb Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Thu, 24 Oct 2024 15:14:27 -0600 Subject: [PATCH 4/4] RubyDNS is failing oddly with Ruby 2.7, so let's just skip it --- spec/integration/reconnect_spec.rb | 2 ++ spec/integration/srv_monitoring_spec.rb | 2 ++ spec/support/constraints.rb | 10 ++++++++++ 3 files changed, 14 insertions(+) diff --git a/spec/integration/reconnect_spec.rb b/spec/integration/reconnect_spec.rb index ad4f0e5647..0fa47c29af 100644 --- a/spec/integration/reconnect_spec.rb +++ b/spec/integration/reconnect_spec.rb @@ -111,6 +111,8 @@ # thread.kill should've similarly failed, but it doesn't. fails_on_jruby + minimum_mri_version '3.0.0' + it 'recreates SRV monitor' do wait_for_discovery diff --git a/spec/integration/srv_monitoring_spec.rb b/spec/integration/srv_monitoring_spec.rb index 857ddd1584..ffa58b053f 100644 --- a/spec/integration/srv_monitoring_spec.rb +++ b/spec/integration/srv_monitoring_spec.rb @@ -76,6 +76,8 @@ # NotImplementedError: recvmsg_nonblock is not implemented fails_on_jruby + minimum_mri_version '3.0.0' + around do |example| # Speed up the tests by listening on the fake ports we are using. done = false diff --git a/spec/support/constraints.rb b/spec/support/constraints.rb index 6d92409937..8c7f3f940d 100644 --- a/spec/support/constraints.rb +++ b/spec/support/constraints.rb @@ -17,6 +17,16 @@ def require_local_tls end end + def minimum_mri_version(version) + require_mri + + before(:all) do + if RUBY_VERSION < version + skip "Ruby #{version} or greater is required" + end + end + end + def forbid_x509_auth before(:all) do skip 'X.509 auth not allowed' if SpecConfig.instance.x509_auth?