forked from xdissent/ievms
-
Notifications
You must be signed in to change notification settings - Fork 5
/
ievms-node.sh
executable file
·394 lines (333 loc) · 13 KB
/
ievms-node.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
#!/usr/bin/env bash
# Caution is a virtue.
set -o nounset
set -o errtrace
set -o errexit
set -o pipefail
# ## Global Variables
# The ievms version.
ievms_version="0.3.3"
# Options passed to each `curl` command.
curl_opts=${CURL_OPTS:-""}
# Reuse Win7 virtual machines for IE versions that are supported.
reuse_win7=${REUSE_WIN7:-"no"}
# Timeout interval to wait between checks for various states.
sleep_wait="5"
# Time to wait for OS to boot before sending other commands.
os_boot_wait="20"
# ## Utilities
# Print a message to the console.
log() { printf '%s\n' "$*" ; return $? ; }
# Print an error message to the console and bail out of the script.
fail() { log "ERROR: $*\n" ; exit 1 ; }
check_md5() { #1 = path, 2 = md5
local md5
case $kernel in
Darwin) md5=`md5 "${1}" | rev | cut -c-32 | rev` ;;
Linux) md5=`md5sum "${1}" | cut -c-32` ;;
esac
if [ "${md5}" != "${2}" ]
then
log "MD5 check failed for ${1} (wanted ${2}, got ${md5})"
return 1
fi
log "MD5 check succeeded for ${1}"
}
# Download a URL to a local file. Accepts a name, URL and file.
download() { # name url path md5
local attempt=${5:-"0"}
local max=${6:-"3"}
let attempt+=1
if [[ -f "${3}" ]]
then
log "Found ${1} at ${3} - skipping download"
check_md5 "${3}" "${4}" && return 0
log "Check failed - redownloading ${1}"
rm -f "${3}"
fi
log "Downloading ${1} from ${2} to ${3} (attempt ${attempt} of ${max})"
curl -O "${2}" || fail "Failed to download ${2} to ${ievms_home}/${3} using 'curl', error code ($?)"
check_md5 "${3}" "${4}" && return 0
if [ "${attempt}" == "${max}" ]
then
echo "Failed to download ${2} to ${ievms_home}/${3} (attempt ${attempt} of ${max})"
return 1
fi
log "Redownloading ${1}"
download "${1}" "${2}" "${3}" "${4}" "${attempt}" "${max}"
}
# ## General Setup
# Create the ievms home folder and `cd` into it. The `INSTALL_PATH` env variable
# is used to determine the full path. The home folder is then added to `PATH`.
create_home() {
local def_ievms_home="${HOME}/.ievms"
ievms_home=${INSTALL_PATH:-$def_ievms_home}
mkdir -p "${ievms_home}"
cd "${ievms_home}"
PATH="${PATH}:${ievms_home}"
# Move ovas and zips from a very old installation into place.
mv -f ./ova/IE*/IE*.{ova,zip} "${ievms_home}/" 2>/dev/null || true
}
# Check for a supported host system (Linux/OS X).
check_system() {
kernel=`uname -s`
case $kernel in
Darwin|Linux) ;;
*) fail "Sorry, $kernel is not supported." ;;
esac
}
# Ensure VirtualBox is installed and `VBoxManage` is on the `PATH`.
check_virtualbox() {
log "Checking for VirtualBox"
hash VBoxManage 2>&- || fail "VirtualBox command line utilities are not installed, please (re)install! (http://virtualbox.org)"
}
# Determine the VirtualBox version details, querying the download page to ensure
# validity.
check_version() {
local version=`VBoxManage -v`
major_minor_release="${version%%[-_r]*}"
local major_minor="${version%.*}"
local dl_page=`curl ${curl_opts} -L "http://download.virtualbox.org/virtualbox/" 2>/dev/null`
if [[ "$version" == *"kernel module is not loaded"* ]]; then
fail "$version"
fi
for (( release="${major_minor_release#*.*.}"; release >= 0; release-- ))
do
major_minor_release="${major_minor}.${release}"
if echo $dl_page | grep "${major_minor_release}/" &>/dev/null
then
log "Virtualbox version ${major_minor_release} found."
break
else
log "Virtualbox version ${major_minor_release} not found, skipping."
fi
done
}
# Check for the VirtualBox Extension Pack and install if not found.
check_ext_pack() {
log "Checking for Oracle VM VirtualBox Extension Pack"
if ! VBoxManage list extpacks | grep "Oracle VM VirtualBox Extension Pack"
then
check_version
local archive="Oracle_VM_VirtualBox_Extension_Pack-${major_minor_release}.vbox-extpack"
local url="http://download.virtualbox.org/virtualbox/${major_minor_release}/${archive}"
local md5s="https://www.virtualbox.org/download/hashes/${major_minor_release}/MD5SUMS"
local md5=`curl ${curl_opts} -L "${md5s}" | grep "${archive}" | cut -c-32`
download "Oracle VM VirtualBox Extension Pack" "${url}" "${archive}" "${md5}"
log "Installing Oracle VM VirtualBox Extension Pack from ${ievms_home}/${archive}"
VBoxManage extpack install "${archive}" || fail "Failed to install Oracle VM VirtualBox Extension Pack from ${ievms_home}/${archive}, error code ($?)"
fi
}
# Download and install `unar` from Google Code.
install_unar() {
local url="https://cdn.theunarchiver.com/downloads/unarMac.zip"
local archive=`basename "${url}"`
download "unar" "${url}" "${archive}" "91796924b1b21ee586ed904b319bb447"
unzip "${archive}" || fail "Failed to extract ${ievms_home}/${archive} to ${ievms_home}/, unzip command returned error code $?"
hash unar 2>&- || fail "Could not find unar in ${ievms_home}"
}
# Check for the `unar` command, downloading and installing it if not found.
check_unar() {
if [ "${kernel}" == "Darwin" ]
then
hash unar 2>&- || install_unar
else
hash unar 2>&- || fail "Linux support requires unar (sudo apt-get install for Ubuntu/Debian)"
fi
}
# Pause execution until the virtual machine with a given name shuts down.
wait_for_shutdown() {
if [ "${1}" == "IE11 - Win81" ]
then
log "Win81 takes some extra time to boot on first run..."
log "Guest Additions will be attached, but you will have to manually install on first run"
os_boot_wait="45"
sleep "${os_boot_wait}"
VBoxManage controlvm "${1}" acpipowerbutton
else
sleep "${os_boot_wait}"
VBoxManage controlvm "${1}" acpipowerbutton
fi
while true ; do
log "Waiting for ${1} to shutdown..."
sleep "${sleep_wait}"
VBoxManage showvminfo "${1}" | grep "State:" | grep -q "powered off" && sleep "${sleep_wait}" && return 0 || true
done
}
# Attach a dvd image to the virtual machine.
attach() {
#VirtualBox currently errors when attempting to attach to Win8.1 with 'additions' command passed in, this is a workaround
if [ "${1}" == "IE11 - Win81" ]
then
if [ "${kernel}" == "Darwin" ]
then
log "Copying Guest Additions iso to ${ievms_home}"
cp /Applications/VirtualBox.app/Contents/MacOS/VBoxGuestAdditions.iso "${ievms_home}/"
log "Attaching ${3}"
VBoxManage storageattach "${1}" --storagectl "IDE Controller" --port 0 \
--device 1 --type dvddrive --medium "${ievms_home}/VBoxGuestAdditions.iso"
fi
if [ "${kernel}" == "Linux" ]
then
log "Copying Guest Additions iso to ${ievms_home}"
cp /usr/share/virtualbox/VBoxGuestAdditions.iso "${ievms_home}/"
log "Attaching ${3}"
VBoxManage storageattach "${1}" --storagectl "IDE Controller" --port 0 \
--device 1 --type dvddrive --medium "${ievms_home}/VBoxGuestAdditions.iso"
fi
else
log "Attaching ${3}"
VBoxManage storageattach "${1}" --storagectl "IDE Controller" --port 0 \
--device 1 --type dvddrive --medium "${2}"
fi
}
# Eject the dvd image from the virtual machine.
eject() {
log "Ejecting ${2}"
VBoxManage modifyvm "${1}" --dvd none
}
# Boot the virtual machine with guest additions in the dvd drive. After running
# `boot_ievms`, the next boot will attempt automatically install guest additions
# if present in the drive. It will shut itself down after installation.
boot_auto_ga() {
attach "${1}" "additions" "Guest Additions"
start_vm "${1}"
wait_for_shutdown "${1}"
}
# Start a virtual machine in headless mode.
start_vm() {
log "Starting VM ${1}"
VBoxManage startvm "${1}" --type headless
}
# Build an ievms virtual machine given the IE version desired.
build_ievm() {
unset archive
unset unit
local prefix="IE"
local version="${1}"
case $1 in
8|9|10) os="Win7" ;;
11)
if [ "${reuse_win7}" != "yes" ]
then
if [ "$1" == "11" ]
then
os="Win81"
unit="8"
fi
else
os="Win7"
archive="IE11.Win7.VirtualBox.zip"
fi
;;
EDGE)
prefix="MS"
version="Edge"
os="Win10"
unit="8"
;;
*) fail "Invalid IE version: ${1}" ;;
esac
local vm="${prefix}${version} - ${os}"
local def_archive="${vm/ - /.}.VirtualBox.zip"
archive=${archive:-$def_archive}
unit=${unit:-"9"}
local ova="`basename "${archive/./ - }" .VirtualBox.zip`.ova"
log "Checking for existing OVA at ${ievms_home}/${ova}"
local url
local list_version
local get_md5
if [[ ! -f "${ova}" ]]
then
case $vm in
'IE8 - Win7') list_version="0";;
'IE9 - Win7') list_version="1";;
'IE10 - Win7') list_version="2";;
'IE11 - Win7') list_version="3";;
'IE11 - Win81') list_version="4";;
'MSEdge - Win10') list_version="5";;
esac
# JSON output is different for Edge
if [ "${list_version}" == "5" ]
then
url=$(node -pe "JSON.parse(process.argv[1])["${list_version}"].software[0].files[0].url" "$(curl -s https://developer.microsoft.com/en-us/microsoft-edge/api/tools/vms/)")
get_md5=$(node -pe "JSON.parse(process.argv[1])[5].software[0].files[0].md5" "$(curl -s https://developer.microsoft.com/en-us/microsoft-edge/api/tools/vms/)")
log "Grabbing md5 file to parse for md5 string"
md5=$(curl "${get_md5}" | tr '[:upper:]' '[:lower:]')
else
url=$(node -pe "JSON.parse(process.argv[1])["${list_version}"].software[0].files[1].url" "$(curl -s https://developer.microsoft.com/en-us/microsoft-edge/api/tools/vms/)")
# md5 url is incorrect on the api itself for every option but Edge
get_md5=$(node -pe "JSON.parse(process.argv[1])["${list_version}"].software[0].files[1].md5.slice(0, 31).concat('vms', JSON.parse(process.argv[1])["${list_version}"].software[0].files[1].md5.slice(34,))" "$(curl -s https://developer.microsoft.com/en-us/microsoft-edge/api/tools/vms/)")
log "Grabbing md5 file to parse for md5 string"
md5=$(curl "${get_md5}" | tr '[:upper:]' '[:lower:]')
fi
# md5 link is broken in MS API for Win81 version of IE11, insert hard-coded workaround here:
if [ "${list_version}" == "4" ]
then
download "OVA ZIP" "${url}" "${archive}" "896db7a54336982241d25f704f35d6c2"
else
download "OVA ZIP" "${url}" "${archive}" "${md5}"
fi
log "Extracting OVA from ${ievms_home}/${archive}"
unar "${archive}" || fail "Failed to extract ${archive} to ${ievms_home}/${ova}, unar command returned error code $?"
fi
log "Checking for existing ${vm} VM"
if ! VBoxManage showvminfo "${vm}" >/dev/null 2>/dev/null
then
local disk_path="${vm}-disk1.vmdk"
log "Creating ${vm} VM (disk: ${disk_path})"
if [ "${vm}" == "IE11 - Win81" ]
then
VBoxManage import "${ova}" --vsys 0 --vmname "${vm}" --unit "${unit}" --disk "${disk_path}"
VBoxManage modifyvm "IE11 - Win81" --vram "128"
else
VBoxManage import "${ova}" --vsys 0 --vmname "${vm}" --unit "${unit}" --disk "${disk_path}"
fi
log "Adding shared folder"
VBoxManage sharedfolder add "${vm}" --automount --name ievms \
--hostpath "${ievms_home}"
log "Building ${vm} VM"
declare -F "build_ievm_ie${1}" && "build_ievm_ie${1}"
log "Tagging VM with ievms version"
VBoxManage setextradata "${vm}" "ievms" "{\"version\":\"${ievms_version}\"}"
log "Creating clean snapshot"
VBoxManage snapshot "${vm}" take clean --description "The initial VM state"
fi
}
# Build the IE8 virtual machine, reusing the XP VM if requested (the default).
build_ievm_ie8() {
boot_auto_ga "IE8 - Win7"
}
# Build the IE9 virtual machine.
build_ievm_ie9() {
boot_auto_ga "IE9 - Win7"
}
# Build the IE10 virtual machine, reusing the Win7 VM if requested (the default).
build_ievm_ie10() {
boot_auto_ga "IE10 - Win7"
}
# Build the IE11 virtual machine, reusing the Win7 VM always.
build_ievm_ie11() {
if [ "${reuse_win7}" != "yes" ]
then
boot_auto_ga "IE11 - Win81"
else
boot_auto_ga "IE11 - Win7"
fi
}
# ## Main Entry Point
# Run through all checks to get the host ready for installation.
check_system
create_home
check_virtualbox
check_ext_pack
check_unar
# Install each requested virtual machine sequentially.
all_versions="8 9 10 11 EDGE"
for ver in ${IEVMS_VERSIONS:-$all_versions}
do
log "Building IE ${ver} VM"
build_ievm $ver
done
# We made it!
log "Done!"