Skip to content

Commit 1aa280e

Browse files
bors[bot]Chris Townsend
andcommitted
Merge #619
619: Remove sshfs install from cloud-init r=ricab a=townsend2010 Also move "just in time" sshfs install logic into the daemon. Fixes #615 Co-authored-by: Chris Townsend <[email protected]>
2 parents 09c6a2e + 9806c08 commit 1aa280e

File tree

17 files changed

+110
-145
lines changed

17 files changed

+110
-145
lines changed

include/multipass/utils.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ std::string timestamp();
6565
bool is_running(const VirtualMachine::State& state);
6666
void wait_until_ssh_up(VirtualMachine* virtual_machine, std::chrono::milliseconds timeout,
6767
std::function<void()> const& process_vm_events = []() { });
68-
void wait_for_cloud_init(VirtualMachine* virtual_machine, std::chrono::milliseconds timeout,
69-
std::function<void()> const& process_vm_events = []() { });
7068

7169
enum class TimeoutAction
7270
{

include/multipass/virtual_machine.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ class VirtualMachine
5858
virtual std::string ipv4() = 0;
5959
virtual std::string ipv6() = 0;
6060
virtual void wait_until_ssh_up(std::chrono::milliseconds timeout) = 0;
61-
virtual void wait_for_cloud_init(std::chrono::milliseconds timeout) = 0;
6261
virtual void update_state() = 0;
6362

6463
VirtualMachine::State state;

src/client/cmd/common_cli.cpp

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2018 Canonical, Ltd.
2+
* Copyright (C) 2018-2019 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
@@ -102,24 +102,3 @@ mp::ReturnCode cmd::standard_failure_handler_for(const std::string& command, std
102102

103103
return return_code_for(status.error_code());
104104
}
105-
106-
void cmd::install_sshfs_for(const std::string& instance_name, int verbosity_level, grpc::Channel* rpc_channel,
107-
mp::Rpc::Stub* stub, std::ostream& cout, std::ostream& cerr)
108-
{
109-
std::vector<Command::UPtr> command;
110-
command.push_back(std::make_unique<Exec>(*rpc_channel, *stub, cout, cerr));
111-
112-
auto args = QStringList() << "" // This is just a dummy string for the unnecessary binary name
113-
<< "exec" << QString::fromStdString(instance_name) << "--"
114-
<< "sudo"
115-
<< "bash"
116-
<< "-c"
117-
<< "apt update && apt install -y sshfs";
118-
ArgParser exec_parser{args, command, cout, cerr};
119-
exec_parser.setVerbosityLevel(verbosity_level);
120-
exec_parser.parse();
121-
122-
fmt::print(cerr, "The sshfs package is missing in \"{}\". Installing...\n", instance_name);
123-
if (exec_parser.chosenCommand()->run(&exec_parser) == mp::ReturnCode::Ok)
124-
fmt::print(cerr, "\n***Please re-run the mount command.\n");
125-
}

src/client/cmd/common_cli.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2018 Canonical, Ltd.
2+
* Copyright (C) 2018-2019 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
@@ -41,8 +41,6 @@ ParseCode handle_format_option(ArgParser* parser, Formatter** chosen_formatter,
4141
std::string instance_action_message_for(const InstanceNames& instance_names, const std::string& action_name);
4242
ReturnCode standard_failure_handler_for(const std::string& command, std::ostream& cerr, const grpc::Status& status,
4343
const std::string& error_details = std::string());
44-
void install_sshfs_for(const std::string& instance_name, int verbosity_level, grpc::Channel* rpc_channel,
45-
Rpc::Stub* stub, std::ostream& cout, std::ostream& cerr);
4644
} // namespace cmd
4745
} // namespace multipass
4846

src/client/cmd/mount.cpp

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2017-2018 Canonical, Ltd.
2+
* Copyright (C) 2017-2019 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
@@ -17,8 +17,8 @@
1717

1818
#include "mount.h"
1919
#include "common_cli.h"
20-
#include "exec.h"
2120

