Quit with an error status on SIGTERM during migration
This prevents the supervisor from thinking that the migration completed successfully. In order to do this, I've introduced a new lock around the start (and finish) of the migration so that we avoid a race between the signal handler in the server_accept loop and the control thread mirror startup. Without that, we'd risk successfully starting a migration after the SIGTERM handler fired, which would be Bad.
This commit is contained in:
@@ -121,11 +121,12 @@ class Environment
|
||||
|
||||
def run_fake( name, addr, port, sock=nil )
|
||||
fakedir = File.join( File.dirname( __FILE__ ), "fakes" )
|
||||
fake = Dir[File.join( fakedir, name ) + "*"].sort.find { |fn|
|
||||
fakeglob = File.join( fakedir, name ) + "*"
|
||||
fake = Dir[fakeglob].sort.find { |fn|
|
||||
File.executable?( fn )
|
||||
}
|
||||
|
||||
raise "no fake executable" unless fake
|
||||
raise "no fake executable at #{fakeglob}" unless fake
|
||||
raise "no addr" unless addr
|
||||
raise "no port" unless port
|
||||
|
||||
|
19
tests/acceptance/fakes/dest/sigterm_after_hello.rb
Executable file
19
tests/acceptance/fakes/dest/sigterm_after_hello.rb
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# Wait for a sender connection, send a correct hello, then sigterm the
|
||||
# sender. We expect the sender to exit with status of 6, which is
|
||||
# enforced in the test.
|
||||
|
||||
require 'flexnbd/fake_dest'
|
||||
include FlexNBD
|
||||
|
||||
addr, port, pid = *ARGV
|
||||
server = FakeDest.new( addr, port )
|
||||
client = server.accept( "Timed out waiting for a connection" )
|
||||
client.write_hello
|
||||
|
||||
Process.kill(15, pid.to_i)
|
||||
|
||||
client.close
|
||||
server.close
|
||||
exit 0
|
20
tests/acceptance/fakes/source/sigterm_after_hello.rb
Executable file
20
tests/acceptance/fakes/source/sigterm_after_hello.rb
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# Connect to the listener, wait for the hello, then sigterm the
|
||||
# listener. We expect the listener to exit with a status of 6, which
|
||||
# is enforced in the test.
|
||||
|
||||
require 'flexnbd/fake_source'
|
||||
include FlexNBD
|
||||
|
||||
addr, port, pid = *ARGV
|
||||
|
||||
client = FakeSource.new( addr, port, "Timed out connecting." )
|
||||
client.read_hello
|
||||
|
||||
Process.kill( "TERM", pid.to_i )
|
||||
|
||||
sleep(0.2)
|
||||
client.close
|
||||
|
||||
exit(0)
|
@@ -136,11 +136,11 @@ class ValgrindKillingExecutor
|
||||
|
||||
|
||||
def call( err )
|
||||
Process.kill( "KILL", @pid )
|
||||
$stderr.puts "*"*72
|
||||
$stderr.puts "* Valgrind error spotted:"
|
||||
$stderr.puts err.to_s.split("\n").map{|s| " #{s}"}
|
||||
$stderr.puts "*"*72
|
||||
Process.kill( "KILL", @pid )
|
||||
exit(1)
|
||||
end
|
||||
|
||||
@@ -323,7 +323,8 @@ module FlexNBD
|
||||
|
||||
|
||||
def serve( file, *acl)
|
||||
run_serve_cmd( serve_cmd( file, acl ) )
|
||||
cmd = serve_cmd( file, acl )
|
||||
run_serve_cmd( cmd )
|
||||
end
|
||||
|
||||
def listen(file, *acl)
|
||||
|
@@ -28,6 +28,11 @@ class TestDestErrorHandling < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
|
||||
def test_sigterm_has_bad_exit_status
|
||||
@env.nbd1.can_die(1)
|
||||
run_fake( "source/sigterm_after_hello" )
|
||||
end
|
||||
|
||||
def test_disconnect_after_hello_causes_error_not_fatal
|
||||
run_fake( "source/close_after_hello" )
|
||||
assert_no_control
|
||||
|
@@ -19,12 +19,24 @@ class TestSourceErrorHandling < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
|
||||
def expect_term_during_migration
|
||||
@env.nbd1.can_die(6,9)
|
||||
end
|
||||
|
||||
|
||||
def test_failure_to_connect_reported_in_mirror_cmd_response
|
||||
stdout, stderr = @env.mirror12_unchecked
|
||||
expect_term_during_migration
|
||||
assert_match( /failed to connect/, stderr )
|
||||
end
|
||||
|
||||
|
||||
def test_sigterm_after_hello_quits_with_status_of_1
|
||||
expect_term_during_migration
|
||||
run_fake( "dest/sigterm_after_hello" )
|
||||
end
|
||||
|
||||
|
||||
def test_destination_hangs_after_connect_reports_error_at_source
|
||||
run_fake( "dest/hang_after_connect",
|
||||
:err => /Remote server failed to respond/ )
|
||||
@@ -36,6 +48,7 @@ class TestSourceErrorHandling < Test::Unit::TestCase
|
||||
:err => /Mirror was rejected/ )
|
||||
end
|
||||
|
||||
|
||||
def test_wrong_size_causes_disconnect
|
||||
run_fake( "dest/hello_wrong_size",
|
||||
:err => /Remote size does not match local size/ )
|
||||
@@ -43,38 +56,45 @@ class TestSourceErrorHandling < Test::Unit::TestCase
|
||||
|
||||
|
||||
def test_wrong_magic_causes_disconnect
|
||||
expect_term_during_migration
|
||||
run_fake( "dest/hello_wrong_magic",
|
||||
:err => /Mirror was rejected/ )
|
||||
end
|
||||
|
||||
|
||||
def test_disconnect_after_hello_causes_retry
|
||||
expect_term_during_migration
|
||||
run_fake( "dest/close_after_hello",
|
||||
:out => /Mirror started/ )
|
||||
end
|
||||
|
||||
|
||||
def test_write_times_out_causes_retry
|
||||
expect_term_during_migration
|
||||
run_fake( "dest/hang_after_write" )
|
||||
end
|
||||
|
||||
|
||||
def test_rejected_write_causes_retry
|
||||
expect_term_during_migration
|
||||
run_fake( "dest/error_on_write" )
|
||||
end
|
||||
|
||||
|
||||
def test_disconnect_before_write_reply_causes_retry
|
||||
expect_term_during_migration
|
||||
run_fake( "dest/close_after_write" )
|
||||
end
|
||||
|
||||
|
||||
def test_bad_write_reply_causes_retry
|
||||
expect_term_during_migration
|
||||
run_fake( "dest/write_wrong_magic" )
|
||||
end
|
||||
|
||||
|
||||
def test_pre_entrust_disconnect_causes_retry
|
||||
expect_term_during_migration
|
||||
run_fake( "dest/close_after_writes" )
|
||||
end
|
||||
|
||||
|
Reference in New Issue
Block a user