-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathvmtest.js
175 lines (153 loc) · 5.76 KB
/
vmtest.js
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
const core = require('@actions/core');
const exec = require('@actions/exec');
const io = require('@actions/io');
const tc = require('@actions/tool-cache');
const fs = require('fs/promises');
const fsSync = require('fs');
const path = require('path');
// Validate input parameters. Throws an exception on error.
//
// Note we only validate vmtest-action provided parameters. We leave
// the remaining validation to vmtest itself.
async function validateArgs(args) {
if (args.kernel.length && args.kernel_url.length) {
throw new Error('Cannot specify both kernel and kernel_url');
}
if (args.image.length && args.image_url.length) {
throw new Error('Cannot specify both image and image_url');
}
}
// Check if the current runner is ubuntu. If not, throws an exception
async function checkOnUbuntu(osRelease) {
const data = await fs.readFile(osRelease, {
encoding: 'utf8'
});
const lines = data.toString().split('\n');
for (var i = 0; i < lines.length; i++) {
if (lines[i].length == 0) {
continue;
}
var parts = lines[i].split('=');
if (parts.length != 2) {
throw new Error(`Invalid line in ${osRelease}: ${lines[i]}`);
}
if (parts[0] == 'ID' && parts[1] == 'ubuntu') {
return;
}
}
throw new Error('This action only works on Ubuntu runners');
}
// Configure KVM to be usable from this job.
//
// See: https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/
async function configureKvm() {
// Only try to configure KVM on host with KVM available
if (!fsSync.existsSync('/dev/kvm')) {
return;
}
await exec.exec(
'/bin/bash',
['-c', `echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules`],
);
await exec.exec('sudo udevadm control --reload-rules')
await exec.exec('sudo udevadm trigger --name-match=kvm')
}
// Install staticically linked vmtest binary
async function installVmtest() {
// We infer the vmtest version to install from the Cargo manifest,
// which is Dependabot managed.
const manifestPath = path.join(__dirname, 'Cargo.toml');
const data = await fs.readFile(manifestPath, {
encoding: 'utf8'
});
const lines = data.trim().split('\n');
const lastLine = lines[lines.length - 1].trim();
const vmtestVersion = lastLine.split('=')[1].trim().replace(/"/g, '');
const downloadPath = await tc.downloadTool(`https://github.com/danobi/vmtest/releases/download/v${vmtestVersion}/vmtest-x86_64`);
await fs.chmod(downloadPath, '755');
await io.cp(downloadPath, '/usr/local/bin/vmtest');
}
async function installPackages() {
await exec.exec('sudo apt-get update');
await exec.exec('sudo apt-get install -y qemu-system-x86-64 qemu-guest-agent ovmf');
}
// Install required vmtest dependencies
async function installDependencies() {
await Promise.all([installVmtest(), installPackages()]);
}
async function materializeConfig(args, configFile, downloadedAssetPath) {
var lines = [];
lines.push('[[target]]');
lines.push(`name = "${args.name}"`);
if (args.image.length) {
lines.push(`image = "${args.image}"`);
} else if (args.image_url.length) {
lines.push(`image = "${downloadedAssetPath}"`);
}
if (args.uefi.toLowerCase() == 'true') {
lines.push('uefi = true');
}
if (args.kernel.length) {
lines.push(`kernel = "${args.kernel}"`);
} else if (args.kernel_url.length) {
lines.push(`kernel = "${downloadedAssetPath}"`);
}
if (args.kernel_args.length) {
lines.push(`kernel_args = "${args.kernel_args}"`);
}
// Triple-quoting the command here is necessary to avoid any parsing
// or escaping issues. We don't want the user command to accidentally
// terminate our string. We also don't want anything to be accidentally
// escaped.
//
// See: https://toml.io/en/v0.3.0#string
lines.push(`command = '''${args.command}'''`);
var contents = lines.join('\n');
await fs.writeFile(configFile, contents);
}
// Download image/kernel asset if necessary and then materialize vmtest config
async function generateConfig(args, configFile) {
var downloadPath = null;
if (args.image_url.length) {
downloadPath = await tc.downloadTool(args.image_url);
} else if (args.kernel_url.length) {
downloadPath = await tc.downloadTool(args.kernel_url);
}
await materializeConfig(args, configFile, downloadPath);
}
async function runVmtest(configFile) {
core.debug(`running vmtest with config file: ${configFile}`);
await exec.exec(`vmtest --config ${configFile}`);
}
async function main() {
var args = {
name: core.getInput('name'),
image: core.getInput('image'),
image_url: core.getInput('image_url'),
uefi: core.getInput('uefi'),
kernel: core.getInput('kernel'),
kernel_url: core.getInput('kernel_url'),
kernel_args: core.getInput('kernel_args'),
command: core.getInput('command'),
};
// Start a collapsable log group
core.startGroup("Install vmtest");
core.debug(`args=${JSON.stringify(args)}`);
await validateArgs(args);
// Can run these in parallel
var check = checkOnUbuntu('/etc/os-release');
var configKvm = configureKvm();
var install = installDependencies();
var generate = generateConfig(args, './vmtest.toml');
await Promise.all([check, configKvm, install, generate]);
// End log group
core.endGroup();
// Once above tasks complete, we can run vmtest
await runVmtest('./vmtest.toml');
}
module.exports = {
validateArgs,
checkOnUbuntu,
materializeConfig,
main
};