summaryrefslogtreecommitdiffstats
path: root/cucumber/features/support/helpers/misc_helpers.rb
diff options
context:
space:
mode:
Diffstat (limited to 'cucumber/features/support/helpers/misc_helpers.rb')
-rw-r--r--cucumber/features/support/helpers/misc_helpers.rb139
1 files changed, 115 insertions, 24 deletions
diff --git a/cucumber/features/support/helpers/misc_helpers.rb b/cucumber/features/support/helpers/misc_helpers.rb
index 7e09411f..865d2978 100644
--- a/cucumber/features/support/helpers/misc_helpers.rb
+++ b/cucumber/features/support/helpers/misc_helpers.rb
@@ -1,4 +1,6 @@
require 'date'
+require 'io/console'
+require 'pry'
require 'timeout'
require 'test/unit'
@@ -28,8 +30,12 @@ end
# Call block (ignoring any exceptions it may throw) repeatedly with
# one second breaks until it returns true, or until `timeout` seconds have
-# passed when we throw a Timeout::Error exception.
+# passed when we throw a Timeout::Error exception. If `timeout` is `nil`,
+# then we just run the code block with no timeout.
def try_for(timeout, options = {})
+ if block_given? && timeout.nil?
+ return yield
+ end
options[:delay] ||= 1
last_exception = nil
# Create a unique exception used only for this particular try_for
@@ -76,11 +82,12 @@ def try_for(timeout, options = {})
# ends up there immediately.
rescue unique_timeout_exception => e
msg = options[:msg] || 'try_for() timeout expired'
+ exc_class = options[:exception] || Timeout::Error
if last_exception
msg += "\nLast ignored exception was: " +
"#{last_exception.class}: #{last_exception}"
end
- raise Timeout::Error.new(msg)
+ raise exc_class.new(msg)
end
class TorFailure < StandardError
@@ -89,6 +96,19 @@ end
class MaxRetriesFailure < StandardError
end
+def force_new_tor_circuit()
+ debug_log("Forcing new Tor circuit...")
+ # Tor rate limits NEWNYM to at most one per 10 second period.
+ interval = 10
+ if $__last_newnym
+ elapsed = Time.now - $__last_newnym
+ # We sleep an extra second to avoid tight timings.
+ sleep interval - elapsed + 1 if 0 < elapsed && elapsed < interval
+ end
+ $vm.execute_successfully('tor_control_send "signal NEWNYM"', :libs => 'tor')
+ $__last_newnym = Time.now
+end
+
# This will retry the block up to MAX_NEW_TOR_CIRCUIT_RETRIES
# times. The block must raise an exception for a run to be considered
# as a failure. After a failure recovery_proc will be called (if
@@ -105,11 +125,6 @@ def retry_tor(recovery_proc = nil, &block)
:operation_name => 'Tor operation', &block)
end
-def retry_i2p(recovery_proc = nil, &block)
- retry_action(15, :recovery_proc => recovery_proc,
- :operation_name => 'I2P operation', &block)
-end
-
def retry_action(max_retries, options = {}, &block)
assert(max_retries.is_a?(Integer), "max_retries must be an integer")
options[:recovery_proc] ||= nil
@@ -120,6 +135,10 @@ def retry_action(max_retries, options = {}, &block)
begin
block.call
return
+ rescue NameError => e
+ # NameError most likely means typos, and hiding that is rarely
+ # (never?) a good idea, so we rethrow them.
+ raise e
rescue Exception => e
if retries <= max_retries
debug_log("#{options[:operation_name]} failed (Try #{retries} of " +
@@ -136,16 +155,15 @@ def retry_action(max_retries, options = {}, &block)
end
end
+alias :retry_times :retry_action
+
+class TorBootstrapFailure < StandardError
+end
+
def wait_until_tor_is_working
try_for(270) { $vm.execute('/usr/local/sbin/tor-has-bootstrapped').success? }
-rescue Timeout::Error => e
- c = $vm.execute("journalctl SYSLOG_IDENTIFIER=restart-tor")
- if c.success?
- debug_log("From the journal:\n" + c.stdout.sub(/^/, " "))
- else
- debug_log("Nothing was in the journal about 'restart-tor'")
- end
- raise e
+rescue Timeout::Error
+ raise TorBootstrapFailure.new('Tor failed to bootstrap')
end
def convert_bytes_mod(unit)
@@ -177,13 +195,14 @@ def convert_from_bytes(size, unit)
return size.to_f/convert_bytes_mod(unit).to_f
end
-def cmd_helper(cmd)
+def cmd_helper(cmd, env = {})
if cmd.instance_of?(Array)
cmd << {:err => [:child, :out]}
elsif cmd.instance_of?(String)
cmd += " 2>&1"
end
- IO.popen(cmd) do |p|
+ env = ENV.to_h.merge(env)
+ IO.popen(env, cmd) do |p|
out = p.readlines.join("\n")
p.close
ret = $?
@@ -192,11 +211,23 @@ def cmd_helper(cmd)
end
end
-# This command will grab all router IP addresses from the Tor
-# consensus in the VM + the hardcoded TOR_AUTHORITIES.
-def get_all_tor_nodes
- cmd = 'awk "/^r/ { print \$6 }" /var/lib/tor/cached-microdesc-consensus'
- $vm.execute(cmd).stdout.chomp.split("\n") + TOR_AUTHORITIES
+def all_tor_hosts
+ nodes = Array.new
+ chutney_torrcs = Dir.glob(
+ "#{$config['TMPDIR']}/chutney-data/nodes/*/torrc"
+ )
+ chutney_torrcs.each do |torrc|
+ open(torrc) do |f|
+ nodes += f.grep(/^(Or|Dir)Port\b/).map do |line|
+ { address: $vmnet.bridge_ip_addr, port: line.split.last.to_i }
+ end
+ end
+ end
+ return nodes
+end
+
+def allowed_hosts_under_tor_enforcement
+ all_tor_hosts + @lan_hosts
end
def get_free_space(machine, path)
@@ -246,8 +277,68 @@ def info_log_artifact_location(type, path)
info_log("#{type.capitalize}: #{path}")
end
+def notify_user(message)
+ alarm_script = $config['NOTIFY_USER_COMMAND']
+ return if alarm_script.nil? || alarm_script.empty?
+ cmd_helper(alarm_script.gsub('%m', message))
+end
+
def pause(message = "Paused")
+ notify_user(message)
+ STDERR.puts
+ STDERR.puts message
+ # Ring the ASCII bell for a helpful notification in most terminal
+ # emulators.
+ STDOUT.write "\a"
STDERR.puts
- STDERR.puts "#{message} (Press ENTER to continue!)"
- STDIN.gets
+ loop do
+ STDERR.puts "Return: Continue; d: Debugging REPL"
+ c = STDIN.getch
+ case c
+ when "\r"
+ return
+ when "d"
+ binding.pry(quiet: true)
+ end
+ end
+end
+
+def dbus_send(service, object_path, method, *args, **opts)
+ opts ||= {}
+ ruby_type_to_dbus_type = {
+ String => 'string',
+ Fixnum => 'int32',
+ }
+ typed_args = args.map do |arg|
+ type = ruby_type_to_dbus_type[arg.class]
+ assert_not_nil(type, "No DBus type conversion for Ruby type '#{arg.class}'")
+ "#{type}:#{arg}"
+ end
+ ret = $vm.execute_successfully(
+ "dbus-send --print-reply --dest=#{service} #{object_path} " +
+ " #{method} #{typed_args.join(' ')}",
+ **opts
+ ).stdout.lines
+ # The first line written is about timings and other stuff we don't
+ # care about; we only care about the return values.
+ ret.shift
+ ret.map! do |s|
+ type, val = /^\s*(\S+)\s+(\S+)$/.match(s)[1,2]
+ case type
+ when 'string'
+ # Unquote
+ val[1, val.length - 2]
+ when 'int32'
+ val.to_i
+ else
+ raise "No Ruby type conversion for DBus type '#{type}'"
+ end
+ end
+ if ret.size == 0
+ return nil
+ elsif ret.size == 1
+ return ret.first
+ else
+ return ret
+ end
end