21+
#include "animated_spinner.h"
2222
#include <multipass/cli/argparser.h>
2323
#include <multipass/cli/client_platform.h>
2424
#include <multipass/logging/log.h>
@@ -59,29 +59,27 @@ mp::ReturnCode cmd::Mount::run(mp::ArgParser* parser)
5959
return parser->returnCodeFrom(ret);
6060
}
6161

62-
auto on_success = [](mp::MountReply& reply) {
62+
mp::AnimatedSpinner spinner{cout};
63+
64+
auto on_success = [&spinner](mp::MountReply& reply) {
65+
spinner.stop();
6366
return ReturnCode::Ok;
6467
};
6568

66-
auto on_failure = [this, &parser](grpc::Status& status) {
67-
auto ret = standard_failure_handler_for(name(), cerr, status);
68-
if (!status.error_details().empty())
69-
{
70-
mp::MountError mount_error;
71-
mount_error.ParseFromString(status.error_details());
69+
auto on_failure = [this, &spinner](grpc::Status& status) {
70+
spinner.stop();
7271

73-
if (mount_error.error_code() == mp::MountError::SSHFS_MISSING)
74-
{
75-
cmd::install_sshfs_for(mount_error.instance_name(), parser->verbosityLevel(), rpc_channel, stub, cout,
76-
cerr);
77-
}
78-
}
72+
return standard_failure_handler_for(name(), cerr, status);
73+
};
7974

80-
return ret;
75+
auto streaming_callback = [&spinner](mp::MountReply& reply) {
76+
spinner.stop();
77+
spinner.start(reply.mount_message());
8178
};
8279

8380
request.set_verbosity_level(parser->verbosityLevel());
84-
return dispatch(&RpcMethod::mount, request, on_success, on_failure);
81+
82+
return dispatch(&RpcMethod::mount, request, on_success, on_failure, streaming_callback);
8583
}
8684

8785
std::string cmd::Mount::name() const { return "mount"; }

src/client/cmd/start.cpp

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2017-2018 Canonical, Ltd.
2+
* Copyright (C) 2017-2019 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
@@ -37,47 +37,39 @@ mp::ReturnCode cmd::Start::run(mp::ArgParser* parser)
3737
return parser->returnCodeFrom(ret);
3838
}
3939

40-
auto on_success = [](mp::StartReply& reply) {
40+
AnimatedSpinner spinner{cout};
41+
42+
auto on_success = [&spinner](mp::StartReply& reply) {
43+
spinner.stop();
4144
return ReturnCode::Ok;
4245
};
4346

44-
AnimatedSpinner spinner{cout};
45-
auto on_failure = [this, &spinner, &parser](grpc::Status& status) {
47+
auto on_failure = [this, &spinner](grpc::Status& status) {
4648
spinner.stop();
4749
auto ret = standard_failure_handler_for(name(), cerr, status);
48-
if (!status.error_details().empty())
50+
if (status.error_code() == grpc::StatusCode::ABORTED && !status.error_details().empty())
4951
{
50-
if (status.error_code() == grpc::StatusCode::ABORTED)
51-
{
52-
mp::StartError start_error;
53-
start_error.ParseFromString(status.error_details());
54-
55-
if (start_error.error_code() == mp::StartError::INSTANCE_DELETED)
56-
{
57-
fmt::print(cerr,
58-
"Use 'recover' to recover the deleted instance or 'purge' to permanently delete the "
59-
"instance.\n");
60-
}
61-
}
62-
else
52+
mp::StartError start_error;
53+
start_error.ParseFromString(status.error_details());
54+
55+
if (start_error.error_code() == mp::StartError::INSTANCE_DELETED)
6356
{
64-
mp::MountError mount_error;
65-
mount_error.ParseFromString(status.error_details());
66-
67-
if (mount_error.error_code() == mp::MountError::SSHFS_MISSING)
68-
{
69-
cmd::install_sshfs_for(mount_error.instance_name(), parser->verbosityLevel(), rpc_channel, stub,
70-
cout, cerr);
71-
}
57+
fmt::print(cerr, "Use 'recover' to recover the deleted instance or 'purge' to permanently delete the "
58+
"instance.\n");
7259
}
7360
}
7461

7562
return ret;
7663
};
7764

65+
auto streaming_callback = [&spinner](mp::StartReply& reply) {
66+
spinner.stop();
67+
spinner.start(reply.start_message());
68+
};
69+
7870
spinner.start(instance_action_message_for(request.instance_names(), "Starting "));
7971
request.set_verbosity_level(parser->verbosityLevel());
80-
return dispatch(&RpcMethod::start, request, on_success, on_failure);
72+
return dispatch(&RpcMethod::start, request, on_success, on_failure, streaming_callback);
8173
}
8274

8375
std::string cmd::Start::name() const { return "start"; }

src/daemon/base_cloud_init_config.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2017 Canonical, Ltd.
2+
* Copyright (C) 2017-2019 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
@@ -13,8 +13,6 @@
1313
* You should have received a copy of the GNU General Public License
1414
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1515
*
16-
* Authored by: Christopher James Halse Rogers <[email protected]>
17-
*
1816
*/
1917
#ifndef MULTIPASS_BASE_CLOUD_INIT_CONFIG_H
2018
#define MULTIPASS_BASE_CLOUD_INIT_CONFIG_H
@@ -27,9 +25,7 @@ constexpr auto base_cloud_init_config = "growpart:\n"
2725
" ignore_growroot_disabled: false\n"
2826
"users:\n"
2927
" - default\n"
30-
"manage_etc_hosts: true\n"
31-
"packages:\n"
32-
" - sshfs\n";
28+
"manage_etc_hosts: true\n";
3329
}
3430

