Skip to content

Commit 9a67480

Browse files
gbraadanjannath
authored andcommitted
Issue #8 Add preflight checks and fixes for Hyper-V
1 parent 041bdb5 commit 9a67480

File tree

4 files changed

+207
-4
lines changed

4 files changed

+207
-4
lines changed

pkg/crc/preflight/preflight_checks_windows.go

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
package preflight
22

33
import (
4-
"errors"
4+
//"errors"
55
"fmt"
6+
"strconv"
7+
"strings"
68

79
"github.com/code-ready/crc/pkg/crc/logging"
810
"github.com/code-ready/crc/pkg/crc/oc"
11+
12+
"github.com/code-ready/crc/pkg/crc/errors"
13+
//"github.com/code-ready/crc/pkg/os/windows/win32"
14+
"github.com/code-ready/crc/pkg/os/windows/powershell"
15+
)
16+
17+
const (
18+
// Fall Creators update comes with the "Default Switch"
19+
minimumWindowsReleaseId = 1709
20+
21+
hypervDefaultVirtualSwitchName = "Default Switch"
22+
hypervDefaultVirtualSwitchId = "c08cb7b8-9b3c-408e-8e30-5e16a3aeb444"
923
)
1024

1125
// Check if oc binary is cached or not
@@ -26,3 +40,115 @@ func fixOcBinaryCached() (bool, error) {
2640
logging.Debug("oc binary cached")
2741
return true, nil
2842
}
43+
44+
func checkVersionOfWindowsUpdate() (bool, error) {
45+
windowsReleaseId := `(Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ReleaseId).ReleaseId`
46+
47+
stdOut, _, _ := powershell.Execute(windowsReleaseId)
48+
yourWindowsReleaseId, err := strconv.Atoi(strings.TrimSpace(stdOut))
49+
50+
if err != nil {
51+
return false, errors.New("Failed to get Windows release id")
52+
}
53+
54+
if yourWindowsReleaseId < minimumWindowsReleaseId {
55+
return false, errors.Newf("Please update Windows. Currently %d is the minimum release needed to run. You are running %d", minimumWindowsReleaseId, yourWindowsReleaseId)
56+
}
57+
return true, nil
58+
}
59+
60+
// Unable to update automatically
61+
func fixVersionOfWindowsUpdate() (bool, error) {
62+
return false, errors.New("Please manually update your Windows 10 installation")
63+
}
64+
65+
func checkHyperVInstalled() (bool, error) {
66+
// check to see if a hypervisor is present. if hyper-v is installed and enabled,
67+
checkHypervisorPresent := `@(Get-Wmiobject Win32_ComputerSystem).HypervisorPresent`
68+
stdOut, _, _ := powershell.Execute(checkHypervisorPresent)
69+
if !strings.Contains(stdOut, "True") {
70+
return false, errors.New("Hyper-V not installed")
71+
}
72+
73+
// Check if Hyper-V's Virtual Machine Management Service is running
74+
checkVmmsRunning := `@(Get-Service vmms).Status`
75+
stdOut, _, _ = powershell.Execute(checkVmmsRunning)
76+
if strings.TrimSpace(stdOut) != "Running" {
77+
return false, errors.New("Hyper-V Virtual Machine Management service not running")
78+
}
79+
80+
return true, nil
81+
}
82+
83+
//
84+
func fixHyperVInstalled() (bool, error) {
85+
enableHyperVCommand := `Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All`
86+
_, _, err := powershell.ExecuteAsAdmin(enableHyperVCommand)
87+
88+
if err != nil {
89+
return false, errors.New("Error occured installing Hyper-V")
90+
}
91+
92+
// We do need to error out as a restart might be needed (unfortunately no output redirect possible)
93+
return true, errors.New("Please reboot your system")
94+
}
95+
96+
func checkIfUserPartOfHyperVAdmins() (bool, error) {
97+
// https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
98+
// BUILTIN\Hyper-V Administrators => S-1-5-32-578
99+
100+
checkIfMemberOfHyperVAdmins :=
101+
`$sid = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-578")
102+
@([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole($sid)`
103+
stdOut, _, _ := powershell.Execute(checkIfMemberOfHyperVAdmins)
104+
if !strings.Contains(stdOut, "True") {
105+
return false, errors.New("User is not a member of the Hyper-V administrators group")
106+
}
107+
108+
return true, nil
109+
}
110+
111+
func fixUserPartOfHyperVAdmins() (bool, error) {
112+
outGroupName, _, err := powershell.Execute(`(New-Object System.Security.Principal.SecurityIdentifier("S-1-5-32-578")).Translate([System.Security.Principal.NTAccount]).Value`)
113+
if err != nil {
114+
return false, errors.New("Unable to get group name")
115+
}
116+
groupName := strings.TrimSpace(strings.Replace(strings.TrimSpace(outGroupName), "BUILTIN\\", "", -1))
117+
118+
outUsername, _, err := powershell.Execute(`Write-Host $env:USERNAME`)
119+
if err != nil {
120+
return false, errors.New("Unable to get user name")
121+
}
122+
username := strings.TrimSpace(outUsername)
123+
124+
netCmdArgs := fmt.Sprintf(`([adsi]"WinNT://./%s,group").Add("WinNT://%s,user")`, groupName, username)
125+
_, _, err = powershell.ExecuteAsAdmin(netCmdArgs)
126+
if err != nil {
127+
return false, errors.New("Error adding user to group")
128+
}
129+
130+
return true, nil
131+
}
132+
133+
func checkIfHyperVVirtualSwitchExists() (bool, error) {
134+
// TODO: vswitch configurable (use MachineConfig)
135+
switchName := hypervDefaultVirtualSwitchName
136+
137+
// check for default switch by using the Id
138+
if switchName == hypervDefaultVirtualSwitchName {
139+
checkIfDefaultSwitchExists := fmt.Sprintf("Get-VMSwitch -Id %s | ForEach-Object { $_.Name }", hypervDefaultVirtualSwitchId)
140+
_, stdErr, _ := powershell.Execute(checkIfDefaultSwitchExists)
141+
142+
if !strings.Contains(stdErr, "Get-VMSwitch") {
143+
// found the default
144+
return true, nil
145+
}
146+
}
147+
148+
return false, errors.New("Virtual Switch not found")
149+
}
150+
151+
// Unable to do for now
152+
func fixHyperVVirtualSwitch() (bool, error) {
153+
return false, errors.New("Please override the default by adding an external virtual switch and set configuration")
154+
}

