-
Notifications
You must be signed in to change notification settings - Fork 651
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add local.instance.bridged
setting.
#3193
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
|
||
#include <multipass/constants.h> | ||
#include <multipass/exceptions/invalid_memory_size_exception.h> | ||
#include <multipass/settings/bool_setting_spec.h> | ||
|
||
#include <QRegularExpression> | ||
#include <QStringList> | ||
|
@@ -30,6 +31,7 @@ namespace | |
constexpr auto cpus_suffix = "cpus"; | ||
constexpr auto mem_suffix = "memory"; | ||
constexpr auto disk_suffix = "disk"; | ||
constexpr auto bridged_suffix = "bridged"; | ||
|
||
enum class Operation | ||
{ | ||
|
@@ -46,7 +48,7 @@ QRegularExpression make_key_regex() | |
{ | ||
const auto instance_pattern = QStringLiteral("(?<instance>.+)"); | ||
const auto prop_template = QStringLiteral("(?<property>%1)"); | ||
const auto either_prop = QStringList{cpus_suffix, mem_suffix, disk_suffix}.join("|"); | ||
const auto either_prop = QStringList{cpus_suffix, mem_suffix, disk_suffix, bridged_suffix}.join("|"); | ||
const auto prop_pattern = prop_template.arg(either_prop); | ||
|
||
const auto key_template = QStringLiteral(R"(%1\.%2\.%3)"); | ||
|
@@ -153,6 +155,27 @@ void update_disk(const QString& key, const QString& val, mp::VirtualMachine& ins | |
} | ||
} | ||
|
||
bool is_bridged(const mp::VMSpecs& spec) | ||
{ | ||
return std::any_of(spec.extra_interfaces.cbegin(), spec.extra_interfaces.cend(), | ||
[](const auto& network) -> bool { return network.auto_mode; }); | ||
} | ||
|
||
void update_bridged(const QString& key, const QString& val, mp::VirtualMachine& instance, mp::VMSpecs& spec) | ||
{ | ||
auto bridged = mp::BoolSettingSpec{key, "false"}.interpret(val) == "true"; | ||
|
||
if (!bridged && is_bridged(spec)) | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to elaborate the roles of bridged boolean (it looks like is checking user's input) and is_bridged(spec) (looks like checking if the spec is bridged)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, sorry if it wasn't clear. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, thanks. |
||
throw mp::InvalidSettingException{key, val, "Bridged interface cannot be removed"}; | ||
} | ||
|
||
if (bridged) | ||
{ | ||
// TODO: add the interface | ||
} | ||
} | ||
|
||
} // namespace | ||
|
||
mp::InstanceSettingsException::InstanceSettingsException(const std::string& reason, const std::string& instance, | ||
|
@@ -180,7 +203,7 @@ std::set<QString> mp::InstanceSettingsHandler::keys() const | |
|
||
std::set<QString> ret; | ||
for (const auto& item : vm_instance_specs) | ||
for (const auto& suffix : {cpus_suffix, mem_suffix, disk_suffix}) | ||
for (const auto& suffix : {cpus_suffix, mem_suffix, disk_suffix, bridged_suffix}) | ||
ret.insert(key_template.arg(item.first.c_str()).arg(suffix)); | ||
|
||
return ret; | ||
|
@@ -191,6 +214,8 @@ QString mp::InstanceSettingsHandler::get(const QString& key) const | |
auto [instance_name, property] = parse_key(key); | ||
const auto& spec = find_spec(instance_name); | ||
|
||
if (property == bridged_suffix) | ||
return is_bridged(spec) ? "true" : "false"; | ||
if (property == cpus_suffix) | ||
return QString::number(spec.num_cores); | ||
if (property == mem_suffix) | ||
|
@@ -214,6 +239,10 @@ void mp::InstanceSettingsHandler::set(const QString& key, const QString& val) | |
|
||
if (property == cpus_suffix) | ||
update_cpus(key, val, instance, spec); | ||
else if (property == bridged_suffix) | ||
{ | ||
update_bridged(key, val, instance, spec); | ||
} | ||
else | ||
{ | ||
auto size = get_memory_size(key, val); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -82,7 +82,9 @@ struct TestInstanceSettingsHandler : public Test | |
std::unordered_map<std::string, mp::VirtualMachine::ShPtr> deleted_vms; | ||
std::unordered_set<std::string> preparing_vms; | ||
bool fake_persister_called = false; | ||
inline static constexpr auto properties = std::array{"cpus", "disk", "memory"}; | ||
inline static constexpr std::array numeric_properties{"cpus", "disk", "memory"}; | ||
inline static constexpr std::array boolean_properties{"bridged"}; | ||
inline static constexpr std::array properties{"cpus", "disk", "memory", "bridged"}; | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know it stems from the original code, but maybe change the copy initialization to direct initialization to make the code cleaner.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, looks better that way. Changed. |
||
|
||
QString make_key(const QString& instance_name, const QString& property) | ||
|
@@ -193,6 +195,26 @@ TEST_F(TestInstanceSettingsHandler, getReturnsMemorySizesInHumanReadableFormat) | |
EXPECT_EQ(handler.get(make_key(target_instance_name, "memory")), "337.6KiB"); | ||
} | ||
|
||
struct TestBridgedInstanceSettings : public TestInstanceSettingsHandler, public WithParamInterface<bool> | ||
{ | ||
}; | ||
|
||
TEST_P(TestBridgedInstanceSettings, getFetchesBridged) | ||
{ | ||
const auto bridged = GetParam(); | ||
|
||
constexpr auto target_instance_name = "lemmy"; | ||
specs.insert({{"mikkey", {}}, {"phil", {}}, {target_instance_name, {}}}); | ||
|
||
// An extra interface in auto mode will result in bridging. | ||
specs[target_instance_name].extra_interfaces = {{"id", "52:54:00:12:34:56", bridged}}; | ||
|
||
const auto got = make_handler().get(make_key(target_instance_name, "bridged")); | ||
EXPECT_EQ(got, bridged ? "true" : "false"); | ||
} | ||
|
||
INSTANTIATE_TEST_SUITE_P(getFetchesBridged, TestBridgedInstanceSettings, Values(true, false)); | ||
|
||
TEST_F(TestInstanceSettingsHandler, getFetchesPropertiesOfInstanceInSpecialState) | ||
{ | ||
constexpr auto preparing_instance = "nouvelle", deleted_instance = "vague"; | ||
|
@@ -424,6 +446,19 @@ TEST_F(TestInstanceSettingsHandler, setRefusesWrongProperty) | |
EXPECT_EQ(original_specs, specs[target_instance_name]); | ||
} | ||
|
||
TEST_F(TestInstanceSettingsHandler, setRefusesToUnbridge) | ||
{ | ||
constexpr auto target_instance_name = "hendrix"; | ||
specs.insert({{"voodoo", {}}, {"chile", {}}, {target_instance_name, {}}}); | ||
specs[target_instance_name].extra_interfaces = {{"id", "52:54:00:78:90:12", true}}; | ||
|
||
mock_vm(target_instance_name); // TODO: make this an expectation. | ||
|
||
MP_EXPECT_THROW_THAT(make_handler().set(make_key(target_instance_name, "bridged"), "false"), | ||
mp::InvalidSettingException, | ||
mpt::match_what(HasSubstr("Bridged interface cannot be removed"))); | ||
} | ||
|
||
using VMSt = mp::VirtualMachine::State; | ||
using Property = const char*; | ||
using PropertyAndState = std::tuple<Property, VMSt>; // no subliminal political msg intended :) | ||
|
@@ -476,7 +511,8 @@ TEST_P(TestInstanceModOnStoppedInstance, setWorksOnOtherStates) | |
} | ||
|
||
INSTANTIATE_TEST_SUITE_P(TestInstanceSettingsHandler, TestInstanceModOnStoppedInstance, | ||
Combine(ValuesIn(TestInstanceSettingsHandler::properties), Values(VMSt::off, VMSt::stopped))); | ||
Combine(ValuesIn(TestInstanceSettingsHandler::numeric_properties), | ||
Values(VMSt::off, VMSt::stopped))); | ||
|
||
struct TestInstanceModPersists : public TestInstanceSettingsHandler, public WithParamInterface<Property> | ||
{ | ||
|
@@ -496,7 +532,7 @@ TEST_P(TestInstanceModPersists, setPersistsInstances) | |
} | ||
|
||
INSTANTIATE_TEST_SUITE_P(TestInstanceSettingsHandler, TestInstanceModPersists, | ||
ValuesIn(TestInstanceSettingsHandler::properties)); | ||
ValuesIn(TestInstanceSettingsHandler::numeric_properties)); | ||
|
||
TEST_F(TestInstanceSettingsHandler, setRefusesToModifyInstancesInSpecialState) | ||
{ | ||
|
@@ -554,12 +590,12 @@ TEST_F(TestInstanceSettingsHandler, getAndSetThrowOnBadKey) | |
|
||
using PlainValue = const char*; | ||
using PropertyValue = std::tuple<Property, PlainValue>; | ||
struct TestInstanceSettingsHandlerBadValues : public TestInstanceSettingsHandler, | ||
public WithParamInterface<PropertyValue> | ||
struct TestInstanceSettingsHandlerBadNumericValues : public TestInstanceSettingsHandler, | ||
public WithParamInterface<PropertyValue> | ||
{ | ||
}; | ||
|
||
TEST_P(TestInstanceSettingsHandlerBadValues, setRefusesBadValues) | ||
TEST_P(TestInstanceSettingsHandlerBadNumericValues, setRefusesBadNumericValues) | ||
{ | ||
constexpr auto target_instance_name = "xanana"; | ||
const auto& [property, bad_val] = GetParam(); | ||
|
@@ -574,8 +610,32 @@ TEST_P(TestInstanceSettingsHandlerBadValues, setRefusesBadValues) | |
EXPECT_EQ(original_specs, specs[target_instance_name]); | ||
} | ||
|
||
INSTANTIATE_TEST_SUITE_P(TestInstanceSettingsHandler, TestInstanceSettingsHandlerBadValues, | ||
Combine(ValuesIn(TestInstanceSettingsHandler::properties), | ||
INSTANTIATE_TEST_SUITE_P(TestInstanceSettingsHandler, TestInstanceSettingsHandlerBadNumericValues, | ||
Combine(ValuesIn(TestInstanceSettingsHandler::numeric_properties), | ||
Values("0", "2u", "1.5f", "2.0", "0xa", "0x8", "-4", "-1", "rubbish", " 123nonsense ", | ||
"¤9", "\n", "\t", "^", ""))); | ||
|
||
struct TestInstanceSettingsHandlerBadBooleanValues : public TestInstanceSettingsHandler, | ||
public WithParamInterface<PropertyValue> | ||
{ | ||
}; | ||
|
||
TEST_P(TestInstanceSettingsHandlerBadBooleanValues, setRefusesBadBooleanValues) | ||
{ | ||
constexpr auto target_instance_name = "zappa"; | ||
const auto& [property, bad_val] = GetParam(); | ||
|
||
const auto original_specs = specs[target_instance_name]; | ||
mock_vm(target_instance_name); // TODO: make this an expectation. | ||
|
||
MP_EXPECT_THROW_THAT(make_handler().set(make_key(target_instance_name, property), bad_val), | ||
mp::InvalidSettingException, | ||
mpt::match_what(AllOf(HasSubstr(bad_val), HasSubstr("try \"true\" or \"false\"")))); | ||
|
||
EXPECT_EQ(original_specs, specs[target_instance_name]); | ||
} | ||
|
||
INSTANTIATE_TEST_SUITE_P(TestInstanceSettingsHandler, TestInstanceSettingsHandlerBadBooleanValues, | ||
Combine(ValuesIn(TestInstanceSettingsHandler::boolean_properties), | ||
Values("apostrophe", "(')", "1974"))); | ||
} // namespace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @luis4a0, I think for our purposes we'd better consider the instance bridged if and only if it has an extra_interface corresponding to the network that is currently set for
local.bridged-network
. That would make it symmetric tolaunch --bridged
and allow adding multiple interfaces.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @ricab, thanks for your comment. You're right, I didn't think on that. I changed the code to reflect this behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, thanks!