-
Notifications
You must be signed in to change notification settings - Fork 6
[machine]:windows node setup #13
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
base: main
Are you sure you want to change the base?
Changes from all commits
a291fb2
e56d75e
f6de88d
90a9a54
b12990f
eed9cde
89ef351
4c949c1
84125a6
963f9b4
42d6a73
896dc65
4ccf98c
57dc1ce
ef33241
6e7b321
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,9 +2,13 @@ package hyperv | |
|
|
||
| import ( | ||
| "encoding/json" | ||
| "errors" | ||
| "fmt" | ||
| "io/ioutil" | ||
| "net" | ||
| "os" | ||
| "path/filepath" | ||
| "regexp" | ||
| "strings" | ||
| "time" | ||
|
|
||
|
|
@@ -19,13 +23,15 @@ import ( | |
| type Driver struct { | ||
| *drivers.BaseDriver | ||
| Boot2DockerURL string | ||
| WindowsVHDUrl string | ||
| VSwitch string | ||
| DiskSize int | ||
| MemSize int | ||
| CPU int | ||
| MacAddr string | ||
| VLanID int | ||
| DisableDynamicMemory bool | ||
| OS string | ||
| } | ||
|
|
||
| const ( | ||
|
|
@@ -35,6 +41,7 @@ const ( | |
| defaultVLanID = 0 | ||
| defaultDisableDynamicMemory = false | ||
| defaultSwitchID = "c08cb7b8-9b3c-408e-8e30-5e16a3aeb444" | ||
| defaultServerImageFilename = "hybrid-minikube-windows-server.vhdx" | ||
| ) | ||
|
|
||
| // NewDriver creates a new Hyper-v driver with default settings. | ||
|
|
@@ -43,6 +50,7 @@ func NewDriver(hostName, storePath string) *Driver { | |
| DiskSize: defaultDiskSize, | ||
| MemSize: defaultMemory, | ||
| CPU: defaultCPU, | ||
| WindowsVHDUrl: mcnutils.ConfigGuest.GetVHDUrl(), | ||
bobsira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| DisableDynamicMemory: defaultDisableDynamicMemory, | ||
| BaseDriver: &drivers.BaseDriver{ | ||
| MachineName: hostName, | ||
|
|
@@ -142,7 +150,7 @@ func (d *Driver) GetURL() (string, error) { | |
| func (d *Driver) GetState() (state.State, error) { | ||
| stdout, err := cmdOut("(", "Hyper-V\\Get-VM", d.MachineName, ").state") | ||
| if err != nil { | ||
| return state.None, fmt.Errorf("Failed to find the VM status") | ||
| return state.None, fmt.Errorf("failed to find the VM status") | ||
| } | ||
|
|
||
| resp := parseLines(stdout) | ||
|
|
@@ -186,20 +194,35 @@ func (d *Driver) PreCreateCheck() error { | |
| return err | ||
| } | ||
|
|
||
| // Downloading boot2docker to cache should be done here to make sure | ||
| // Downloading boot2docker/windows-server to cache should be done here to make sure | ||
| // that a download failure will not leave a machine half created. | ||
| b2dutils := mcnutils.NewB2dUtils(d.StorePath) | ||
| err = b2dutils.UpdateISOCache(d.Boot2DockerURL) | ||
|
|
||
| if mcnutils.ConfigGuest.GetGuestOS() != "windows" { | ||
| err = b2dutils.UpdateISOCache(d.Boot2DockerURL) | ||
| } else { | ||
| err = b2dutils.UpdateVHDCache(d.WindowsVHDUrl) | ||
| } | ||
|
|
||
| return err | ||
| } | ||
|
|
||
| func (d *Driver) Create() error { | ||
| b2dutils := mcnutils.NewB2dUtils(d.StorePath) | ||
| if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil { | ||
| return err | ||
|
|
||
| if mcnutils.ConfigGuest.GetGuestOS() == "windows" { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'd be nice if the ConfigGuest type had a "IsWindows()" method that we reuse throughout this change. I know it's just a comparison against a string, but this type of comparison changes over time. |
||
| d.SSHUser = "Administrator" | ||
| if err := b2dutils.CopyWindowsVHDToMachineDir(d.WindowsVHDUrl, d.MachineName); err != nil { | ||
| return err | ||
| } | ||
| } else { | ||
| if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| log.Infof("Creating SSH key...") | ||
|
|
||
| if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil { | ||
| return err | ||
| } | ||
|
|
@@ -214,18 +237,36 @@ func (d *Driver) Create() error { | |
| } | ||
| log.Infof("Using switch %q", d.VSwitch) | ||
|
|
||
| diskImage, err := d.generateDiskImage() | ||
| if mcnutils.ConfigGuest.GetGuestOS() == "windows" { | ||
| log.Infof("Adding SSH key to the VHDX...") | ||
| if err := writeSSHKeyToVHDX(d.ResolveStorePath(defaultServerImageFilename), d.publicSSHKeyPath()); err != nil { | ||
| log.Errorf("Error creating disk image: %s", err) | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| var diskImage string | ||
| var err error | ||
| if mcnutils.ConfigGuest.GetGuestOS() != "windows" { | ||
| diskImage, err = d.generateDiskImage() | ||
| } | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| vmGeneration := "1" | ||
| if mcnutils.ConfigGuest.GetGuestOS() == "windows" { | ||
| vmGeneration = "2" | ||
| } | ||
|
|
||
| if err := cmd("Hyper-V\\New-VM", | ||
| d.MachineName, | ||
| "-Path", fmt.Sprintf("'%s'", d.ResolveStorePath(".")), | ||
| "-SwitchName", quote(d.VSwitch), | ||
| "-Generation", quote(vmGeneration), | ||
| "-MemoryStartupBytes", toMb(d.MemSize)); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| if d.DisableDynamicMemory { | ||
| if err := cmd("Hyper-V\\Set-VMMemory", | ||
| "-VMName", d.MachineName, | ||
|
|
@@ -237,7 +278,8 @@ func (d *Driver) Create() error { | |
| if d.CPU > 1 { | ||
| if err := cmd("Hyper-V\\Set-VMProcessor", | ||
| d.MachineName, | ||
| "-Count", fmt.Sprintf("%d", d.CPU)); err != nil { | ||
| "-Count", fmt.Sprintf("%d", d.CPU), | ||
| "-ExposeVirtualizationExtensions", "$true"); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
@@ -259,19 +301,29 @@ func (d *Driver) Create() error { | |
| } | ||
| } | ||
|
|
||
| if err := cmd("Hyper-V\\Set-VMDvdDrive", | ||
| "-VMName", d.MachineName, | ||
| "-Path", quote(d.ResolveStorePath("boot2docker.iso"))); err != nil { | ||
| return err | ||
| if mcnutils.ConfigGuest.GetGuestOS() != "windows" { | ||
| log.Infof("Attaching ISO and disk...") | ||
| if err := cmd("Hyper-V\\Set-VMDvdDrive", | ||
| "-VMName", d.MachineName, | ||
| "-Path", quote(d.ResolveStorePath("boot2docker.iso"))); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| if err := cmd("Hyper-V\\Add-VMHardDiskDrive", | ||
| "-VMName", d.MachineName, | ||
| "-Path", quote(diskImage)); err != nil { | ||
| return err | ||
| if mcnutils.ConfigGuest.GetGuestOS() == "windows" { | ||
| if err := cmd("Hyper-V\\Add-VMHardDiskDrive", | ||
| "-VMName", d.MachineName, | ||
| "-Path", quote(d.ResolveStorePath("hybrid-minikube-windows-server.vhdx")), | ||
| "-ControllerType", "SCSI"); err != nil { | ||
| return err | ||
| } | ||
| } else { | ||
| if err := cmd("Hyper-V\\Add-VMHardDiskDrive", | ||
| "-VMName", d.MachineName, | ||
| "-Path", quote(diskImage)); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| log.Infof("Starting VM...") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like a useful logging message we're removing. Could even enhance it a bit with info about the VM we're starting. |
||
| return d.Start() | ||
| } | ||
|
|
||
|
|
@@ -507,3 +559,57 @@ func (d *Driver) generateDiskImage() (string, error) { | |
|
|
||
| return diskImage, nil | ||
| } | ||
| func writeSSHKeyToVHDX(vhdxPath, publicSSHKeyPath string) (retErr error) { | ||
| output, err := cmdOut( | ||
bobsira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "powershell", "-Command", | ||
| "(Get-DiskImage -ImagePath", quote(vhdxPath), "| Mount-DiskImage -PassThru) | Out-Null;", | ||
| "$diskNumber = (Get-DiskImage -ImagePath", quote(vhdxPath), "| Get-Disk).Number;", | ||
| "Set-Disk -Number $diskNumber -IsReadOnly $false;", | ||
| "(Get-Disk -Number $diskNumber | Get-Partition | Get-Volume).DriveLetter", | ||
| ) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to mount VHDX and retrieve mount directory: %w", err) | ||
| } | ||
|
|
||
| regex := regexp.MustCompile(`\s+|\r|\n`) | ||
| driveLetter := regex.ReplaceAllString(output, "") | ||
|
|
||
| if driveLetter == "" { | ||
bobsira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| log.Debugf("No drive letter assigned to VHDX") | ||
| return errors.New("no drive letter assigned to VHDX") | ||
| } | ||
|
|
||
| mountDir := strings.TrimSpace(driveLetter) + ":" + string(os.PathSeparator) | ||
|
|
||
| defer func() { | ||
| if unmountErr := cmd("Dismount-DiskImage", "-ImagePath", quote(vhdxPath)); unmountErr != nil { | ||
bobsira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| retErr = errors.Join(retErr, fmt.Errorf("failed to unmount VHDX: %w", unmountErr)) | ||
| } | ||
| }() | ||
|
|
||
| sshDir := filepath.Join(mountDir, "ProgramData", "ssh") | ||
| adminAuthKeys := filepath.Join(sshDir, "administrators_authorized_keys") | ||
|
|
||
| pubKey, err := os.ReadFile(publicSSHKeyPath) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to read public SSH key from %s: %w", publicSSHKeyPath, err) | ||
| } | ||
|
|
||
| if _, err := os.Stat(mountDir); os.IsNotExist(err) { | ||
| return fmt.Errorf("mount point %s does not exist", mountDir) | ||
| } | ||
|
|
||
| if err := os.MkdirAll(sshDir, 0755); err != nil { | ||
| return fmt.Errorf("failed to create SSH directory: %w", err) | ||
| } | ||
|
|
||
| if err := ioutil.WriteFile(adminAuthKeys, pubKey, 0644); err != nil { | ||
| return fmt.Errorf("failed to write public key: %w", err) | ||
| } | ||
|
|
||
| if err := cmd("icacls.exe", quote(adminAuthKeys), "/inheritance:r", "/grant", "Administrators:F", "/grant", "SYSTEM:F"); err != nil { | ||
| return fmt.Errorf("failed to set permissions on %s: %w", adminAuthKeys, err) | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.