8000 Settings daemon driver - take 2 by ricab · Pull Request #835 · canonical/multipass · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Settings daemon driver - take 2 #835

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

Merged
merged 48 commits into from
Jun 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
655b023
[settings] Add driver setting
ricab May 29, 2019
d74cd30
[daemon] Use driver from settings
ricab May 29, 2019
7c6f150
[settings] Use separate files for client/daemon
ricab May 31, 2019
04a5682
[settings] Use single directory for files
ricab May 31, 2019
aa21460
[settings] Employ QString/fmtlib integration
ricab Jun 3, 2019
71dc714
[test] Linux-platform test for default driver
ricab Jun 4, 2019
2cd1f97
[test] Linux-platform test explicit qemu driver
ricab Jun 4, 2019
7cd3178
[test] Refactor linux-platform tests
ricab Jun 4, 2019
57f3c3c
[test] Linux-platform test for libvirt driver
ricab Jun 4, 2019
7d11752
[test] Test hyperkit driver unsupported on Linux
ricab Jun 4, 2019
2130d47
[test] Add parameterized unsupported driver tests
ricab Jun 4, 2019
db65b04
[test] Extend get/set tests for local.driver
ricab Jun 4, 2019
dd7a295
[daemon] Monitor settings file and quit on change
ricab Jun 5, 2019
e3cabab
[daemon] Create settings file if absent
ricab Jun 9, 2019
99d9a53
[settings] Use root file for daemon settings
ricab Jun 11, 2019
22cb7dd
[settings] Fix permissions being silently ignored
ricab Jun 11, 2019
ddbb7b6
[settings] Improve error message
ricab Jun 11, 2019
ae5ad5a
[settings] Use nicer directory for daemon settings
ricab Jun 11, 2019
f94047b
[test] Mock libvirt in platform test
ricab Jun 12, 2019
cae2185
[test] Extract fake_handle utility
ricab Jun 12, 2019
7781a22
[test] Test that driver env var precedes settings
ricab Jun 13, 2019
d5995a8
[daemon] Implement driver env var precedence
ricab Jun 13, 2019
b59597f
[daemon] Drop message concatenation
ricab Jun 13, 2019
87e591e
[settings] Use QDir to construct paths
ricab Jun 13, 2019
f1e8859
[snap] Configure XDG paths
ricab Jun 18, 2019
f9b0008
[snap] Create XDG directories upon launching
ricab Jun 18, 2019
d1dd546
[settings] Rename helper function
ricab Jun 18, 2019
e441578
[snap] Make daemon config dir available in client
ricab Jun 18, 2019
adb9e10
[settings] Use platform-dependent config path
ricab Jun 18, 2019
921bbb1
[daemon] Exit with EXIT_FAILURE on settings change
ricab Jun 18, 2019
bda6d55
[settings] Update default dir of daemon config
ricab Jun 19, 2019
b76ad25
[settings] Fix silent ignore of permission denied
ricab Jun 19, 2019
39f4402
[daemon] Update exit code on settings change (42)
ricab Jun 19, 2019
5988e92
[settings] Use QDir for file path
ricab Jun 19, 2019
053c155
[daemon] Drop split driver treatment in error msg
ricab Jun 19, 2019
0f6471e
[settings] Improve error messages
ricab Jun 19, 2019
80e629d
[test] Test that unsupported drivers are rejected
ricab Jun 21, 2019
7330309
[settings] Implement synchronous driver rejection
ricab Jun 21, 2019
b9617b5
[test] Small test refactoring
ricab Jun 21, 2019
f5fb45b
[test] Change `set` cli tests to `key=val` format
ricab Jun 21, 2019
9268774
[test] Test `set` rejects bad key-val format
ricab Jun 21, 2019
1a122b8
[cli] Implement <key>=<val> format handling in set
ricab Jun 21, 2019
c65a269
[test] Extract parameterized tests to own class
ricab Jun 25, 2019
b240fa0
[test] Test wrong arg num in `get`/`set` cmds
ricab Jun 25, 2019
dab7ddd
[cli] Fix argument checking in `set` cmd
ricab Jun 25, 2019
e8e3605
[settings] Improve error message
ricab Jun 25, 2019
62166a3
[test] Fix number of arguments in mock expectation
ricab Jun 25, 2019
e1dd710
Merge branch 'master' into settings-daemon-driver-take2
ricab Jun 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/multipass/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ constexpr auto daemon_name = "multipassd";
constexpr auto min_memory_size = "128M";
constexpr auto min_disk_size = "512M";
constexpr auto petenv_key = "client.primary_name"; // This will eventually be moved to some dynamic settings schema
constexpr auto driver_key = "local.driver"; // idem
constexpr auto driver_env_var = "MULTIPASS_VM_DRIVER";
} // namespace multipass