3531
#endif // MULTIPASS_BASE_CLOUD_INIT_CONFIG_H

src/daemon/daemon.cpp

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ constexpr auto metrics_opt_in_file = "multipassd-send-metrics.yaml";
6868
constexpr auto reboot_cmd = "sudo reboot";
6969
constexpr auto up_timeout = 2min; // This may be tweaked as appropriate and used in places that wait for ssh to be up
7070
constexpr auto stop_ssh_cmd = "sudo systemctl stop ssh";
71+
constexpr auto max_install_sshfs_retries = 3;
7172

7273
mp::Query query_from(const mp::LaunchRequest* request, const std::string& name)
7374
{
@@ -144,10 +145,6 @@ void prepare_user_data(YAML::Node& user_data_config, YAML::Node& vendor_config)
144145
if (users.IsSequence())
145146
users.push_back("default");
146147

147-
auto packages = user_data_config["packages"];
148-
if (packages.IsSequence())
149-
packages.push_back("sshfs");
150-
151148
auto keys = user_data_config["ssh_authorized_keys"];
152149
if (keys.IsSequence())
153150
keys.push_back(vendor_config["ssh_authorized_keys"][0]);
@@ -304,11 +301,8 @@ auto validate_create_arguments(const mp::LaunchRequest* request)
304301

305302
auto grpc_status_for_mount_error(const std::string& instance_name)
306303
{
307-
mp::MountError mount_error;
308-
mount_error.set_error_code(mp::MountError::SSHFS_MISSING);
309-
mount_error.set_instance_name(instance_name);
310-
311-
return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION, "Mount failed", mount_error.SerializeAsString());
304+
return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION,
305+
fmt::format("Error enabling mount support in '{}'", instance_name));
312306
}
313307

314308
auto grpc_status_for(fmt::memory_buffer& errors)
@@ -757,10 +751,6 @@ try // clang-format on
757751
vm->start();
758752
vm->wait_until_ssh_up(std::chrono::minutes(5));
759753

760-
reply.set_create_message("Waiting for initialization to complete");
761-
server->Write(reply);
762-
vm->wait_for_cloud_init(std::chrono::minutes(5));
763-
764754
reply.set_vm_instance_name(name);
765755
server->Write(reply);
766756

