Add global.with-veth/with-ipv6 config defaults for -n/--network creation

Replaces --no-ipv6/--no-veth (plain flags) with --with-ipv6/--with-veth,
each taking an explicit true/false value (e.g. --with-veth=false), parsed
via the same parse_bool_flag() the config file itself already uses (now
exported from config_file.h so cli_args.cpp can reuse it).

create_network_command() now resolves ipv6/veth as CLI flag -> config's own
global.with-ipv6/global.with-veth -> true, so a host that always wants the
tap+relay fallback (or no IPv6) can set it once in the config instead of
passing the flag on every network creation. -w/--write-config fills in both
new keys like the existing six unshare-* bools.
This commit is contained in:
2026-09-05 08:36:12 +00:00
parent 1f52bc5f6f
commit cdf9dcd210
10 changed files with 287 additions and 112 deletions
@@ -102,3 +102,26 @@ TEST_CASE("config file -> NamespaceConfig -> bwrap argv: default (unset) config
CHECK(contains(argv, flag));
}
}
TEST_CASE("config file -> AppConfig: global.with-veth/with-ipv6 round-trip", "[integration]") {
ScratchXdgDirs scratch;
auto config_path = scratch.path() / "config.yaml";
AppConfig written;
written.with_veth = false;
written.with_ipv6 = false;
REQUIRE(write_config_file(config_path, written));
auto loaded = load_config_file(config_path);
REQUIRE(loaded.has_value());
CHECK(loaded->with_veth == std::optional<bool>(false));
CHECK(loaded->with_ipv6 == std::optional<bool>(false));
// Same "unset means enabled" convention as the six unshare-* keys --
// create_network_command()'s own resolution (commands.cpp) is
// `args.network_with_*_flag.value_or(config.with_*.value_or(true))`.
auto loaded_empty = load_config_file(scratch.path() / "nonexistent.yaml");
REQUIRE(loaded_empty.has_value());
CHECK_FALSE(loaded_empty->with_veth.has_value());
CHECK_FALSE(loaded_empty->with_ipv6.has_value());
}
+35
View File
@@ -171,6 +171,41 @@ TEST_CASE("parse_args: -v with -r accumulates volume_specs as (spec, container-p
CHECK(result.args.volume_specs[0].second == "/data");
}
TEST_CASE("parse_args: --with-ipv6/--with-veth accept true/false and default to unset", "[unit]") {
auto plain = run_parse({"-n", "net1", "--extern"});
REQUIRE_FALSE(plain.exit_code.has_value());
CHECK_FALSE(plain.args.network_with_ipv6_flag.has_value());
CHECK_FALSE(plain.args.network_with_veth_flag.has_value());
auto disabled = run_parse({"-n", "net1", "--extern", "--with-ipv6=false", "--with-veth=false"});
REQUIRE_FALSE(disabled.exit_code.has_value());
REQUIRE(disabled.args.network_with_ipv6_flag.has_value());
CHECK(*disabled.args.network_with_ipv6_flag == false);
REQUIRE(disabled.args.network_with_veth_flag.has_value());
CHECK(*disabled.args.network_with_veth_flag == false);
// Same accepted forms as the config file itself (parse_bool_flag(),
// config_file.h) -- not just a literal "true"/"false".
auto on = run_parse({"-n", "net1", "--intern", "--with-ipv6=on", "--with-veth=1"});
REQUIRE_FALSE(on.exit_code.has_value());
REQUIRE(on.args.network_with_ipv6_flag.has_value());
CHECK(*on.args.network_with_ipv6_flag == true);
REQUIRE(on.args.network_with_veth_flag.has_value());
CHECK(*on.args.network_with_veth_flag == true);
}
TEST_CASE("parse_args: --with-ipv6 with an unrecognized value is a parse error", "[unit]") {
auto result = run_parse({"-n", "net1", "--extern", "--with-ipv6=maybe"});
REQUIRE(result.exit_code.has_value());
CHECK(*result.exit_code == 1);
}
TEST_CASE("parse_args: --subnet6 and --with-ipv6=false can't be used together", "[unit]") {
auto result = run_parse({"-n", "net1", "--extern", "--subnet6", "fdf0::/64", "--with-ipv6=false"});
REQUIRE(result.exit_code.has_value());
CHECK(*result.exit_code == 1);
}
TEST_CASE("parse_args: --kill requires a numeric pid", "[unit]") {
auto ok = run_parse({"--kill", "5678"});
REQUIRE_FALSE(ok.exit_code.has_value());
+1 -1
View File
@@ -105,7 +105,7 @@ TEST_CASE("allocate_ipv6_subnet: skips a subnet already in use", "[unit]") {
}
TEST_CASE("allocate_ipv6_subnet: an ipv6-disabled existing entry doesn't block reuse of its subnet6", "[unit]") {
// A network created with --no-ipv6 has ipv6=false and an empty subnet6
// A network created with --with-ipv6=false has ipv6=false and an empty subnet6
// -- nothing to collide with, so this is really just confirming
// allocate_ipv6_subnet() doesn't crash/misbehave on such an entry.
std::vector<NetworkEntry> existing = {{"v4only", NetworkKind::extern_, "10.168.0.0/24", false, "", true}};