Skip to content

Commit

Permalink
mount: Move "just in time" sshfs logic to the daemon
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Townsend committed Feb 20, 2019
1 parent cd621b9 commit f494c36
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 76 deletions.
23 changes: 1 addition & 22 deletions src/client/cmd/common_cli.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Canonical, Ltd.
* Copyright (C) 2018-2019 Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -102,24 +102,3 @@ mp::ReturnCode cmd::standard_failure_handler_for(const std::string& command, std

return return_code_for(status.error_code());
}

void cmd::install_sshfs_for(const std::string& instance_name, int verbosity_level, grpc::Channel* rpc_channel,
mp::Rpc::Stub* stub, std::ostream& cout, std::ostream& cerr)
{
std::vector<Command::UPtr> command;
command.push_back(std::make_unique<Exec>(*rpc_channel, *stub, cout, cerr));

auto args = QStringList() << "" // This is just a dummy string for the unnecessary binary name
<< "exec" << QString::fromStdString(instance_name) << "--"
<< "sudo"
<< "bash"
<< "-c"
<< "apt update && apt install -y sshfs";
ArgParser exec_parser{args, command, cout, cerr};
exec_parser.setVerbosityLevel(verbosity_level);
exec_parser.parse();

fmt::print(cerr, "The sshfs package is missing in \"{}\". Installing...\n", instance_name);
if (exec_parser.chosenCommand()->run(&exec_parser) == mp::ReturnCode::Ok)
fmt::print(cerr, "\n***Please re-run the mount command.\n");
}
4 changes: 1 addition & 3 deletions src/client/cmd/common_cli.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 Canonical, Ltd.
* Copyright (C) 2018-2019 Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -41,8 +41,6 @@ ParseCode handle_format_option(ArgParser* parser, Formatter** chosen_formatter,
std::string instance_action_message_for(const InstanceNames& instance_names, const std::string& action_name);
ReturnCode standard_failure_handler_for(const std::string& command, std::ostream& cerr, const grpc::Status& status,
const std::string& error_details = std::string());
void install_sshfs_for(const std::string& instance_name, int verbosity_level, grpc::Channel* rpc_channel,
Rpc::Stub* stub, std::ostream& cout, std::ostream& cerr);
} // namespace cmd
} // namespace multipass

Expand Down
31 changes: 13 additions & 18 deletions src/client/cmd/mount.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2017-2018 Canonical, Ltd.
* Copyright (C) 2017-2019 Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -17,8 +17,8 @@

#include "mount.h"
#include "common_cli.h"
#include "exec.h"

#include "animated_spinner.h"
#include <multipass/cli/argparser.h>
#include <multipass/cli/client_platform.h>
#include <multipass/logging/log.h>
Expand Down Expand Up @@ -59,29 +59,24 @@ mp::ReturnCode cmd::Mount::run(mp::ArgParser* parser)
return parser->returnCodeFrom(ret);
}

auto on_success = [](mp::MountReply& reply) {
mp::AnimatedSpinner spinner{cout};

auto on_success = [&spinner](mp::MountReply& reply) {
spinner.stop();
return ReturnCode::Ok;
};

auto on_failure = [this, &parser](grpc::Status& status) {
auto ret = standard_failure_handler_for(name(), cerr, status);
if (!status.error_details().empty())
{
mp::MountError mount_error;
mount_error.ParseFromString(status.error_details());

if (mount_error.error_code() == mp::MountError::SSHFS_MISSING)
{
cmd::install_sshfs_for(mount_error.instance_name(), parser->verbosityLevel(), rpc_channel, stub, cout,
cerr);
}
}
auto on_failure = [this, &spinner](grpc::Status& status) {
spinner.stop();

return ret;
return standard_failure_handler_for(name(), cerr, status);
};

auto streaming_callback = [&spinner](mp::MountReply& reply) { spinner.start(reply.mount_message()); };

request.set_verbosity_level(parser->verbosityLevel());
return dispatch(&RpcMethod::mount, request, on_success, on_failure);

return dispatch(&RpcMethod::mount, request, on_success, on_failure, streaming_callback);
}

std::string cmd::Mount::name() const { return "mount"; }
Expand Down
26 changes: 11 additions & 15 deletions src/client/cmd/start.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2017-2018 Canonical, Ltd.
* Copyright (C) 2017-2019 Canonical, Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -37,11 +37,13 @@ mp::ReturnCode cmd::Start::run(mp::ArgParser* parser)
return parser->returnCodeFrom(ret);
}

auto on_success = [](mp::StartReply& reply) {
AnimatedSpinner spinner{cout};

auto on_success = [&spinner](mp::StartReply& reply) {
spinner.stop();
return ReturnCode::Ok;
};

AnimatedSpinner spinner{cout};
auto on_failure = [this, &spinner, &parser](grpc::Status& status) {
spinner.stop();
auto ret = standard_failure_handler_for(name(), cerr, status);
Expand All @@ -59,25 +61,19 @@ mp::ReturnCode cmd::Start::run(mp::ArgParser* parser)
"instance.\n");
}
}
else
{
mp::MountError mount_error;
mount_error.ParseFromString(status.error_details());

if (mount_error.error_code() == mp::MountError::SSHFS_MISSING)
{
cmd::install_sshfs_for(mount_error.instance_name(), parser->verbosityLevel(), rpc_channel, stub,
cout, cerr);
}
}
}