pkg/crc/preflight/preflight_windows.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package preflight
22

33
import (
4+
"errors"
5+
46
cmdConfig "github.com/code-ready/crc/cmd/crc/cmd/config"
57
"github.com/code-ready/crc/pkg/crc/config"
68
)
@@ -17,6 +19,29 @@ func StartPreflightChecks(vmDriver string) {
1719
"Checking if CRC bundle is cached in '$HOME/.crc'",
1820
config.GetBool(cmdConfig.WarnCheckBundleCached.Name),
1921
)
22+
23+
if vmDriver == "hyperv" {
24+
preflightCheckSucceedsOrFails(false,
25+
checkVersionOfWindowsUpdate,
26+
"Check Windows 10 release",
27+
false,
28+
)
29+
preflightCheckSucceedsOrFails(false,
30+
checkHyperVInstalled,
31+
"Hyper-V installed and operational",
32+
false,
33+
)
34+
preflightCheckSucceedsOrFails(false,
35+
checkIfUserPartOfHyperVAdmins,
36+
"Is user a member of the Hyper-V Administrators group",
37+
false,
38+
)
39+
preflightCheckSucceedsOrFails(false,
40+
checkIfHyperVVirtualSwitchExists,
41+
"Does the Hyper-V virtual switch exist",
42+
false,
43+
)
44+
}
2045
}
2146

