Skip to content
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

feat: add launch VM button #865

Merged
merged 6 commits into from
Oct 21, 2024
Merged

feat: add launch VM button #865

merged 6 commits into from
Oct 21, 2024

Conversation

cdrage
Copy link
Contributor

@cdrage cdrage commented Sep 27, 2024

feat: add launch VM button

What does this PR do?

  • Using QEMU we add a feature to "Launch VM" in the background
  • Uses websockets, qemu as well as our xterm.js library to achieve this
  • Launches in "snapshot" mode so no data is written to .raw file so the
    file can be easily re-used

Screenshot / video of UI

Screen.Recording.2024-09-27.at.11.05.00.AM.mov

What issues does this PR fix or reference?

Closes #813

How to test this PR?

  1. Be on macOS silicon
  2. brew install qemu
  3. Build a bootc container image
  4. Press launch VM button in actions bar

Signed-off-by: Charlie Drage [email protected]

@cdrage cdrage requested a review from a team as a code owner September 27, 2024 15:06
@cdrage cdrage requested review from dgolovin, jeffmaury and lstocchi and removed request for a team September 27, 2024 15:06
@cdrage cdrage changed the title feat: add launch VM button [WIP / no review] feat: add launch VM button Sep 27, 2024
@cdrage cdrage marked this pull request as draft September 27, 2024 15:06
@cdrage cdrage force-pushed the add-terminal branch 2 times, most recently from c1e9589 to 9dbe951 Compare September 27, 2024 15:07
@cdrage
Copy link
Contributor Author

cdrage commented Sep 27, 2024

This is an initial implementation and meant for experimenting as parts have not been finalized yet / further discussion needed.

Feel free to test it out @deboer-tim @slemeur

We can further discuss this during the week!

@cdrage cdrage force-pushed the add-terminal branch 2 times, most recently from 00a2992 to 017082e Compare September 27, 2024 15:11
@cdrage cdrage force-pushed the add-terminal branch 6 times, most recently from 7f889c4 to b38fa33 Compare September 30, 2024 19:05
@cdrage cdrage changed the title [WIP / no review] feat: add launch VM button [WIP] feat: add launch VM button Sep 30, 2024
@deboer-tim
Copy link
Contributor

Comments from my initial testing:

  • That's a lot of text on the page. I wonder if we can detect if qemu is installed and give shorter messages, shorten text, or say there are caveats and move those to docs?
  • Do we hide the page for non-raw disk images? Feels like it might be annoying to always have it there when you can't use it, but is also good advertising.
  • I get errors like Error stopping VM: sh: line 0: kill: (97942) - No such process in the log, even before I've tried to start a VM.
  • Woo hoo! It worked perfectly, and was so nice to see this function (again).
  • The Connection opened label is maybe correct, but I think something like ' VM running' might be more intuitive.
  • I wanted to check something from the image I was using... unfortunate that you can't pop away for a minute.
  • I know leaving the page will stop it, but I felt like I should have a Stop button too. Or conversely, if I accidentally click out before stopping it might be nice to have a warning?

@cdrage
Copy link
Contributor Author

cdrage commented Oct 1, 2024

Comments from my initial testing:

  • That's a lot of text on the page. I wonder if we can detect if qemu is installed and give shorter messages, shorten text, or say there are caveats and move those to docs?

Agreed, let's launch it and maybe just link to docs near the status icon to give more information?

  • Do we hide the page for non-raw disk images? Feels like it might be annoying to always have it there when you can't use it, but is also good advertising.

I don't think we should hide, but maybe an empty page saying "must build raw image, no raw image found"?

  • I get errors like Error stopping VM: sh: line 0: kill: (97942) - No such process in the log, even before I've tried to start a VM.

Intentional! Sorry!

  • Woo hoo! It worked perfectly, and was so nice to see this function (again).
  • The Connection opened label is maybe correct, but I think something like ' VM running' might be more intuitive.

Will fix.

  • I wanted to check something from the image I was using... unfortunate that you can't pop away for a minute.

