Skip to content

Commit a130207

Browse files
bors[bot]surahman
andauthored
Merge #2102
2102: [cli] Toggle Primary Instance (petenv) r=ricab a=surahman **_Issue #1138:_** I have added support to handle the `client.primary-name=` command. I am not sure about the mock tests I have set up and they would require some review. When stepping through the tests with a debugger I was unable to hit breakpoints on `set` and `set_aux` within the `utils/settings.cpp` file - which is typically an indicator of an issue. Manual argument passing tests work on the `cmdl`. Co-authored-by: Saad Ur Rahman <[email protected]>
2 parents 2126b95 + 4cb066e commit a130207

File tree

12 files changed

+268
-51
lines changed

12 files changed

+268
-51
lines changed

include/multipass/cli/client_platform.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2018-2019 Canonical, Ltd.
2+
* Copyright (C) 2018-2021 Canonical, Ltd.
33
*
44
* This program is free software; you can redistribute it and/or modify
55
* it under the terms of the GNU General Public License as published by
@@ -32,7 +32,7 @@ namespace platform
3232
void parse_transfer_entry(const QString& entry, QString& path, QString& instance_name);
3333
int getuid();
3434
int getgid();
35-
void open_multipass_shell(const QString& instance_name);
35+
void open_multipass_shell(const QString& instance_name); // precondition: requires a valid instance name
3636
QStringList gui_tray_notification_strings();
3737
}
3838
}