@@ -1265,7 +1255,18 @@ try // clang-format on
12651255
}
12661256
catch (const mp::SSHFSMissingError&)
12671257
{
1268-
return grpc_status_for_mount_error(name);
1258+
try
1259+
{
1260+
MountReply mount_reply;
1261+
mount_reply.set_mount_message("Enabling support for mounting");
1262+
server->Write(mount_reply);
1263+
install_sshfs(vm, name);
1264+
start_mount(vm, name, request->source_path(), target_path, gid_map, uid_map);
1265+
}
1266+
catch (const mp::SSHFSMissingError&)
1267+
{
1268+
return grpc_status_for_mount_error(name);
1269+
}
12691270
}
12701271
catch (const std::exception& e)
12711272
{
@@ -1474,7 +1475,18 @@ try // clang-format on
14741475
}
14751476
catch (const mp::SSHFSMissingError&)
14761477
{
1477-
return grpc_status_for_mount_error(name);
1478+
try
1479+
{
1480+
StartReply start_reply;
1481+
start_reply.set_start_message("Enabling support for mounting");
1482+
server->Write(start_reply);
1483+
install_sshfs(vm, name);
1484+
start_mount(vm, name, source_path, target_path, gid_map, uid_map);
1485+
}
1486+
catch (const mp::SSHFSMissingError&)
1487+
{
1488+
return grpc_status_for_mount_error(name);
1489+
}
14781490
}
14791491
catch (const std::exception& e)
14801492
{
@@ -2038,3 +2050,38 @@ grpc::Status mp::Daemon::cmd_vms(const std::vector<std::string>& tgts, std::func
20382050

20392051
return grpc::Status::OK;
20402052
}
2053+
2054+
void mp::Daemon::install_sshfs(const VirtualMachine::UPtr& vm, const std::string& name)
2055+
{
2056+
auto& key_provider = *config->ssh_key_provider;
2057+
2058+
SSHSession session{vm->ssh_hostname(), vm->ssh_port(), vm->ssh_username(), key_provider};
2059+
2060+
mpl::log(mpl::Level::info, category, fmt::format("Installing sshfs in \'{}\'", name));
2061+
2062+
int retries{0};
2063+
while (++retries <= max_install_sshfs_retries)
2064+
{
2065+
try
2066+
{
2067+
auto proc = session.exec("sudo apt update && sudo apt install -y sshfs");
2068+
if (proc.exit_code(std::chrono::minutes(5)) != 0)
2069+
{
2070+
auto error_msg = proc.read_std_error();
2071+
mpl::log(mpl::Level::warning, category,
2072+
fmt::format("Failed to install 'sshfs', error message: '{}'", mp::utils::trim_end(error_msg)));
2073+
}
2074+
else
2075+
{
2076+
break;
2077+
}
2078+
}
2079+
catch (const mp::ExitlessSSHProcessException&)
2080+
{
2081+
mpl::log(mpl::Level::info, category, fmt::format("Timeout while installing 'sshfs' in '{}'", name));
2082+
}
2083+
}
2084+
2085+
if (retries > max_install_sshfs_retries)
2086+
throw mp::SSHFSMissingError();
2087+
}

src/daemon/daemon.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ public slots:
139139
grpc::Status shutdown_vm(VirtualMachine& vm, const std::chrono::milliseconds delay);
140140
grpc::Status cancel_vm_shutdown(const VirtualMachine& vm);
141141
grpc::Status cmd_vms(const std::vector<std::string>& tgts, std::function<grpc::Status(VirtualMachine&)> cmd);
142+
void install_sshfs(const VirtualMachine::UPtr& vm, const std::string& name);
142143

143144
std::unique_ptr<const DaemonConfig> config;
144145
std::unordered_map<std::string, VMSpecs> vm_instance_specs;

src/platform/backends/libvirt/libvirt_virtual_machine.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -384,11 +384,6 @@ void mp::LibVirtVirtualMachine::wait_until_ssh_up(std::chrono::milliseconds time
384384
mp::utils::wait_until_ssh_up(this, timeout);
385385
}
386386

387-
void mp::LibVirtVirtualMachine::wait_for_cloud_init(std::chrono::milliseconds timeout)
388-
{
389-
mp::utils::wait_for_cloud_init(this, timeout);
390-
}
391-
392387
void mp::LibVirtVirtualMachine::update_state()
393388
{
394389
monitor->persist_state_for(vm_name);

0 commit comments

Comments
 (0)