return ret;
};

auto streaming_callback = [&spinner](mp::StartReply& reply) {
spinner.stop();
spinner.start(reply.start_message());
};

spinner.start(instance_action_message_for(request.instance_names(), "Starting "));
request.set_verbosity_level(parser->verbosityLevel());
return dispatch(&RpcMethod::start, request, on_success, on_failure);
return dispatch(&RpcMethod::start, request, on_success, on_failure, streaming_callback);
}

std::string cmd::Start::name() const { return "start"; }
Expand Down
68 changes: 61 additions & 7 deletions src/daemon/daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,11 +299,8 @@ auto validate_create_arguments(const mp::LaunchRequest* request)

auto grpc_status_for_mount_error(const std::string& instance_name)
{
mp::MountError mount_error;
mount_error.set_error_code(mp::MountError::SSHFS_MISSING);
mount_error.set_instance_name(instance_name);

return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION, "Mount failed", mount_error.SerializeAsString());
return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION,
fmt::format("Error enabling mount support in '{}'", instance_name));
}

auto grpc_status_for(fmt::memory_buffer& errors)
Expand Down Expand Up @@ -1260,7 +1257,18 @@ try // clang-format on
}
catch (const mp::SSHFSMissingError&)
{
return grpc_status_for_mount_error(name);
try
{
MountReply mount_reply;
mount_reply.set_mount_message("Enabling support for mounting");
server->Write(mount_reply);
install_sshfs(vm, name);
start_mount(vm, name, request->source_path(), target_path, gid_map, uid_map);
}
catch (const mp::SSHFSMissingError&)
{
return grpc_status_for_mount_error(name);
}
}
catch (const std::exception& e)
{
Expand Down Expand Up @@ -1470,7 +1478,18 @@ try // clang-format on
}
catch (const mp::SSHFSMissingError&)
{
return grpc_status_for_mount_error(name);
try
{
StartReply start_reply;
start_reply.set_start_message("Enabling support for mounting");
server->Write(start_reply);
install_sshfs(vm, name);
start_mount(vm, name, source_path, target_path, gid_map, uid_map);
}
catch (const mp::SSHFSMissingError&)
{
return grpc_status_for_mount_error(name);
}
}
catch (const std::exception& e)
{
Expand Down Expand Up @@ -2034,3 +2053,38 @@ grpc::Status mp::Daemon::cmd_vms(const std::vector<std::string>& tgts, std::func

return grpc::Status::OK;
}

void mp::Daemon::install_sshfs(const VirtualMachine::UPtr& vm, const std::string& name)
{
auto& key_provider = *config->ssh_key_provider;

SSHSession session{vm->ssh_hostname(), vm->ssh_port(), vm->ssh_username(), key_provider};

mpl::log(mpl::Level::info, category, fmt::format("Installing sshfs in \'{}\'", name));

int retries{0};
while (++retries <= 3)
{
try
{
auto proc = session.exec("sudo apt update && sudo apt install -y sshfs");
if (proc.exit_code(std::chrono::minutes(5)) != 0)
{
auto error_msg = proc.read_std_error();
mpl::log(mpl::Level::warning, category,
fmt::format("Failed to install 'sshfs', error message: '{}'", mp::utils::trim_end(error_msg)));
}
else
{
break;
}
}
catch (const mp::ExitlessSSHProcessException&)
{
mpl::log(mpl::Level::info, category, fmt::format("Timeout while installing 'sshfs' in '{}'", name));
}
}

if (retries > 3)
throw mp::SSHFSMissingError();
}
1 change: 1 addition & 0 deletions src/daemon/daemon.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ public slots:
grpc::Status shutdown_vm(VirtualMachine& vm, const std::chrono::milliseconds delay);
grpc::Status cancel_vm_shutdown(const VirtualMachine& vm);
grpc::Status cmd_vms(const std::vector<std::string>& tgts, std::function<grpc::Status(VirtualMachine&)> cmd);
void install_sshfs(const VirtualMachine::UPtr& vm, const std::string& name);

std::unique_ptr<const DaemonConfig> config;
std::unordered_map<std::string, VMSpecs> vm_instance_specs;
Expand Down
14 changes: 3 additions & 11 deletions src/rpc/multipass.proto
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2017-2018 Canonical Ltd.
// Copyright © 2017-2019 Canonical Ltd.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 3 as
Expand Down Expand Up @@ -217,16 +217,6 @@ message TargetPathInfo {
string target_path = 2;
}

message MountError {
enum ErrorCode {
OK = 0;
SSHFS_MISSING = 1;
OTHER = 2;
}
string instance_name = 1;
ErrorCode error_code = 2;
}

message MountRequest {
string source_path = 1;
repeated TargetPathInfo target_paths = 2;
Expand All @@ -236,6 +226,7 @@ message MountRequest {

message MountReply {
string log_line = 1;
string mount_message = 2;
}

message PingRequest {
Expand Down Expand Up @@ -288,6 +279,7 @@ message StartRequest {

message StartReply {
string log_line = 1;
string start_message = 2;
}

message StopRequest {
Expand Down

0 comments on commit f494c36

Please sign in to comment.