зеркало из https://github.com/microsoft/CCF.git
Complex lua literals in proposal generator (#1694)
This commit is contained in:
Родитель
a13b33d442
Коммит
aa07efb755
|
@ -26,17 +26,41 @@ def dump_to_file(output_path: str, obj: dict, dump_args: dict):
|
|||
json.dump(obj, f, **dump_args)
|
||||
|
||||
|
||||
def list_as_lua_literal(l):
|
||||
return str(l).translate(str.maketrans("[]", "{}"))
|
||||
def as_lua_literal(arg):
|
||||
if isinstance(arg, str):
|
||||
# This long string swallows any initial newline. This means if we
|
||||
# had an actual newline, it will be lost. To work around this, we
|
||||
# insert a newline to every string. If there was originally a
|
||||
# newline at the start, its now the second character, and is kept.
|
||||
return f"[====[\n{arg}]====]"
|
||||
elif isinstance(arg, bool):
|
||||
return str(arg).lower()
|
||||
elif isinstance(arg, collections.abc.Sequence):
|
||||
return f"{{ {', '.join(as_lua_literal(e) for e in arg)} }}"
|
||||
elif isinstance(arg, collections.abc.Mapping):
|
||||
inner = ", ".join(
|
||||
f"[ {as_lua_literal(k)} ] = {as_lua_literal(v)}" for k, v in arg.items()
|
||||
)
|
||||
return f"{{ {inner} }}"
|
||||
else:
|
||||
return str(arg)
|
||||
|
||||
|
||||
LUA_FUNCTION_EQUAL_ARRAYS = """function equal_arrays(a, b)
|
||||
LUA_FUNCTION_EQUAL_TABLES = """function equal_tables(a, b)
|
||||
if #a ~= #b then
|
||||
return false
|
||||
else
|
||||
for k, v in ipairs(a) do
|
||||
if b[k] ~= v then
|
||||
for k, v in pairs(a) do
|
||||
if type(v) ~= type(b[k]) then
|
||||
return false
|
||||
elseif type(v) == "table" then
|
||||
if not equal_tables(v, b[k]) then
|
||||
return false
|
||||
end
|
||||
else
|
||||
if v ~= b[k] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
|
@ -84,48 +108,38 @@ def add_arg_construction(
|
|||
arg: Union[str, collections.abc.Sequence, collections.abc.Mapping],
|
||||
arg_name: str = "args",
|
||||
):
|
||||
if isinstance(arg, str):
|
||||
lines.append(f"{arg_name} = [====[{arg}]====]")
|
||||
elif isinstance(arg, collections.abc.Sequence):
|
||||
lines.append(f"{arg_name} = {list_as_lua_literal(arg)}")
|
||||
elif isinstance(arg, collections.abc.Mapping):
|
||||
lines.append(f"{arg_name} = {{}}")
|
||||
for k, v in args.items():
|
||||
add_arg_construction(lines, v, arg_name=f"{arg_name}.{k}")
|
||||
else:
|
||||
lines.append(f"{arg_name} = {arg}")
|
||||
lines.append(f"{arg_name} = {as_lua_literal(arg)}")
|
||||
|
||||
|
||||
def add_arg_checks(
|
||||
lines: list,
|
||||
arg: Union[str, collections.abc.Sequence, collections.abc.Mapping],
|
||||
arg_name: str = "args",
|
||||
added_equal_arrays_fn: bool = False,
|
||||
added_equal_tables_fn: bool = False,
|
||||
):
|
||||
lines.append(f"if {arg_name} == nil then return false end")
|
||||
if isinstance(arg, str):
|
||||
lines.append(f"if not {arg_name} == [====[{arg}]====] then return false end")
|
||||
elif isinstance(arg, collections.abc.Sequence):
|
||||
if not added_equal_arrays_fn:
|
||||
lines.extend(
|
||||
line.strip() for line in LUA_FUNCTION_EQUAL_ARRAYS.splitlines()
|
||||
)
|
||||
added_equal_arrays_fn = True
|
||||
expected_name = arg_name.replace(".", "_")
|
||||
lines.append(f"{expected_name} = {list_as_lua_literal(arg)}")
|
||||
lines.append(
|
||||
f"if not equal_arrays({arg_name}, {expected_name}) then return false end"
|
||||
f"if not {arg_name} == {as_lua_literal(arg)} then return false end"
|
||||
)
|
||||
elif isinstance(arg, collections.abc.Mapping):
|
||||
for k, v in arg.items():
|
||||
add_arg_checks(
|
||||
lines,
|
||||
v,
|
||||
arg_name=f"{arg_name}.{k}",
|
||||
added_equal_arrays_fn=added_equal_arrays_fn,
|
||||
elif isinstance(arg, collections.abc.Sequence) or isinstance(
|
||||
arg, collections.abc.Mapping
|
||||
):
|
||||
if not added_equal_tables_fn:
|
||||
lines.extend(
|
||||
line.strip() for line in LUA_FUNCTION_EQUAL_TABLES.splitlines()
|
||||
)
|
||||
added_equal_tables_fn = True
|
||||
expected_name = "expected"
|
||||
lines.append(f"{expected_name} = {as_lua_literal(arg)}")
|
||||
lines.append(
|
||||
f"if not equal_tables({arg_name}, {expected_name}) then return false end"
|
||||
)
|
||||
else:
|
||||
lines.append(f"if not {arg_name} == {arg} then return false end")
|
||||
lines.append(
|
||||
f"if not {arg_name} == {as_lua_literal(arg)} then return false end"
|
||||
)
|
||||
return added_equal_tables_fn
|
||||
|
||||
|
||||
def build_proposal(
|
||||
|
@ -146,7 +160,7 @@ def build_proposal(
|
|||
proposal_script_lines.append("tables, args = ...")
|
||||
proposal_script_lines.append(f'return Calls:call("{proposed_call}", args)')
|
||||
|
||||
proposal_script_text = "; ".join(proposal_script_lines)
|
||||
proposal_script_text = ";\n".join(proposal_script_lines)
|
||||
proposal = {
|
||||
"script": {"text": proposal_script_text},
|
||||
}
|
||||
|
@ -165,7 +179,7 @@ def build_proposal(
|
|||
vote_lines.append("args = call.args")
|
||||
add_arg_checks(vote_lines, args)
|
||||
vote_lines.append("return true")
|
||||
vote_text = "; ".join(vote_lines)
|
||||
vote_text = ";\n".join(vote_lines)
|
||||
vote = {"ballot": {"text": vote_text}}
|
||||
|
||||
LOG.trace(f"Made {proposed_call} proposal:\n{json.dumps(proposal, indent=2)}")
|
||||
|
|
|
@ -174,25 +174,25 @@ namespace ccf
|
|||
|
||||
static int lua_log_trace(lua_State* l)
|
||||
{
|
||||
LOG_TRACE_FMT(get_var_string_from_args(l));
|
||||
LOG_TRACE_FMT("{}", get_var_string_from_args(l));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lua_log_debug(lua_State* l)
|
||||
{
|
||||
LOG_DEBUG_FMT(get_var_string_from_args(l));
|
||||
LOG_DEBUG_FMT("{}", get_var_string_from_args(l));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lua_log_info(lua_State* l)
|
||||
{
|
||||
LOG_INFO_FMT(get_var_string_from_args(l));
|
||||
LOG_INFO_FMT("{}", get_var_string_from_args(l));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lua_log_fail(lua_State* l)
|
||||
{
|
||||
LOG_FAIL_FMT(get_var_string_from_args(l));
|
||||
LOG_FAIL_FMT("{}", get_var_string_from_args(l));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -316,8 +316,11 @@ class Consortium:
|
|||
return self.vote_using_majority(remote_node, proposal)
|
||||
|
||||
def deploy_js_app(self, remote_node, app_bundle_path):
|
||||
proposal_body, _ = self.make_proposal("deploy_js_app", app_bundle_path)
|
||||
proposal_body, careful_vote = self.make_proposal(
|
||||
"deploy_js_app", app_bundle_path
|
||||
)
|
||||
proposal = self.get_any_active_member().propose(remote_node, proposal_body)
|
||||
proposal.vote_for = careful_vote
|
||||
return self.vote_using_majority(remote_node, proposal)
|
||||
|
||||
def accept_recovery(self, remote_node):
|
||||
|
|
|
@ -55,10 +55,11 @@ def make_module_set_proposal(path, content, network):
|
|||
with tempfile.NamedTemporaryFile("w") as f:
|
||||
f.write(content)
|
||||
f.flush()
|
||||
proposal_body, _ = ccf.proposal_generator.set_module(path, f.name)
|
||||
proposal_body, careful_vote = ccf.proposal_generator.set_module(path, f.name)
|
||||
proposal = network.consortium.get_any_active_member().propose(
|
||||
primary, proposal_body
|
||||
)
|
||||
proposal.vote_for = careful_vote
|
||||
network.consortium.vote_using_majority(primary, proposal)
|
||||
|
||||
|
||||
|
@ -77,10 +78,11 @@ def test_module_set_and_remove(network, args):
|
|||
assert r.body.json()["js"] == MODULE_CONTENT_1, r.body
|
||||
|
||||
LOG.info("Member makes a module remove proposal")
|
||||
proposal_body, _ = ccf.proposal_generator.remove_module(MODULE_PATH_1)
|
||||
proposal_body, careful_vote = ccf.proposal_generator.remove_module(MODULE_PATH_1)
|
||||
proposal = network.consortium.get_any_active_member().propose(
|
||||
primary, proposal_body
|
||||
)
|
||||
proposal.vote_for = careful_vote
|
||||
network.consortium.vote_using_majority(primary, proposal)
|
||||
|
||||
with primary.client(
|
||||
|
@ -106,10 +108,11 @@ def test_modules_remove(network, args):
|
|||
assert r.body.json()["js"] == MODULE_CONTENT_1, r.body
|
||||
|
||||
LOG.info("Member makes a prefix-based modules remove proposal")
|
||||
proposal_body, _ = ccf.proposal_generator.remove_modules(MODULE_PREFIX_1)
|
||||
proposal_body, careful_vote = ccf.proposal_generator.remove_modules(MODULE_PREFIX_1)
|
||||
proposal = network.consortium.get_any_active_member().propose(
|
||||
primary, proposal_body
|
||||
)
|
||||
proposal.vote_for = careful_vote
|
||||
network.consortium.vote_using_majority(primary, proposal)
|
||||
|
||||
with primary.client(
|
||||
|
@ -177,10 +180,11 @@ def test_app_bundle(network, args):
|
|||
assert r.body.json() == {"error": "invalid operand type"}, r.body
|
||||
|
||||
LOG.info("Removing js app")
|
||||
proposal_body, _ = ccf.proposal_generator.remove_js_app()
|
||||
proposal_body, careful_vote = ccf.proposal_generator.remove_js_app()
|
||||
proposal = network.consortium.get_any_active_member().propose(
|
||||
primary, proposal_body
|
||||
)
|
||||
proposal.vote_for = careful_vote
|
||||
network.consortium.vote_using_majority(primary, proposal)
|
||||
|
||||
LOG.info("Verifying that modules and endpoints were removed")
|
||||
|
|
Загрузка…
Ссылка в новой задаче