#endif // MULTIPASS_CONSTANTS_H
4 changes: 2 additions & 2 deletions include/multipass/exceptions/settings_exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class SettingsException : public std::runtime_error
class PersistentSettingsException : public SettingsException
{
public:
PersistentSettingsException(const QString& attempted_operation, const QString& failure)
: SettingsException{fmt::format("Could not {} settings: {} error", attempted_operation, failure)}
PersistentSettingsException(const QString& attempted_operation, const QString& detail)
: SettingsException{fmt::format("Unable to {} settings: {}", attempted_operation, detail)}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I won't block on this, but we'll have to move away from constructing the error message like this later.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what the issue is. Note that attempted_operation could be an enum here, representing read and write ops, that we would then turn into string. Then detail is a separate phrase. How could I make this friendlier to translation? Perhaps an example of what problems this could cause would help me understand better.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically what I mean is that "Unable to format settings" may translate to something completely different than "Unable to access settings" depending on language.

Different languages work in very different ways, building a sentence from a set of strings is bound to break at one point or another. It's a discussion to have over beers, though :)

I'm just sensitive to this because I've translated a lot of software over the years and Polish is one of the more complex languages out there so it easily highlights problems in approaches like that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I understand the idea. To avoid duplication, I think I would then try a more "square" way, e.g. "Unable to operate on settings. Operation: {}; Details: {}". But this might not feel so good for the user... Isn't this the sort of thing that one would do when preparing the code for translation? Preemptively duplicating messages in the code does not feel right to me either.

BTW, the operation here is just "read" or "read/write", "format/access" goes in the second param as a free string.

{
}
};
Expand Down
5 changes: 5 additions & 0 deletions include/multipass/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@

#include <libssh/sftp.h>

#include <QString>

#include <string>