I agree :( I think we should have that in a follow up issue / PR though.

  • I know leaving the page will stop it, but I felt like I should have a Stop button too. Or conversely, if I accidentally click out before stopping it might be nice to have a warning?

@cdrage
Copy link
Contributor Author

cdrage commented Oct 1, 2024

Screen.Recording.2024-10-01.at.12.56.43.PM.mov

@deboer-tim Ready for another review! I have done the following:

  • No more button to launch VM, it will insta-launch
  • Added prereq checks for mac + QEMU
  • Removed the error logging (except for when a VM is shut down, still working on that).
  • Fixed connection closed / VM status labels

Feel free to test it out and let me know if this works and I can begin adding some unit tests so we can get this initial POC in.

@cdrage cdrage force-pushed the add-terminal branch 2 times, most recently from 73cc1d8 to a570f03 Compare October 1, 2024 17:06
@cdrage cdrage changed the title [WIP] feat: add launch VM button feat: add launch VM button Oct 1, 2024
@cdrage cdrage marked this pull request as ready for review October 1, 2024 19:14
@cdrage
Copy link
Contributor Author

cdrage commented Oct 1, 2024

Ready for review with the exception of unit tests being added later.

This is marked as experimental (as per the tab) and only shown for macOS users.

Copy link
Contributor

@deboer-tim deboer-tim left a comment

Choose a reason for hiding this comment

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

As before, testing worked without any issues.

I don't love auto-launching a 'large' VM, but that's fine for first pass. The main drawback is the VM getting reset when you leave the tab, but again that is expected for this first step.

@@ -21,6 +21,7 @@ import type { ImageInfo, ImageInspectInfo, ManifestInspectInfo } from '@podman-d

export abstract class BootcApi {
abstract checkPrereqs(): Promise<string | undefined>;
abstract checkVMLaunchPrereqs(folder: string, architecture: string): Promise<string | undefined>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Both backend and frontend know about list of BootcBuildInfos (i.e. disk images), but frontend grabs folder and arch from one of these to check if it can be launched, then to actually launchVM(). Seems like we could use the BootBuildInfo or its id directly instead? i.e. can I launch disk image X / run disk image Y.

packages/backend/src/vm-manager.ts Outdated Show resolved Hide resolved
@cdrage cdrage force-pushed the add-terminal branch 2 times, most recently from 561f0d5 to b314111 Compare October 16, 2024 18:11
@cdrage
Copy link
Contributor Author

cdrage commented Oct 16, 2024

@deboer-tim updated with a new commit!

Let me know if that works and I can start on tests 👍

@deboer-tim
Copy link
Contributor

Thanks for the changes! I was also suggesting instead of:
abstract launchVM(folder: string, architecture: string): Promise<void>;
API could be:
abstract launchVM(diskImageId: string): Promise<void>;
but please go ahead with wrapping up tests.

@cdrage
Copy link
Contributor Author

cdrage commented Oct 16, 2024

Thanks for the changes! I was also suggesting instead of: abstract launchVM(folder: string, architecture: string): Promise<void>; API could be: abstract launchVM(diskImageId: string): Promise<void>; but please go ahead with wrapping up tests.

Done! I use BuildImageInfo for that section now because who knows what other information we may be passing in the future now.

@cdrage cdrage force-pushed the add-terminal branch 4 times, most recently from 464623c to 89f5a7a Compare October 17, 2024 18:23
### What does this PR do?

* Using QEMU we add a feature to "Launch VM" in the background
* Uses websockets, qemu as well as our xterm.js library to achieve this
* Launches in "snapshot" mode so no data is written to .raw file so the
  file can be easily re-used

### Screenshot / video of UI

<!-- If this PR is changing UI, please include
screenshots or screencasts showing the difference -->

### What issues does this PR fix or reference?

<!-- Include any related issues from Podman Desktop
repository (or from another issue tracker). -->

Closes podman-desktop#813

### How to test this PR?

<!-- Please explain steps to reproduce -->

1. Be on macOS silicon
2. `brew install qemu`
3. Build a bootc container image
4. Press launch VM button in actions bar

Signed-off-by: Charlie Drage <[email protected]>
Signed-off-by: Charlie Drage <[email protected]>
Signed-off-by: Charlie Drage <[email protected]>
Signed-off-by: Charlie Drage <[email protected]>
Signed-off-by: Charlie Drage <[email protected]>
@cdrage
Copy link
Contributor Author

cdrage commented Oct 17, 2024

Tests added and ready for review 👍

@benoitf @deboer-tim

Copy link
Contributor

@deboer-tim deboer-tim left a comment

Choose a reason for hiding this comment

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

Thanks for all the improvements, @cdrage. One minor url change noted but otherwise let's get this in and get some feedback!

@@ -17,7 +20,20 @@ async function deleteBuild(): Promise<void> {
async function gotoLogs(): Promise<void> {
router.goto(`/disk-image/${btoa(object.id)}/build`);
}

async function gotoVM(): Promise<void> {
router.goto(`/details/${btoa(object.id)}/vm`);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this needs to be updated to /disk-image/ after the nav change.

Signed-off-by: Charlie Drage <[email protected]>
@cdrage
Copy link
Contributor Author

cdrage commented Oct 21, 2024

@deboer-tim Updated and confirm link works now!

Agreed, let's get this in even if it's in nightly and get some feedback :)

Screen.Recording.2024-10-21.at.9.34.34.AM.mov

@cdrage cdrage enabled auto-merge (squash) October 21, 2024 13:36
@cdrage cdrage dismissed benoitf’s stale review October 21, 2024 13:51

dismissing as changes were made based on review + two other reviews of tim and axel

@cdrage cdrage merged commit 0bdb5a5 into podman-desktop:main Oct 21, 2024
6 checks passed
Copy link
Contributor

@benoitf benoitf left a comment

Choose a reason for hiding this comment

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

@cdrage cdrage dismissed [benoitf]

please do not dismiss the review of someone without asking him first

}
await this.notify(Messages.MSG_VM_LAUNCH_ERROR, { success: '', error: errorMessage });
}
return Promise.resolve();
Copy link
Contributor

Choose a reason for hiding this comment

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

do not need to return anything
it's an async method being void so just remove


// Stop VM by pid file on the system
async stopCurrentVM(): Promise<void> {
return await new VMManager().stopCurrentVM();
Copy link
Contributor

Choose a reason for hiding this comment

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

do not return await here, return the promise

packages/backend/src/machine-utils.ts Show resolved Hide resolved
Comment on lines +41 to +46
private build: BootcBuildInfo;

// Only values needed is the location of the VM file as well as the architecture of the image that
// will be used.
constructor(build?: BootcBuildInfo) {
this.build = build!;
Copy link
Contributor

Choose a reason for hiding this comment

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

please don't do that

it can't be optional and then you're saying there is a value with !

it will make unexpected runtime failures

Comment on lines +97 to +99
if (isMac() && isArm()) {
return this.checkMacPrereqs();
} else {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it doesn't scale well there, the idea was to have multiple instances

like you have an abstract base, then a MacArm, a MacIntel that has a preReqs method to fill
it's not the client that needs to do the if if at each call

}

private isArchitectureSupported(): boolean {
return this.build.arch === 'amd64' || this.build.arch === 'arm64';
Copy link
Contributor

Choose a reason for hiding this comment

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

should be part of the Mac implementation

// Unsupported: MacOS Intel, Linux, Windows
private generateLaunchCommand(diskImage: string): string[] {
// Future support for Mac Intel, Linux ARM, Linux X86 and Windows ARM, Windows X86 to be added here.
if (isMac() && isArm()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

if we had proper method in macArm implementation, we would not need the is and is

// logs.terminal.buffer.normal will contain the "ascii cursor" with a value of 1 until there is more logs.
// we wait until buffer.normal.length is more than 1.
while (logsTerminal.buffer.normal.length < 1) {
await new Promise(resolve => setTimeout(resolve, 500));
Copy link
Contributor

Choose a reason for hiding this comment

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

it should have a safe exit (max-retries) to avoid infinite loop

Comment on lines +208 to +216
async function resetTerminalTheme(): Promise<void> {
// Wait until we have more than 100 lines in the buffer before resetting the terminal
while (logsTerminal.buffer.normal.length < 100) {
await new Promise(resolve => setTimeout(resolve, 500));
}

// Reset the terminal
logsTerminal.reset();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

it doesn't reset the theme, it reset the terminal

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's intentional as it will clear any ASCII background characters in the log output. Tried many other alternative ways (including "setting the theme again") and nothing works.

It's a bug with websockets mode and using the host hardware for mac support and luckily a very minor edge case.

Comment on lines +157 to +174
// Allow copy / paste support with Ctrl+V
logsTerminal.attachCustomKeyEventHandler((arg: KeyboardEvent) => {
// Allow copy and paste from the ctrl key (windows and linux)
// and "meta" key (macOS). Sometimes on macOS the ctrl key is used, so we safely assume that either meta or ctrl is meant
// for paste.
if ((arg.ctrlKey || arg.metaKey) && arg.code === 'KeyV' && arg.type === 'keydown') {
bootcClient.readFromClipboard().then(clipboardText => {
// Send to socket
if (socket !== undefined) {
const encoder = new TextEncoder();
const binaryData = encoder.encode(clipboardText);
socket.send(binaryData.buffer);
}
});
}
return true;
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

why do we need that, I can copy/paste in Terminal of Podman Desktop without this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason is because we are using the special websockets mode for addon-attach https://www.npmjs.com/package/xterm-addon-attach and unfortunately we have to "disable stdin" since it's more meant for viewing websocket connections rather than interaction.

That's why we have to add a custom event handler for copying and pasting code as well as interpret each keystroke (see key section code).

@cdrage
Copy link
Contributor Author

cdrage commented Oct 22, 2024

@cdrage cdrage dismissed [benoitf]

please do not dismiss the review of someone without asking him first

I apologize and won't dismiss in the future. I had implemented your suggestions. I should of asked.

I'll follow up in a new PR with the suggested changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add initial "launch VM" implementation with terminal
5 participants