src/client/cli/cmd/launch.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,15 @@ mp::ParseCode cmd::Launch::parse_args(mp::ArgParser* parser)
185185
"in bytes, or with K, M, G suffix.\nMinimum: {}, default: {}.",
186186
min_memory_size, default_memory_size)),
187187
"mem", QString::fromUtf8(default_memory_size)); // In MB's
188-
QCommandLineOption nameOption(
189-
{"n", "name"},
190-
QString{"Name for the instance. If it is '%1' (the configured primary instance name), the user's home "
191-
"directory is mounted inside the newly launched instance, in '%2'."}
192-
.arg(petenv_name, mp::home_automount_dir),
193-
"name");
188+
189+
const auto name_option_desc =
190+
petenv_name.isEmpty()
191+
? QString{"Name for the instance."}
192+
: QString{"Name for the instance. If it is '%1' (the configured primary instance name), the user's home "
193+
"directory is mounted inside the newly launched instance, in '%2'."}
194+
.arg(petenv_name, mp::home_automount_dir);
195+
196+
QCommandLineOption nameOption({"n", "name"}, name_option_desc, "name");
194197
QCommandLineOption cloudInitOption("cloud-init", "Path to a user-data cloud-init configuration, or '-' for stdin",
195198
"file");
196199
QCommandLineOption networkOption("network",

src/client/cli/cmd/restart.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,17 @@ QString cmd::Restart::description() const
8282
mp::ParseCode cmd::Restart::parse_args(mp::ArgParser* parser)
8383
{
8484
const auto petenv_name = MP_SETTINGS.get(petenv_key);
85-
parser->addPositionalArgument(
86-
"name",
87-
QString{"Names of instances to restart. If omitted, and without the --all option, '%1' will be assumed."}.arg(
88-
petenv_name),
89-
"[<name> ...]");
85+
86+
const auto& [description, syntax] =
87+
petenv_name.isEmpty()
88+
? std::make_pair(QString{"Names of instances to restart."}, QString{"<name> [<name> ...]"})
89+
: std::make_pair(
90+
QString{
91+
"Names of instances to restart. If omitted, and without the --all option, '%1' will be assumed."}
92+
.arg(petenv_name),
93+
QString{"[<name> ...]"});
94+
95+
parser->addPositionalArgument("name", description, syntax);
9096

9197
QCommandLineOption all_option(all_option_name, "Restart all instances");
9298
parser->addOption(all_option);
@@ -108,9 +114,14 @@ mp::ParseCode cmd::Restart::parse_args(mp::ArgParser* parser)
108114
return ParseCode::CommandLineError;
109115
}
110116

111-
auto parse_code = check_for_name_and_all_option_conflict(parser, cerr, /*allow_empty=*/true);
117+
auto parse_code = check_for_name_and_all_option_conflict(parser, cerr, /*allow_empty=*/!petenv_name.isEmpty());
112118
if (parse_code != ParseCode::Ok)
119+
{
120+
if (petenv_name.isEmpty() && parser->positionalArguments().isEmpty())
121+
fmt::print(cerr, "Note: the primary instance is disabled.\n");
122+
113123
return parse_code;
124+
}
114125

115126
request.mutable_instance_names()->CopyFrom(add_instance_names(parser, /*default_name=*/petenv_name.toStdString()));
116127

src/client/cli/cmd/shell.cpp

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,16 @@ QString cmd::Shell::description() const
128128

129129
mp::ParseCode cmd::Shell::parse_args(mp::ArgParser* parser)
130130
{
131-
parser->addPositionalArgument(
132-
"name",
133-
QString{
134-
"Name of the instance to open a shell on. If omitted, '%1' (the configured primary instance name) will be "
135-
"assumed. If the instance is not running, an attempt is made to start it (see `start` for more info)."}
136-
.arg(petenv_name),
137-
"[<name>]");
131+
const auto& [description, syntax] =
132+
petenv_name.isEmpty()
133+
? std::make_pair(QString{"Name of instance to open a shell on."}, QString{"<name>"})
134+
: std::make_pair(QString{"Name of the instance to open a shell on. If omitted, '%1' (the configured "
135+
"primary instance name) will be assumed. If the instance is not running, an "
136+
"attempt is made to start it (see `start` for more info)."}
137+
.arg(petenv_name),
138+
QString{"[<name>]"});
139+
140+
parser->addPositionalArgument("name", description, syntax);
138141

139142
mp::cmd::add_timeout(parser);
140143

@@ -162,10 +165,15 @@ mp::ParseCode cmd::Shell::parse_args(mp::ArgParser* parser)
162165
cerr << "Too many arguments given\n";
163166
status = ParseCode::CommandLineError;
164167
}
165-
else
168+
else if (auto instance = num_args ? pos_args.first() : petenv_name; !instance.isEmpty())
166169
{
167170
auto entry = request.add_instance_name();
168-
entry->append(num_args ? pos_args.first().toStdString() : petenv_name.toStdString());
171+
entry->append(instance.toStdString());
172+
}
173+
else
174+
{
175+
fmt::print(cerr, "The primary instance is disabled, please provide an instance name.\n");
176+
return ParseCode::CommandLineError;
169177
}
170178

171179
return status;

src/client/cli/cmd/start.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,17 @@ QString cmd::Start::description() const
155155

156156
mp::ParseCode cmd::Start::parse_args(mp::ArgParser* parser)
157157
{
158-
parser->addPositionalArgument(
159-
"name",
160-
QString{"Names of instances to start. If omitted, and without the --all option, '%1' (the configured primary "
161-
"instance name) will be assumed. If '%1' does not exist but is included in a successful start command "
162-
"(either implicitly or explicitly), it is launched automatically (see `launch` for more info)."}
163-
.arg(petenv_name),
164-
"[<name> ...]");
158+
const auto& [description, syntax] =
159+
petenv_name.isEmpty()
160+
? std::make_pair(QString{"Names of instances to start."}, QString{"<name> [<name> ...]"})
161+
: std::make_pair(QString{"Names of instances to start. If omitted, and without the --all option, '%1' (the "
162+
"configured primary instance name) will be assumed. If '%1' does not exist but is "
163+
"included in a successful start command either implicitly or explicitly), it is "
164+
"launched automatically (see `launch` for more info)."}
165+
.arg(petenv_name),
166+
QString{"[<name> ...]"});
167+
168+
parser->addPositionalArgument("name", description, syntax);
165169

166170
QCommandLineOption all_option(all_option_name, "Start all instances");
167171
parser->addOption(all_option);
@@ -173,9 +177,14 @@ mp::ParseCode cmd::Start::parse_args(mp::ArgParser* parser)
173177
if (status != ParseCode::Ok)
174178
return status;
175179

176-
auto parse_code = check_for_name_and_all_option_conflict(parser, cerr, /*allow_empty=*/true);
180+
auto parse_code = check_for_name_and_all_option_conflict(parser, cerr, /*allow_empty=*/!petenv_name.isEmpty());
177181
if (parse_code != ParseCode::Ok)
182+
{
183+
if (petenv_name.isEmpty() && parser->positionalArguments().isEmpty())
184+
fmt::print(cerr, "Note: the primary instance is disabled.\n");
185+
178186
return parse_code;
187+
}
179188

180189
try
181190
{

src/client/cli/cmd/stop.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,16 @@ QString cmd::Stop::description() const
6868
mp::ParseCode cmd::Stop::parse_args(mp::ArgParser* parser)
6969
{
7070
const auto petenv_name = MP_SETTINGS.get(petenv_key);
71-
parser->addPositionalArgument(
72-
"name",
73-
QString{"Names of instances to stop. If omitted, and without the --all option, '%1' will be assumed"}.arg(
74-
petenv_name),
75-
"[<name> ...]");
71+
72+
const auto& [description, syntax] =
73+
petenv_name.isEmpty()
74+
? std::make_pair(QString{"Names of instances to stop."}, QString{"<name> [<name> ...]"})
75+
: std::make_pair(
76+
QString{"Names of instances to stop. If omitted, and without the --all option, '%1' will be assumed."}
77+
.arg(petenv_name),
78+
QString{"[<name> ...]"});
79+
80+
parser->addPositionalArgument("name", description, syntax);
7681

7782
QCommandLineOption all_option(all_option_name, "Stop all instances");
7883
QCommandLineOption time_option({"t", "time"}, "Time from now, in minutes, to delay shutdown of the instance",
@@ -84,9 +89,14 @@ mp::ParseCode cmd::Stop::parse_args(mp::ArgParser* parser)
8489
if (status != ParseCode::Ok)
8590
return status;
8691

87-
auto parse_code = check_for_name_and_all_option_conflict(parser, cerr, /*allow_empty=*/true);
92+
auto parse_code = check_for_name_and_all_option_conflict(parser, cerr, /*allow_empty=*/!petenv_name.isEmpty());
8893
if (parse_code != ParseCode::Ok)
94+
{
95+
if (petenv_name.isEmpty() && parser->positionalArguments().isEmpty())
96+
fmt::print(cerr, "Note: the primary instance is disabled.\n");
97+
8998
return parse_code;
99+
}
90100

91101
if (parser->isSet(time_option) && parser->isSet(cancel_option))
92102
{

src/client/cli/cmd/suspend.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,17 @@ QString cmd::Suspend::description() const
6868
mp::ParseCode cmd::Suspend::parse_args(mp::ArgParser* parser)
6969
{
7070
const auto petenv_name = MP_SETTINGS.get(petenv_key);
71-
parser->addPositionalArgument(
72-
"name",
73-
QString{"Names of instances to suspend. If omitted, and without the --all option, '%1' will be assumed."}.arg(
74-
petenv_name),
75-
"[<name> ...]");
71+
72+
const auto& [description, syntax] =
73+
petenv_name.isEmpty()
74+
? std::make_pair(QString{"Names of instances to suspend."}, QString{"<name> [<name> ...]"})
75+
: std::make_pair(
76+
QString{
77+
"Names of instances to suspend. If omitted, and without the --all option, '%1' will be assumed."}
78+
.arg(petenv_name),
79+
QString{"[<name> ...]"});
80+
81+
parser->addPositionalArgument("name", description, syntax);
7682

7783
QCommandLineOption all_option("all", "Suspend all instances");
7884
parser->addOptions({all_option});
@@ -81,9 +87,14 @@ mp::ParseCode cmd::Suspend::parse_args(mp::ArgParser* parser)
8187
if (status != ParseCode::Ok)
8288
return status;
8389

84-
auto parse_code = check_for_name_and_all_option_conflict(parser, cerr, /*allow_empty=*/true);
90+
auto parse_code = check_for_name_and_all_option_conflict(parser, cerr, /*allow_empty=*/!petenv_name.isEmpty());
8591
if (parse_code != ParseCode::Ok)
92+
{
93+
if (petenv_name.isEmpty() && parser->positionalArguments().isEmpty())
94+
fmt::print(cerr, "Note: the primary instance is disabled.\n");
95+
8696
return parse_code;
97+
}
8798

8899
request.mutable_instance_names()->CopyFrom(add_instance_names(parser, /*default_name=*/petenv_name.toStdString()));
89100

src/client/gui/gui_cmd.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,12 @@ mp::ReturnCode cmd::GuiCmd::run(mp::ArgParser* parser)
104104
}
105105

106106
update_hotkey();
107-
QObject::connect(&hotkey, &QHotkey::activated, qApp, [&]() { mp::cli::platform::open_multipass_shell(QString()); });
107+
QObject::connect(&hotkey, &QHotkey::activated, qApp,
108+
[&]()
109+
{
110+
if (!current_petenv_name.empty())
111+
mp::cli::platform::open_multipass_shell(QString::fromStdString(current_petenv_name));
112+
});
108113

109114
create_actions();
110115
create_menu();
@@ -158,7 +163,7 @@ void cmd::GuiCmd::create_actions()
158163
{&petenv_start_action, &petenv_shell_action, &petenv_stop_action});
159164

160165
QObject::connect(&petenv_shell_action, &QAction::triggered,
161-
[] { mp::cli::platform::open_multipass_shell(QString()); });
166+
[this] { mp::cli::platform::open_multipass_shell(QString::fromStdString(current_petenv_name)); });
162167
QObject::connect(&petenv_stop_action, &QAction::triggered, [this] {
163168
future_synchronizer.addFuture(QtConcurrent::run(this, &GuiCmd::stop_instance_for, current_petenv_name));
164169
});
@@ -235,6 +240,12 @@ void cmd::GuiCmd::update_menu()
235240
{
236241
about_separator->setVisible(true);
237242
}
243+
244+
const bool petenv_visibility = !current_petenv_name.empty();
245+
petenv_actions_separator->setVisible(petenv_visibility);
246+
petenv_start_action.setVisible(petenv_visibility);
247+
petenv_shell_action.setVisible(petenv_visibility);
248+
petenv_stop_action.setVisible(petenv_visibility);
238249
}
239250

240251
void cmd::GuiCmd::update_about_menu()

src/client/gui/gui_cmd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2019-2020 Canonical, Ltd.
2+
* Copyright (C) 2019-2021 Canonical, Ltd.
33
*
44
* This program is free software; you can redistribute it and/or modify
55
* it under the terms of the GNU General Public License as published by

src/platform/client/client_platform_linux.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2019 Canonical, Ltd.
2+
* Copyright (C) 2019-2021 Canonical, Ltd.
33
*
44
* This program is free software; you can redistribute it and/or modify
55
* it under the terms of the GNU General Public License as published by
@@ -24,6 +24,7 @@ namespace mcp = multipass::cli::platform;
2424

2525
void mcp::open_multipass_shell(const QString& instance_name)
2626
{
27+
assert(!instance_name.isEmpty() && "Instance name cannot be empty");
2728
QProcess::startDetached(
2829
"xterm", {"-title", instance_name, "-j", "-e", QString("multipass shell %1 || read").arg(instance_name)});
2930
}

0 commit comments

Comments
 (0)