Skip to content

Commit 38c423d

Browse files
committed
Added Hyper-V support, some code refactoring and fixes
1 parent 2f23f45 commit 38c423d

File tree

5 files changed

+72
-19
lines changed

5 files changed

+72
-19
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# getIE
22
Command line tool to download and setup Internet Explorer virtual machines provided by Microsoft for developers.
33

4-
Inspired by [Automated installation of the Microsoft IE App Compat virtual machines](http://xdissent.github.com/ievms)
4+
Inspired by [Automated installation of the Microsoft IE App Compat virtual machines](http://xdissent.github.com/ievms).
55

6-
More information about IE virtual machines provided by Microsoft could be found [here](https://dev.windows.com/en-us/microsoft-edge/tools/vms/windows/)
6+
More information about IE virtual machines provided by Microsoft could be found [here](https://dev.windows.com/en-us/microsoft-edge/tools/vms/windows/).
77

8-
### Status
8+
### Build status
99
[![Build Status](https://travis-ci.org/artemdevel/getIE.svg)](https://travis-ci.org/artemdevel/getIE)

main.go

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ func main() {
2222
platforms, "Select platform", "All", utils.GetDefaultPlatform)
2323
userChoice.Hypervisor = utils.SelectOption(
2424
hypervisors, "Select hypervisor", userChoice.Platform, utils.GetDefaultHypervisor)
25+
if userChoice.Hypervisor == "HyperV" {
26+
utils.EnterToContinue("WARNING: For HyperV you must run this tool as Administrator")
27+
}
2528
userChoice.BrowserOs = utils.SelectOption(
2629
browsers, "Select browser and OS", userChoice.Hypervisor, utils.GetDefaultBrowser)
2730
userChoice.VMImage = availableVms[userChoice.Spec]

utils/cli.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ func YesNoConfirmation(msg string) {
3636
func EnterToContinue(msg string) {
3737
reader := bufio.NewReader(os.Stdin)
3838
if runtime.GOOS == "darwin" {
39-
fmt.Printf("%s. Press ENTER to continue CMD-C to abort\n", msg)
39+
fmt.Printf("%s. Press ENTER to continue CMD-C to abort.\n", msg)
4040
} else {
41-
fmt.Printf("%s. Press ENTER to continue CTRL-C to abort\n", msg)
41+
fmt.Printf("%s. Press ENTER to continue CTRL-C to abort.\n", msg)
4242
}
4343
reader.ReadString('\n')
4444
}
@@ -57,7 +57,7 @@ func SelectOption(choices ChoiceGroups, groupMsg, groupName string, defaultChoic
5757
for {
5858
fmt.Printf("%s [%d]: ", groupMsg, defaultChoice)
5959
text, _ := reader.ReadString('\n')
60-
if text == "\n" {
60+
if strings.TrimSpace(text) == "" {
6161
return sortedChoices[defaultChoice]
6262
}
6363
selected, err := strconv.Atoi(strings.TrimSpace(text))

utils/data.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"io/ioutil"
99
"net/http"
1010
"os"
11-
"path"
1211
"regexp"
1312
"runtime"
1413
"strings"
@@ -146,11 +145,11 @@ func ParseJSON(rawData *[]byte) (
146145
func getDownloadPath() string {
147146
switch runtime.GOOS {
148147
case "linux":
149-
return path.Join(os.Getenv("HOME"), "Downloads")
148+
return pathJoin(os.Getenv("HOME"), "Downloads")
150149
case "darwin":
151-
return path.Join(os.Getenv("HOME"), "Downloads")
150+
return pathJoin(os.Getenv("HOME"), "Downloads")
152151
case "windows":
153-
return path.Join(os.Getenv("USERPROFILE"), "Downloads")
152+
return pathJoin(os.Getenv("USERPROFILE"), "Downloads")
154153
default:
155154
return ""
156155
}

utils/vm.go

+60-9
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"os/exec"
1515
"path"
1616
"strings"
17+
"runtime"
1718
)
1819

1920
// ProgressWrapper type is used to track download progress.
@@ -77,12 +78,19 @@ func compareMd5(md5str1, md5str2 string) {
7778
}
7879
}
7980

81+
func pathJoin(path1, path2 string) string {
82+
if runtime.GOOS == "windows" {
83+
return strings.Replace(path.Join(path1, path2), "/", "\\", -1)
84+
}
85+
return path.Join(path1, path2)
86+
}
87+
8088
// DownloadVM function downloads VM archive defined by a user and returns the path where it was stored.
8189
func DownloadVM(uc UserChoice) string {
8290
// TODO: during download add .part extension to the downloaded file
8391
// TODO: add check for .part file for resumable downloads
8492
// TODO: return error instead of panic()
85-
vmFile := path.Join(uc.DownloadPath, path.Base(uc.VMImage.FileURL))
93+
vmFile := pathJoin(uc.DownloadPath, path.Base(uc.VMImage.FileURL))
8694
fmt.Printf("Prepare to download %s to %s\n", uc.VMImage.FileURL, vmFile)
8795

8896
origMd5 := getOrigMd5(uc.VMImage)
@@ -147,6 +155,8 @@ func vmFilePath(hypervisor string, collectedPaths []string) string {
147155
search = ".ova"
148156
} else if hypervisor == "VMware" {
149157
search = ".ovf"
158+
} else if hypervisor == "HyperV" {
159+
search = ".xml"
150160
} else {
151161
fmt.Printf("Hypervisor %s isn't supported.\n", hypervisor)
152162
return ""
@@ -162,14 +172,14 @@ func vmFilePath(hypervisor string, collectedPaths []string) string {
162172

163173
// UnzipVM function unpack downloaded VM archive.
164174
func UnzipVM(uc UserChoice) string {
165-
vmPath := path.Join(uc.DownloadPath, path.Base(uc.VMImage.FileURL))
175+
vmPath := pathJoin(uc.DownloadPath, path.Base(uc.VMImage.FileURL))
166176
zipReader, err := zip.OpenReader(vmPath)
167177
if err != nil {
168178
panic(err)
169179
}
170180
defer zipReader.Close()
171181

172-
unzipFolder := path.Join(uc.DownloadPath, path.Base(uc.VMImage.FileURL))
182+
unzipFolder := pathJoin(uc.DownloadPath, path.Base(uc.VMImage.FileURL))
173183
unzipFolderParts := strings.Split(unzipFolder, ".")
174184
unzipFolder = strings.Join(unzipFolderParts[:len(unzipFolderParts)-1], ".")
175185
if _, err := os.Stat(unzipFolder); os.IsNotExist(err) {
@@ -182,10 +192,10 @@ func UnzipVM(uc UserChoice) string {
182192
var collectedPaths []string
183193
for _, file := range zipReader.File {
184194
fmt.Printf("Unpacking '%s'\n", file.Name)
185-
filePath := path.Join(unzipFolder, file.Name)
195+
filePath := pathJoin(unzipFolder, file.Name)
186196
if _, err := os.Stat(filePath); err == nil {
187197
collectedPaths = append(collectedPaths, filePath)
188-
fmt.Printf("File '%s' already exist, skip\n", filePath)
198+
fmt.Printf("File '%s' already exist, skip.\n", filePath)
189199
continue
190200
}
191201
if file.FileInfo().IsDir() {
@@ -245,6 +255,7 @@ func virtualboxImportVM(vmPath string) error {
245255
}
246256

247257
func vmwareCheck() error {
258+
// TODO: improve VMware installation checks for Windows platforms.
248259
// NOTE: VMware requires two command line tools to works with VMs.
249260
fmt.Println("Checking VMware installation..")
250261
cmdName := "ovftool"
@@ -258,9 +269,11 @@ func vmwareCheck() error {
258269

259270
// NOTE: vmrun doesn't have --help or --version or similar options.
260271
// Without any parameters it exits with status code 255 and help text.
272+
// NOTE: Under Windows vmrun has exist status 4294967295.
261273
cmdName = "vmrun"
262274
result, err = exec.Command(cmdName).CombinedOutput()
263-
if fmt.Sprintf("%s", err) != "exit status 255" {
275+
// TODO: improve this check
276+
if !strings.Contains(fmt.Sprintf("%s", err), "exit status") {
264277
fmt.Println(string(result), err)
265278
return err
266279
}
@@ -317,13 +330,47 @@ func vmwareImportVM(vmxPath string) error {
317330
return nil
318331
}
319332

333+
func checkHyperv() error {
334+
// Powershell is required for Hyper-V.
335+
cmdName := "powershell"
336+
cmdArgs1 := []string{"-Command", "Get-Host"}
337+
if result, err := exec.Command(cmdName, cmdArgs1...).CombinedOutput(); err != nil {
338+
fmt.Println(string(result))
339+
return err
340+
}
341+
fmt.Println("Powershell is present.")
342+
343+
// Check if Hyper-V cmdlets are available.
344+
cmdArgs2 := []string{"-Command", "Get-Command", "-Module", "Hyper-V"}
345+
if result, err := exec.Command(cmdName, cmdArgs2...).CombinedOutput(); err != nil {
346+
fmt.Println(string(result))
347+
return err
348+
}
349+
fmt.Println("Hyper-V Cmdlets are present.")
350+
return nil
351+
}
352+
353+
func hypervImportVM(vmPath string) error {
354+
fmt.Printf("Import '%s'. Please wait.\n", vmPath)
355+
cmdName := "powershell"
356+
cmdArgs1 := []string{"-Command", "Import-VM", "-Path", fmt.Sprintf("'%s'", vmPath)}
357+
if result, err := exec.Command(cmdName, cmdArgs1...).CombinedOutput(); err != nil {
358+
fmt.Println(string(result))
359+
return err
360+
}
361+
// TODO: check if it is possible to fix network for HyperV.
362+
fmt.Println("WARNING: Please check Network adapter settings. By default it isn't connected.")
363+
return nil
364+
}
365+
320366
// InstallVM function installs unpacked VM into a selected hypervisor.
321367
func InstallVM(hypervisor string, vmPath string) {
322-
if hypervisor == "VirtualBox" {
368+
switch hypervisor{
369+
case "VirtualBox":
323370
if err := virtualboxCheck(); err == nil {
324371
virtualboxImportVM(vmPath)
325372
}
326-
} else if hypervisor == "VMware" {
373+
case "VMware":
327374
if err := vmwareCheck(); err != nil {
328375
os.Exit(1)
329376
}
@@ -333,7 +380,11 @@ func InstallVM(hypervisor string, vmPath string) {
333380
}
334381
vmwareFixNetwork(vmxPath)
335382
vmwareImportVM(vmxPath)
336-
} else {
383+
case "HyperV":
384+
if err := checkHyperv(); err == nil {
385+
hypervImportVM(vmPath)
386+
}
387+
default:
337388
fmt.Printf("Hypervisor %s isn't supported.\n", hypervisor)
338389
}
339390
}

0 commit comments

Comments
 (0)