Files
flexnbd-c/tests/acceptance/test_source_error_handling.rb
Alex Young fd935ce4c9 Simplify the migration handover protocol
The three-way hand-off has a problem: there's no way to arrange for the
state of the migration to be unambiguous in case of failure.  If the
final "disconnect" message is lost (as in, the destination never
receives it whether it is sent by the sender or not), the destination
has no option but to quit with an error status and let a human sort it
out.  However, at that point we can either arrange to have a .INCOMPLETE
file still on disc or not - and it doesn't matter which we choose, we
can still end up with dataloss by picking a specific calamity to have
befallen the sender.

Given this, it makes sense to fall back to a simpler protocol: just send
all the data, then send a "disconnect" message.  This has the same
downside that we need a human to sort out specific failure cases, but
combined with --unlink before sending "disconnect" (see next patch) it
will always be possible for a human to disambiguate, whether the
destination quit with an error status or not.
2012-07-23 10:22:25 +01:00

103 lines
2.1 KiB
Ruby

# encoding: utf-8
require 'test/unit'
require 'environment'
class TestSourceErrorHandling < Test::Unit::TestCase
def setup
@env = Environment.new
@env.writefile1( "f" * 4 )
@env.serve1
end
def teardown
@env.nbd1.can_die(0)
@env.cleanup
end
def test_failure_to_connect_reported_in_mirror_cmd_response
stdout, stderr = @env.mirror12_unchecked
assert_match( /failed to connect/, stderr )
end
def test_destination_hangs_after_connect_reports_error_at_source
run_fake( "dest/hang_after_connect",
:err => /Remote server failed to respond/ )
end
def test_destination_rejects_connection_reports_error_at_source
run_fake( "dest/reject_acl",
: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/ )
end
def test_wrong_magic_causes_disconnect
run_fake( "dest/hello_wrong_magic",
:err => /Mirror was rejected/ )
end
def test_disconnect_after_hello_causes_retry
run_fake( "dest/close_after_hello",
:out => /Mirror started/ )
end
def test_write_times_out_causes_retry
run_fake( "dest/hang_after_write" )
end
def test_rejected_write_causes_retry
run_fake( "dest/error_on_write" )
end
def test_disconnect_before_write_reply_causes_retry
run_fake( "dest/close_after_write" )
end
def test_bad_write_reply_causes_retry
run_fake( "dest/write_wrong_magic" )
end
def test_pre_entrust_disconnect_causes_retry
run_fake( "dest/close_after_writes" )
end
def test_cancel_migration
run_fake( "dest/break_after_hello" )
end
private
def run_fake(name, opts = {})
@env.run_fake( name, @env.ip, @env.port2, @env.nbd1.ctrl )
stdout, stderr = @env.mirror12_unchecked
assert_success
assert_match( opts[:err], stderr ) if opts[:err]
assert_match( opts[:out], stdout ) if opts[:out]
return stdout, stderr
end
def assert_success( msg=nil )
assert @env.fake_reports_success, msg || "Fake failed"
end
end # class TestSourceErrorHandling