2247
// SetupHost performs the prerequisite checks and setups the host to run the cluster
@@ -33,4 +58,32 @@ func SetupHost(vmDriver string) {
3358
"Unpacking bundle from the CRC binary",
3459
config.GetBool(cmdConfig.WarnCheckBundleCached.Name),
3560
)
61+
62+
if vmDriver == "hyperv" {
63+
preflightCheckAndFix(false,
64+
checkVersionOfWindowsUpdate,
65+
fixVersionOfWindowsUpdate,
66+
"Check Windows 10 release",
67+
false,
68+
)
69+
preflightCheckAndFix(false,
70+
checkHyperVInstalled,
71+
fixHyperVInstalled,
72+
"Hyper-V installed",
73+
false,
74+
)
75+
preflightCheckAndFix(false,
76+
// Workaround to an issue the check returns "True"
77+
func() (bool, error) { return false, errors.New("Always add user") },
78+
fixUserPartOfHyperVAdmins,
79+
"Is user a member of the Hyper-V Administrators group",
80+
false,
81+
)
82+
preflightCheckAndFix(false,
83+
checkIfHyperVVirtualSwitchExists,
84+
fixHyperVVirtualSwitch,
85+
"Does the Hyper-V virtual switch exist",
86+
false,
87+
)
88+
}
3689
}

pkg/crc/services/dns/dns_windows.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,34 @@
11
package dns
22

33
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
8+
"github.com/code-ready/crc/pkg/crc/errors"
49
"github.com/code-ready/crc/pkg/crc/services"
10+
11+
"github.com/code-ready/crc/pkg/os/windows/powershell"
512
)
613

714
func runPostStartForOS(serviceConfig services.ServicePostStartConfig, result *services.ServicePostStartResult) (services.ServicePostStartResult, error) {
15+
// NOTE: this is very Hyper-V specific
16+
// TODO: localize the use of the Default Switch
17+
setDNSServerCommand := fmt.Sprintf(`Set-DnsClientServerAddress "vEthernet (Default Switch)" -ServerAddress ("%s")`, serviceConfig.IP)
18+
powershell.ExecuteAsAdmin(setDNSServerCommand)
19+
20+
time.Sleep(2 * time.Second)
21+
22+
getDNSServerCommand := `(Get-DnsClientServerAddress "vEthernet (Default Switch)").ServerAddresses`
23+
stdOut, _, _ := powershell.Execute(getDNSServerCommand)
24+
25+
if !strings.Contains(stdOut, serviceConfig.IP) {
26+
err := errors.New("Nameserver not successfully set")
27+
result.Success = false
28+
result.Error = err.Error()
29+
return *result, err
30+
}
31+
832
result.Success = true
933
return *result, nil
1034
}

pkg/os/windows/powershell/powershell_windows.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var (
1818
`$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator;`,
1919
`if (-Not ($myWindowsPrincipal.IsInRole($adminRole))) {`,
2020
` $procInfo = New-Object System.Diagnostics.ProcessStartInfo;`,
21-
` $procInfo.FileName = "` + locatePowerShell() + `"`,
21+
` $procInfo.FileName = "` + LocatePowerShell() + `"`,
2222
` $procInfo.WindowStyle = [Diagnostics.ProcessWindowStyle]::Hidden`,
2323
` $procInfo.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'"`,
2424
` $procInfo.Verb = "runas";`,
@@ -32,7 +32,7 @@ var (
3232
}
3333
)
3434

35-
func locatePowerShell() string {
35+
func LocatePowerShell() string {
3636
ps, _ := exec.LookPath("powershell.exe")
3737
return ps
3838
}
@@ -52,7 +52,7 @@ func IsAdmin() bool {
5252

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

5858
var stdout bytes.Buffer

0 commit comments

Comments
 (0)