fix(admin-config-tool): unwrap Request reply envelope before decoding as RequestReply
config push crashed on every real controller with a capnp KjException --
xce_reply() wraps every reply in an outer Request{body.replymsg} envelope,
but discovery.py/push.py decoded raw wire bytes directly as a bare
RequestReply. Adds a shared decode_reply() unwrap point used by both call
sites, and fixes the same wrong-shape assumption baked into task 5.6's
and test_cli.py's mock-server fixtures.
This commit is contained in:
parent
46bbdaa9a6
commit
915e6fdf64
7 changed files with 231 additions and 26 deletions
|
|
@ -99,7 +99,7 @@ def cluster_bin(tmp_path):
|
|||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_push_config_successful_commit(mock_controller, cluster_bin):
|
||||
def test_push_config_successful_commit(mock_controller, cluster_bin, wrap_reply):
|
||||
mod = load_schema()
|
||||
received = {}
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ def test_push_config_successful_commit(mock_controller, cluster_bin):
|
|||
upgrade_reply.versionTag = 7
|
||||
upgrade_reply.ackedCount = 3
|
||||
upgrade_reply.error = ""
|
||||
wire.send_frame(conn, reply.to_bytes())
|
||||
wire.send_frame(conn, wrap_reply(reply))
|
||||
|
||||
srv = mock_controller(_handler)
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ def test_push_config_successful_commit(mock_controller, cluster_bin):
|
|||
assert received["sha256"] == hashlib.sha256(Path(cluster_bin).read_bytes()).digest()
|
||||
|
||||
|
||||
def test_push_config_follows_one_redirect_to_primary(mock_controller, cluster_bin):
|
||||
def test_push_config_follows_one_redirect_to_primary(mock_controller, cluster_bin, wrap_reply):
|
||||
mod = load_schema()
|
||||
|
||||
def _standby_handler(conn):
|
||||
|
|
@ -144,7 +144,7 @@ def test_push_config_follows_one_redirect_to_primary(mock_controller, cluster_bi
|
|||
# port filled in once the primary server exists, see below
|
||||
reply.redirectPort = _standby_handler.primary_port
|
||||
reply.data.nodata = None
|
||||
wire.send_frame(conn, reply.to_bytes())
|
||||
wire.send_frame(conn, wrap_reply(reply))
|
||||
|
||||
def _primary_handler(conn):
|
||||
wire.recv_frame(conn)
|
||||
|
|
@ -154,7 +154,7 @@ def test_push_config_follows_one_redirect_to_primary(mock_controller, cluster_bi
|
|||
upgrade_reply.versionTag = 9
|
||||
upgrade_reply.ackedCount = 2
|
||||
upgrade_reply.error = ""
|
||||
wire.send_frame(conn, reply.to_bytes())
|
||||
wire.send_frame(conn, wrap_reply(reply))
|
||||
|
||||
primary_srv = mock_controller(_primary_handler)
|
||||
_standby_handler.primary_port = primary_srv.port
|
||||
|
|
@ -166,7 +166,7 @@ def test_push_config_follows_one_redirect_to_primary(mock_controller, cluster_bi
|
|||
assert result.acked_count == 2
|
||||
|
||||
|
||||
def test_push_config_non_zero_status_reports_error_not_exception(mock_controller, cluster_bin):
|
||||
def test_push_config_non_zero_status_reports_error_not_exception(mock_controller, cluster_bin, wrap_reply):
|
||||
mod = load_schema()
|
||||
|
||||
def _handler(conn):
|
||||
|
|
@ -174,7 +174,7 @@ def test_push_config_non_zero_status_reports_error_not_exception(mock_controller
|
|||
reply = mod.RequestReply.new_message()
|
||||
reply.status = 1
|
||||
reply.data.debugmsg = "simulated failure"
|
||||
wire.send_frame(conn, reply.to_bytes())
|
||||
wire.send_frame(conn, wrap_reply(reply))
|
||||
|
||||
srv = mock_controller(_handler)
|
||||
|
||||
|
|
@ -184,7 +184,7 @@ def test_push_config_non_zero_status_reports_error_not_exception(mock_controller
|
|||
assert "status 1" in result.error
|
||||
|
||||
|
||||
def test_push_config_wrong_reply_variant_raises_reply_decode_error(mock_controller, cluster_bin):
|
||||
def test_push_config_wrong_reply_variant_raises_reply_decode_error(mock_controller, cluster_bin, wrap_reply):
|
||||
mod = load_schema()
|
||||
|
||||
def _handler(conn):
|
||||
|
|
@ -192,7 +192,34 @@ def test_push_config_wrong_reply_variant_raises_reply_decode_error(mock_controll
|
|||
reply = mod.RequestReply.new_message()
|
||||
reply.status = 0
|
||||
reply.data.nodata = None # not upgradeconfigreplymsg
|
||||
wire.send_frame(conn, reply.to_bytes())
|
||||
wire.send_frame(conn, wrap_reply(reply))
|
||||
|
||||
srv = mock_controller(_handler)
|
||||
|
||||
with pytest.raises(ReplyDecodeError):
|
||||
push.push_config(cluster_bin, f"127.0.0.1:{srv.port}")
|
||||
|
||||
|
||||
def test_push_config_rejects_bare_requestreply_bytes_old_wire_shape_regression(
|
||||
mock_controller, cluster_bin
|
||||
):
|
||||
"""Regression test (task 6.1): a mock server that still sends the OLD,
|
||||
wrong shape -- a bare RequestReply with no outer Request envelope --
|
||||
must be cleanly rejected as ReplyDecodeError, not silently accepted
|
||||
and not an unhandled capnp.KjException. This locks in that the fixed
|
||||
client only accepts the real controller wire shape.
|
||||
"""
|
||||
mod = load_schema()
|
||||
|
||||
def _handler(conn):
|
||||
wire.recv_frame(conn)
|
||||
reply = mod.RequestReply.new_message()
|
||||
reply.status = 0
|
||||
upgrade_reply = reply.data.init("upgradeconfigreplymsg")
|
||||
upgrade_reply.versionTag = 1
|
||||
upgrade_reply.ackedCount = 1
|
||||
upgrade_reply.error = ""
|
||||
wire.send_frame(conn, reply.to_bytes()) # NOT wrapped -- old shape
|
||||
|
||||
srv = mock_controller(_handler)
|
||||
|
||||
|
|
@ -301,7 +328,7 @@ def test_probe_config_has_no_wire_level_rejection_signal_by_design(
|
|||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_push_config_propagates_too_many_redirects_end_to_end(mock_controller, cluster_bin):
|
||||
def test_push_config_propagates_too_many_redirects_end_to_end(mock_controller, cluster_bin, wrap_reply):
|
||||
"""A real chain of 4 redirecting mock servers -- push_config's own
|
||||
composition with xceadmin.discovery must surface TooManyRedirectsError,
|
||||
not silently follow forever or return a bogus result.
|
||||
|
|
@ -317,7 +344,7 @@ def test_push_config_propagates_too_many_redirects_end_to_end(mock_controller, c
|
|||
reply.redirectHost = "127.0.0.1"
|
||||
reply.redirectPort = next_port_getter()
|
||||
reply.data.nodata = None
|
||||
wire.send_frame(conn, reply.to_bytes())
|
||||
wire.send_frame(conn, wrap_reply(reply))
|
||||
|
||||
return _handler
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue