Skip to content

Commit

Permalink
Issue #8 Add preflight checks and fixes for Hyper-V
Browse files Browse the repository at this point in the history
  • Loading branch information
gbraad authored and anjannath committed Aug 5, 2019
1 parent 041bdb5 commit 9a67480
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 4 deletions.
128 changes: 127 additions & 1 deletion pkg/crc/preflight/preflight_checks_windows.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
package preflight

import (
"errors"
//"errors"
"fmt"
"strconv"
"strings"

"github.com/code-ready/crc/pkg/crc/logging"
"github.com/code-ready/crc/pkg/crc/oc"

"github.com/code-ready/crc/pkg/crc/errors"
//"github.com/code-ready/crc/pkg/os/windows/win32"
"github.com/code-ready/crc/pkg/os/windows/powershell"
)

const (
// Fall Creators update comes with the "Default Switch"
minimumWindowsReleaseId = 1709

hypervDefaultVirtualSwitchName = "Default Switch"
hypervDefaultVirtualSwitchId = "c08cb7b8-9b3c-408e-8e30-5e16a3aeb444"
)

// Check if oc binary is cached or not
Expand All @@ -26,3 +40,115 @@ func fixOcBinaryCached() (bool, error) {
logging.Debug("oc binary cached")
return true, nil
}

func checkVersionOfWindowsUpdate() (bool, error) {
windowsReleaseId := `(Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ReleaseId).ReleaseId`

stdOut, _, _ := powershell.Execute(windowsReleaseId)
yourWindowsReleaseId, err := strconv.Atoi(strings.TrimSpace(stdOut))

if err != nil {
return false, errors.New("Failed to get Windows release id")
}

if yourWindowsReleaseId < minimumWindowsReleaseId {
return false, errors.Newf("Please update Windows. Currently %d is the minimum release needed to run. You are running %d", minimumWindowsReleaseId, yourWindowsReleaseId)
}
return true, nil
}

// Unable to update automatically
func fixVersionOfWindowsUpdate() (bool, error) {
return false, errors.New("Please manually update your Windows 10 installation")
}

func checkHyperVInstalled() (bool, error) {
// check to see if a hypervisor is present. if hyper-v is installed and enabled,
checkHypervisorPresent := `@(Get-Wmiobject Win32_ComputerSystem).HypervisorPresent`
stdOut, _, _ := powershell.Execute(checkHypervisorPresent)
if !strings.Contains(stdOut, "True") {
return false, errors.New("Hyper-V not installed")
}

// Check if Hyper-V's Virtual Machine Management Service is running
checkVmmsRunning := `@(Get-Service vmms).Status`
stdOut, _, _ = powershell.Execute(checkVmmsRunning)
if strings.TrimSpace(stdOut) != "Running" {
return false, errors.New("Hyper-V Virtual Machine Management service not running")
}

return true, nil
}

//
func fixHyperVInstalled() (bool, error) {
enableHyperVCommand := `Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All`
_, _, err := powershell.ExecuteAsAdmin(enableHyperVCommand)

if err != nil {
return false, errors.New("Error occured installing Hyper-V")
}

// We do need to error out as a restart might be needed (unfortunately no output redirect possible)
return true, errors.New("Please reboot your system")
}

func checkIfUserPartOfHyperVAdmins() (bool, error) {
// https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
// BUILTIN\Hyper-V Administrators => S-1-5-32-578

checkIfMemberOfHyperVAdmins :=
`$sid = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-578")
@([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole($sid)`
stdOut, _, _ := powershell.Execute(checkIfMemberOfHyperVAdmins)
if !strings.Contains(stdOut, "True") {
return false, errors.New("User is not a member of the Hyper-V administrators group")
}

return true, nil
}

func fixUserPartOfHyperVAdmins() (bool, error) {
outGroupName, _, err := powershell.Execute(`(New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-578")).Translate([System.Security.Principal.NTAccount]).Value`)
if err != nil {
return false, errors.New("Unable to get group name")
}
groupName := strings.TrimSpace(strings.Replace(strings.TrimSpace(outGroupName), "BUILTIN\\", "", -1))

outUsername, _, err := powershell.Execute(`Write-Host $env:USERNAME`)
if err != nil {
return false, errors.New("Unable to get user name")
}
username := strings.TrimSpace(outUsername)

netCmdArgs := fmt.Sprintf(`([adsi]"WinNT://./%s,group").Add("WinNT://%s,user")`, groupName, username)
_, _, err = powershell.ExecuteAsAdmin(netCmdArgs)
if err != nil {
return false, errors.New("Error adding user to group")
}

return true, nil
}

func checkIfHyperVVirtualSwitchExists() (bool, error) {
// TODO: vswitch configurable (use MachineConfig)
switchName := hypervDefaultVirtualSwitchName

// check for default switch by using the Id
if switchName == hypervDefaultVirtualSwitchName {
checkIfDefaultSwitchExists := fmt.Sprintf("Get-VMSwitch -Id %s | ForEach-Object { $_.Name }", hypervDefaultVirtualSwitchId)
_, stdErr, _ := powershell.Execute(checkIfDefaultSwitchExists)

if !strings.Contains(stdErr, "Get-VMSwitch") {
// found the default
return true, nil
}
}

return false, errors.New("Virtual Switch not found")
}

// Unable to do for now
func fixHyperVVirtualSwitch() (bool, error) {
return false, errors.New("Please override the default by adding an external virtual switch and set configuration")
}
53 changes: 53 additions & 0 deletions pkg/crc/preflight/preflight_windows.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package preflight

import (
"errors"

cmdConfig "github.com/code-ready/crc/cmd/crc/cmd/config"
"github.com/code-ready/crc/pkg/crc/config"
)
Expand All @@ -17,6 +19,29 @@ func StartPreflightChecks(vmDriver string) {
"Checking if CRC bundle is cached in '$HOME/.crc'",
config.GetBool(cmdConfig.WarnCheckBundleCached.Name),
)

if vmDriver == "hyperv" {
preflightCheckSucceedsOrFails(false,
checkVersionOfWindowsUpdate,
"Check Windows 10 release",
false,
)
preflightCheckSucceedsOrFails(false,
checkHyperVInstalled,
"Hyper-V installed and operational",
false,
)
preflightCheckSucceedsOrFails(false,
checkIfUserPartOfHyperVAdmins,
"Is user a member of the Hyper-V Administrators group",
false,
)
preflightCheckSucceedsOrFails(false,
checkIfHyperVVirtualSwitchExists,
"Does the Hyper-V virtual switch exist",
false,
)
}
}

// SetupHost performs the prerequisite checks and setups the host to run the cluster
Expand All @@ -33,4 +58,32 @@ func SetupHost(vmDriver string) {
"Unpacking bundle from the CRC binary",
config.GetBool(cmdConfig.WarnCheckBundleCached.Name),
)

if vmDriver == "hyperv" {
preflightCheckAndFix(false,
checkVersionOfWindowsUpdate,
fixVersionOfWindowsUpdate,
"Check Windows 10 release",
false,
)
preflightCheckAndFix(false,
checkHyperVInstalled,
fixHyperVInstalled,
"Hyper-V installed",
false,
)
preflightCheckAndFix(false,
// Workaround to an issue the check returns "True"
func() (bool, error) { return false, errors.New("Always add user") },
fixUserPartOfHyperVAdmins,
"Is user a member of the Hyper-V Administrators group",
false,
)
preflightCheckAndFix(false,
checkIfHyperVVirtualSwitchExists,
fixHyperVVirtualSwitch,
"Does the Hyper-V virtual switch exist",
false,
)
}
}
24 changes: 24 additions & 0 deletions pkg/crc/services/dns/dns_windows.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
package dns

import (
"fmt"
"strings"
"time"

"github.com/code-ready/crc/pkg/crc/errors"
"github.com/code-ready/crc/pkg/crc/services"

"github.com/code-ready/crc/pkg/os/windows/powershell"
)

func runPostStartForOS(serviceConfig services.ServicePostStartConfig, result *services.ServicePostStartResult) (services.ServicePostStartResult, error) {
// NOTE: this is very Hyper-V specific
// TODO: localize the use of the Default Switch
setDNSServerCommand := fmt.Sprintf(`Set-DnsClientServerAddress "vEthernet (Default Switch)" -ServerAddress ("%s")`, serviceConfig.IP)
powershell.ExecuteAsAdmin(setDNSServerCommand)

time.Sleep(2 * time.Second)

getDNSServerCommand := `(Get-DnsClientServerAddress "vEthernet (Default Switch)").ServerAddresses`
stdOut, _, _ := powershell.Execute(getDNSServerCommand)

if !strings.Contains(stdOut, serviceConfig.IP) {
err := errors.New("Nameserver not successfully set")
result.Success = false
result.Error = err.Error()
return *result, err
}

result.Success = true
return *result, nil
}
6 changes: 3 additions & 3 deletions pkg/os/windows/powershell/powershell_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var (
`$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator;`,
`if (-Not ($myWindowsPrincipal.IsInRole($adminRole))) {`,
` $procInfo = New-Object System.Diagnostics.ProcessStartInfo;`,
` $procInfo.FileName = "` + locatePowerShell() + `"`,
` $procInfo.FileName = "` + LocatePowerShell() + `"`,
` $procInfo.WindowStyle = [Diagnostics.ProcessWindowStyle]::Hidden`,
` $procInfo.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'"`,
` $procInfo.Verb = "runas";`,
Expand All @@ -32,7 +32,7 @@ var (
}
)

func locatePowerShell() string {
func LocatePowerShell() string {
ps, _ := exec.LookPath("powershell.exe")
return ps
}
Expand All @@ -52,7 +52,7 @@ func IsAdmin() bool {

func Execute(args ...string) (stdOut string, stdErr string, err error) {
args = append([]string{"-NoProfile", "-NonInteractive", "-ExecutionPolicy", "RemoteSigned", "-Command"}, args...)
cmd := exec.Command(locatePowerShell(), args...)
cmd := exec.Command(LocatePowerShell(), args...)
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}

var stdout bytes.Buffer
Expand Down

0 comments on commit 9a67480

Please sign in to comment.