-
Notifications
You must be signed in to change notification settings - Fork 623
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
Support LXD as a backend driver #1533
Conversation
This comment has been minimized.
This comment has been minimized.
|
@Saviq, yep, saw that yesterday and will fix today along with some other things 😀 |
06dd5bb
to
8ae5c74
Compare
This comment has been minimized.
This comment has been minimized.
Codecov Report
@@ Coverage Diff @@
## master #1533 +/- ##
==========================================
- Coverage 74.47% 73.99% -0.48%
==========================================
Files 215 222 +7
Lines 7785 8138 +353
==========================================
+ Hits 5798 6022 +224
- Misses 1987 2116 +129
Continue to review full report at Codecov.
|
44bb6dd
to
e2ab23e
Compare
This comment has been minimized.
This comment has been minimized.
e2ab23e
to
c217ea0
Compare
This comment has been minimized.
This comment has been minimized.
c217ea0
to
9f25d64
Compare
This comment has been minimized.
This comment has been minimized.
9f25d64
to
88f6e9d
Compare
This comment has been minimized.
This comment has been minimized.
88f6e9d
to
81b3a82
Compare
This comment has been minimized.
This comment has been minimized.
81b3a82
to
dc742c9
Compare
This comment has been minimized.
This comment has been minimized.
dc742c9
to
c86c490
Compare
This comment has been minimized.
This comment has been minimized.
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.
Need a lxd
plug, don't we?
{}, | ||
{}, | ||
{}}; |
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.
I wonder if we should store and populate this…
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.
Maybe, but as you just mentioned, perhaps the image vault should configure it? I'll need to think about it some.
if (json_reply["metadata"].toObject()["class"] == QStringLiteral("task") && | ||
json_reply["status_code"].toInt(-1) == 100) | ||
{ | ||
QUrl task_url(QString("%1/operations/%2") | ||
.arg(base_url.toString()) | ||
.arg(json_reply["metadata"].toObject()["id"].toString())); | ||
|
||
// Instead of polling, need to use websockets to get events | ||
while (true) | ||
{ | ||
try | ||
{ | ||
auto task_reply = lxd_request(manager, "GET", task_url); | ||
|
||
if (task_reply["error_code"].toInt(-1) != 0) | ||
{ | ||
break; | ||
} | ||
|
||
auto status_code = task_reply["metadata"].toObject()["status_code"].toInt(-1); | ||
if (status_code == 200) | ||
{ | ||
break; | ||
} | ||
else | ||
{ | ||
std::this_thread::sleep_for(5s); | ||
} | ||
} | ||
// Implies the task is finished | ||
catch (const LXDNotFoundException& e) | ||
{ | ||
break; | ||
} | ||
} | ||
} |
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.
Should this go into lxd_request?
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.
I kind of think the caller of lxd_request()
should determine how it waits on an operation to finish. Some callers may use the operations/blah/wait
method while others may use a polling (and later websockets) method.
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.
Sure, it would have to be the caller's decision whether to let lxd_request()
handle the async operation or not. I'm kinda thinking ::waitForFinished()
style. Except it's not an object, I know ;)
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.
Well, still, the caller should determine how it waits on an operation to complete. I suppose we could have a default wait logic in lxd_request
and if the caller doesn't want or need to use that, then it can pass in 0
for the timeout and lxd_request
will just return the json response from LXD. WDYT?
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.
That's roughly what I had in mind. Maybe not inferred from timeout == 0
but rather something explicit like bool wait
. Ideally something that resembles something already existing :)
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.
Hmm, ok, I'll basically put it back to how you originally had it 😁
QUrl task_url(QString("%1/operations/%2/wait") | ||
.arg(base_url.toString()) | ||
.arg(state_task["metadata"].toObject()["id"].toString())); | ||
lxd_request(manager, "GET", task_url); |
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.
Couldn't use that instead of the above poll? Or will it time out?
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.
I'm not sure if you're referring to using wait
here or the polling method.
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.
Yes, using wait
, why not use it above?
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.
Hmm, right, I think using a wait
with no or a long-ish timeout above would be just as effective. I observed that LXD can take quite a long time uncompressing an image for the first time.
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.
Need to prune multipass find
.
void mp::LXDVirtualMachineFactory::configure(const std::string& /* name */, YAML::Node& /* meta_config */, | ||
YAML::Node& /* user_config */) | ||
{ | ||
} |
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.
Hmm should we actually configure cloud init through here rather than shipping it with the vm description?
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.
Hmm, I'll look into that.
} // namespace | ||
|
||
mp::LXDVMImageVault::LXDVMImageVault(std::vector<VMImageHost*> image_hosts, const QUrl& base_url) | ||
: image_hosts{image_hosts}, base_url{base_url}, manager{std::make_unique<mp::NetworkAccessManager>()} |
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.
{C,Sh}ould the manager not be shared?
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.
I think the only way to make that work and still be generic enough for all image vault & backend driver cases would be to refactor/expand URLDownloader
to use the new NetworkManager
class.
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.
I just mean that, ideally, we'd have a shared manager for everything LXD… Not saying that URLDownloader should, too.
My rationale being that we could then set it up once (especially with SSL and authentication) and share it through all LXD components. Also saving memories.
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.
The problem I'm seeing here is that DaemonConfig
is responsible for setting up the image vault and setting up the backend driver and does that as two separate steps that are only coupled by URLDownloader
. In order for LXDVMImageVault
and LXDVirtualMachineFactory
to share a NetworkAccessManager
object would be to somehow relate those two, but do it in a way that DaemonConfig
does not have to account for backend and image host differences. I think we want DaemonConfig
to be a generic as possible and we achieve that via the platform
calls, but still, those platform calls do not couple the image vault and the backed driver.
If we don't go the URLDownloader
way, then we'd need to refactor the VirtualMachineFactory
to accept the VMImageVault
object (at least a pointer to it) and then each factory and image vault can determine their relationship to one another. Some would just ignore each other like now and we'd basically be forcing it hold at least a pointer to the vault and others like LXD would have a fairly coupled relationship like sharing the NetworkAcessManager
, etc.
I'll think about it some more, but I don't think it will be trivial.
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.
Bear in mind none of the comments I made here are my requirement, or stuff to be fixed in this PR. It's just driving my thinking about the architecture, since this is pushing what we had in place.
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.
Sure, and I totally agree with the idea of being able to couple the image vault and vm factory when needed.
Thanks for the first review.
Yep
I'm pretty sure I already did that for cloud images. I know I have not accounted for our custom repository yet. Is this what you are seeing? |
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.
That's it for now, it does work fine, though :)
void mp::LXDVMImageVault::prune_expired_images() | ||
{ | ||
} | ||
|
||
void mp::LXDVMImageVault::update_images(const FetchType& fetch_type, const PrepareAction& prepare, | ||
const ProgressMonitor& monitor) | ||
{ | ||
} |
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.
These are TODO, right? We should definitely prune old images, and {c,sh}ould we drive the update some way?
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.
They might be TODO. LXD can also handle this in and of itself, so should we config LXD to do this for us or we drive it?
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.
If we can make it DTRT without our involvement, probably best.
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.
Ok, then I will look into that and failing being able to have LXD control all of that, I'll implement it here.
{ | ||
LXDBackend() | ||
: socket_path{QString("%1/test_socket").arg(data_dir.path())}, | ||
test_server{mpt::MockLocalSocketServer(socket_path)}, |
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.
Should we not test this on the access manager level?
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.
We already unit test the access manager itself in other tests, so I think mocking it as high up as possible is the best way to go for testing the backend. I sense you disagree with this approach, so am I missing some benefits if we use the access manger as well?
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.
I mean to mock the access manager, rather than talking through a test socket (which was the correct thing to do for testing the manager itself).
Unless I'm misunderstanding what MockLocalSocketServer does?
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.
Duh on my end. I will do that to simplify this.
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.
Looking at this a bit more, what will need to happen is to make LocalAccessManager
a non-final
class and then create a mock LocalAccessManager
class and have it respond how we want. Then we'll need to modify LXDVirtualMachine
and LXDVMImageVault
to accept a LocalAccessManager
object (ie, the mocked object) and not create one in the ctor.
Then we can get rid of the MockLocalSocketServer
and just put that logic back into the test_local_network_access_manager.cpp
file.
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.
Even some more diving into this and really, doing it the current way where we talk over a local socket to a "mocked" server is the most straightforward way to go. If we went the route of mocking NetworkAccessManager
itself, we'll basically need to reimplement that along with LocalSocketReply
and that doesn't really make much sense. I think leaving it the way it is is the right thing to do in order not to introduce much more complexity in testing for the sake of not using a temporary local socket.
This comment has been minimized.
This comment has been minimized.
- Don't return const variable. - Use current_state() for getting and saving state upon initialization.
Co-authored-by: Michał Sawicz <michal.sawicz@canonical.com>
- Add `lxd` snap plug. - Remove "None" fetch type since there is now the LXDVMImageVault implementation. - Make some logging messages trace level.
- This will create a "multipass" project and define a default profile that uses a Multipass specific network bridge.
- Also, do not allow URL based launches.
This comment has been minimized.
This comment has been minimized.
08df4be
to
5f90d8e
Compare
This comment has been minimized.
This comment has been minimized.
We'll need to handle this on startup: cze 01 12:48:46 michal-laptop multipassd[767819]: Requesting LXD: GET unix:///var/snap/lxd/common/lxd/unix.socket@1.0/projects/multipass
cze 01 12:48:46 michal-laptop multipassd[767819]: Caught an unhandled exception: Cannot connect to /var/snap/lxd/common/lxd/unix.socket: 0
cze 01 12:48:46 michal-laptop systemd[1]: snap.multipass.multipassd.service: Main process exited, code=exited, status=1/FAILURE |
Yes, the health check needs a complete overhaul. Is that something that needs to be in a alpha/preview/experimental release of this? |
It's probably ok for now. |
Ack, then I'll put this on my list of TODO's for the next release of this 😁 |
5f90d8e
to
686d2cd
Compare
macOS build available: multipass-1.3.0-dev.269.pr1533+g056f1874.mac-Darwin.pkg |
I say: bors merge |
Build failed: |
This allows Multipass to communicate with the LXD daemon in order to create and mange instances.
List of TODO items needed for the next iteration:
NetworkAccessManager
andLocalSocketReply
instead of usingMockLocalSocketServer
.NetworkAccessManager
acrossLXDVirtualMachineFactory
andLXDVMImageVault
.LXDVirtualMachineFactory::hypervisor_health_check()
including detecting if LXD socket is not available, etc.VirtualMachineDesription
contains the values? Do we just create it once and letLXDVirtualMachineFactory
configure it and then forget about it since LXD has it stored?lxd-request()
the ability to wait on operations to complete.LXDVirtualMachine
to wait on an instance to be created instead of the clunkywhile
loop w/sleep()
.VMImageVault
'supdate_images()
andprune_expired_images
and associated timer.source
part of the LXD initialization can be modified to not explicitly call out the remote since the image should already be in thelocal
repository.