namespace multipass
{
namespace platform
{
std::string default_server_address();
QString default_driver();
QString daemon_config_home(); // temporary
bool is_backend_supported(const QString& backend); // temporary
VirtualMachineFactory::UPtr vm_backend(const Path& data_dir);
logging::Logger::UPtr make_logger(logging::Level level);
UpdatePrompt::UPtr make_update_prompt();
Expand Down
2 changes: 2 additions & 0 deletions include/multipass/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class Settings : public Singleton<Settings>
virtual QString get(const QString& key) const; // throws on unknown key
virtual void set(const QString& key, const QString& val); // throws on unknown key or bad settings

static QString get_daemon_settings_file_path(); // temporary

protected:
const QString& get_default(const QString& key) const; // throws on unknown key

Expand Down
Empty file.
6 changes: 6 additions & 0 deletions snap-wrappers/bin/launch-multipass
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh -e

. launcher-common.sh
mk_xdg_dirs

exec "$SNAP/bin/multipass" "$@"
6 changes: 6 additions & 0 deletions snap-wrappers/bin/launch-multipass-gui
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh -e

. launcher-common.sh
mk_xdg_dirs

exec "$SNAP/bin/multipass-gui" "$@"
3 changes: 3 additions & 0 deletions snap-wrappers/bin/launch-multipassd
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#!/bin/sh -e

. launcher-common.sh
mk_xdg_dirs

http_proxy="$(snapctl get proxy.http)"
https_proxy="$(snapctl get proxy.https)"
MULTIPASS_VM_DRIVER="$(snapctl get driver)"
Expand Down
10 changes: 10 additions & 0 deletions snap-wrappers/bin/launcher-common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/sh -e

mk_xdg_dirs()
{ # need quotes to handle spaces, but then better test for emptiness
for dir in "$XDG_CACHE_HOME" "$XDG_CONFIG_HOME" "$XDG_DATA_HOME"; do
if [ ! -z "$dir" ]; then
mkdir -p "$dir"
fi
done
}
26 changes: 16 additions & 10 deletions snap/snapcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,31 @@ apps:
multipassd:
command: bin/launch-multipassd
environment:
LD_LIBRARY_PATH: $SNAP/lib:$SNAP/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/usr/lib:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET
PATH: $SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH
LD_LIBRARY_PATH: &library-path |
$SNAP/lib:$SNAP/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/usr/lib:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET
PATH: &path |
$SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH
QT_PLUGIN_PATH: $SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/plugins
XDG_DATA_HOME: $SNAP_COMMON/data
XDG_CACHE_HOME: $SNAP_COMMON/cache
XDG_CONFIG_HOME: &daemon-config $SNAP_DATA/config
DAEMON_CONFIG_HOME: *daemon-config # temporary
daemon: simple
plugs: [libvirt]
multipass:
environment:
LD_LIBRARY_PATH: $SNAP/lib:$SNAP/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/usr/lib:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET
PATH: $SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH
QT_PLUGIN_PATH: $SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/plugins
command: bin/multipass
<<: &client-environment
LD_LIBRARY_PATH: *library-path
PATH: *path
QT_PLUGIN_PATH: $SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/plugins
XDG_DATA_HOME: $SNAP_USER_DATA/data
XDG_CACHE_HOME: $SNAP_USER_DATA/cache
XDG_CONFIG_HOME: $SNAP_USER_DATA/config
DAEMON_CONFIG_HOME: *daemon-config # temporary
command: bin/launch-multipass
completer: etc/bash_completion.d/snap.multipass
gui:
environment:
LD_LIBRARY_PATH: $SNAP/lib:$SNAP/lib/$SNAPCRAFT_ARCH_TRIPLET:$SNAP/usr/lib:$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET
PATH: $SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH
QT_PLUGIN_PATH: $SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/qt5/plugins
environment: *client-environment
command: bin/multipass-gui

parts:
Expand Down
25 changes: 18 additions & 7 deletions src/client/cli/cmd/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,33 @@ QString cmd::Set::description() const

mp::ParseCode cmd::Set::parse_args(mp::ArgParser* parser)
{
parser->addPositionalArgument("key", "Path to the option to configure.", "<key>");
parser->addPositionalArgument("value", "Value to set the option to.", "<value>");
parser->addPositionalArgument(
"keyval",
"A key-value pair. The key specifies a path to the option to configure. The value is its intended value.",
"<key>=<value>");

auto status = parser->commandParse(this);
if (status == ParseCode::Ok)
{
const auto args = parser->positionalArguments();
if (args.count() == 2)
if (args.size() != 1)
{
key = args.at(0);
val = args.at(1);
cerr << "Need exactly one key-value pair.\n";
status = ParseCode::CommandLineError;
}
else
{
cerr << "Need exactly one option key and one value\n";
status = ParseCode::CommandLineError;
const auto keyval = args.at(0).split('=');
if (keyval.size() != 2 || keyval.contains(QStringLiteral("")))
{
cerr << "Bad key-value format.\n";
status = ParseCode::CommandLineError;
}
else
{
key = keyval.at(0);
val = keyval.at(1);
}
}
}

Expand Down
25 changes: 23 additions & 2 deletions src/daemon/daemon_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <multipass/name_generator.h>
#include <multipass/platform.h>
#include <multipass/platform_unix.h>
#include <multipass/settings.h>
#include <multipass/utils.h>
#include <multipass/version.h>
#include <multipass/virtual_machine_factory.h>
Expand All @@ -35,6 +36,8 @@
#include <multipass/format.h>

#include <QCoreApplication>
#include <QFileSystemWatcher>
#include <QObject>

#include <csignal>
#include <cstring>
Expand All @@ -48,6 +51,7 @@ namespace mpp = multipass::platform;

namespace
{
constexpr const int settings_changed_code = 42;
const std::vector<std::string> supported_socket_groups{"sudo", "adm", "admin"};

void set_server_permissions(const std::string& server_address)
Expand Down Expand Up @@ -76,6 +80,12 @@ void set_server_permissions(const std::string& server_address)
throw std::runtime_error("Could not set permissions for the multipass socket.");
}

void init_settings(const QString& filename)
{
QFile file{filename};
file.open(QIODevice::WriteOnly | QIODevice::Append);
}

class UnixSignalHandler
{
public:
Expand All @@ -102,6 +112,16 @@ class UnixSignalHandler
private:
mp::AutoJoinThread signal_handling_thread;
};

void monitor_and_quit_on_settings_change()
{
static const auto filename = mp::Settings::get_daemon_settings_file_path();
init_settings(filename); // create if not there

static QFileSystemWatcher monitor{{filename}};
QObject::connect(&monitor, &QFileSystemWatcher::fileChanged, [] { QCoreApplication::exit(settings_changed_code); });
}

} // namespace

int main(int argc, char* argv[]) // clang-format off
Expand All @@ -117,14 +137,15 @@ try // clang-format on
auto config = builder.build();
auto server_address = config->server_address;

monitor_and_quit_on_settings_change();
mp::Daemon daemon(std::move(config));

set_server_permissions(server_address);

QCoreApplication::exec();
auto ret = QCoreApplication::exec();

mpl::log(mpl::Level::info, "daemon", "Goodbye!");
return EXIT_SUCCESS;
return ret;
}
catch (const std::exception& e)
{
Expand Down
52 changes: 42 additions & 10 deletions src/platform/platform_linux.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 @@ -13,36 +13,68 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by: Alberto Aguirre <alberto.aguirre@canonical.com>
*
*/

#include <multipass/constants.h>
#include <multipass/format.h>
#include <multipass/platform.h>

#include <multipass/settings.h>
#include <multipass/virtual_machine_factory.h>

#include "backends/libvirt/libvirt_virtual_machine_factory.h"
#include "backends/qemu/qemu_virtual_machine_factory.h"
#include "logger/journald_logger.h"
#include <disabled_update_prompt.h>

#include <QtGlobal>

namespace mp = multipass;

namespace
{

QString get_driver_str()
{
auto driver = qgetenv(mp::driver_env_var);
return driver.isEmpty() ? mp::Settings::instance().get(mp::driver_key) : driver.toLower();
}

} // namespace

std::string mp::platform::default_server_address()
{
return {"unix:/run/multipass_socket"};
}

mp::VirtualMachineFactory::UPtr mp::platform::vm_backend(const mp::Path& data_dir)
QString mp::platform::default_driver()
{
auto driver = qgetenv("MULTIPASS_VM_DRIVER");
return QStringLiteral("qemu");
}

if (driver.isEmpty() || driver == "QEMU")
QString mp::platform::daemon_config_home() // temporary
{
auto ret = QString{qgetenv("DAEMON_CONFIG_HOME")};
if (ret.isEmpty())
ret = QStringLiteral("/root/.config");

ret = QDir{ret}.absoluteFilePath(mp::daemon_name);
return ret;
}

bool mp::platform::is_backend_supported(const QString& backend)
{
return backend == "qemu" || backend == "libvirt";
}

mp::VirtualMachineFactory::UPtr mp::platform::vm_backend(const mp::Path& data_dir)
{
const auto& driver = get_driver_str();
if (driver == QStringLiteral("qemu"))
return std::make_unique<QemuVirtualMachineFactory>(data_dir);
else if (driver == "LIBVIRT")
else if (driver == QStringLiteral("libvirt"))
return std::make_unique<LibVirtVirtualMachineFactory>(data_dir);

throw std::runtime_error("Invalid virtualization driver set in the environment");
else
throw std::runtime_error(fmt::format("Unsupported virtualization driver: {}", driver));
}

mp::UpdatePrompt::UPtr mp::platform::make_update_prompt()
Expand Down
Loading
0