Skip to content

Commit 9799cd9

Browse files
committed
Support overriding config's command through CLI args
Commit ab25958 added support for oneliners, but it marked the 'command' arg as conflicting with the 'config' arg. This prevents using a config file with `go run|test -exec`. Moreover, having a way to override the command defined in the config file gives an easy way to exec into a VM to manually debug a failing command. Signed-off-by: Albin Kerouanton <[email protected]>
1 parent 80cd7b5 commit 9799cd9

File tree

5 files changed

+42
-31
lines changed

5 files changed

+42
-31
lines changed

src/config.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ pub struct Target {
114114
/// Command used to launch QEMU
115115
pub qemu_command: Option<String>,
116116
/// Command to run inside virtual machine.
117-
pub command: String,
117+
pub command: Option<String>,
118118

119119
/// VM Configuration.
120120
#[serde(default)]
@@ -143,7 +143,7 @@ impl Default for Target {
143143
rootfs: Self::default_rootfs(),
144144
arch: Self::default_arch(),
145145
qemu_command: None,
146-
command: "".into(),
146+
command: Some("".into()),
147147
vm: VMConfig::default(),
148148
}
149149
}
@@ -171,7 +171,7 @@ fn test_triple_quoted_strings_are_literal() {
171171

172172
assert_eq!(
173173
config.target[0].command,
174-
r#"this string has 'single' and "double" quotes"#
174+
Some(r#"this string has 'single' and "double" quotes"#.into())
175175
);
176176
}
177177

@@ -189,7 +189,7 @@ fn test_triple_quoted_strings_backslash() {
189189

190190
assert_eq!(
191191
config.target[0].command,
192-
r#"this string has \back \slash\es"#
192+
Some(r#"this string has \back \slash\es"#.into())
193193
);
194194
}
195195

src/main.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ struct Args {
4545
#[clap(short, long, conflicts_with = "config")]
4646
qemu_command: Option<String>,
4747
/// Command to run in kernel mode. `-` to get an interactive shell.
48-
#[clap(conflicts_with = "config")]
48+
#[clap()]
4949
command: Vec<String>,
5050
}
5151

@@ -119,7 +119,7 @@ fn config(args: &Args) -> Result<Vmtest> {
119119
arch: args.arch.clone(),
120120
kernel_args: args.kargs.clone(),
121121
qemu_command: args.qemu_command.clone(),
122-
command: args.command.join(" "),
122+
command: Some(args.command.join(" ")),
123123
vm: VMConfig::default(),
124124
}],
125125
};
@@ -135,6 +135,13 @@ fn config(args: &Args) -> Result<Vmtest> {
135135
.target
136136
.into_iter()
137137
.filter(|t| filter.is_match(&t.name))
138+
.map(|t| {
139+
let mut t = t;
140+
if !args.command.is_empty() {
141+
t.command = Some(args.command.join(" "));
142+
}
143+
t
144+
})
138145
.collect::<Vec<_>>();
139146
let base = config_path.parent().unwrap();
140147
Vmtest::new(base, config)

src/qemu.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,7 @@ impl Qemu {
719719
process: c,
720720
qga_sock,
721721
qmp_sock,
722-
command: target.command,
722+
command: target.command.unwrap_or_default(),
723723
command_sock,
724724
host_shared: host_shared.to_owned(),
725725
rootfs: target.rootfs,

src/vmtest.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ fn validate_config(config: &Config) -> Result<()> {
6868
}
6969
}
7070

71-
if target.command.is_empty() {
71+
if match &target.command {
72+
Some(command) => command.is_empty(),
73+
None => true,
74+
} {
7275
bail!("Target '{}' has empty command", target.name);
7376
}
7477
}

tests/test.rs

+24-23
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ fn test_run() {
2424
name: "uefi image boots with uefi flag".to_string(),
2525
image: Some(asset("image-uefi.raw-efi")),
2626
uefi: true,
27-
command: "/mnt/vmtest/main.sh nixos".to_string(),
27+
command: Some("/mnt/vmtest/main.sh nixos".to_string()),
2828
..Default::default()
2929
},
3030
Target {
3131
name: "not uefi image boots without uefi flag".to_string(),
3232
image: Some(asset("image-not-uefi.raw")),
3333
uefi: false,
34-
command: "/mnt/vmtest/main.sh nixos".to_string(),
34+
command: Some("/mnt/vmtest/main.sh nixos".to_string()),
3535
..Default::default()
3636
},
3737
],
@@ -51,21 +51,21 @@ fn test_run_multiple_return_number_failures() {
5151
name: "uefi image boots with uefi flag".to_string(),
5252
image: Some(asset("image-uefi.raw-efi")),
5353
uefi: true,
54-
command: "exit 1".to_string(),
54+
command: Some("exit 1".to_string()),
5555
..Default::default()
5656
},
5757
Target {
5858
name: "uefi image boots with uefi flag 2".to_string(),
5959
image: Some(asset("image-uefi.raw-efi")),
6060
uefi: true,
61-
command: "exit 1".to_string(),
61+
command: Some("exit 1".to_string()),
6262
..Default::default()
6363
},
6464
Target {
6565
name: "not uefi image boots without uefi flag".to_string(),
6666
image: Some(asset("image-not-uefi.raw")),
6767
uefi: false,
68-
command: "/mnt/vmtest/main.sh nixos".to_string(),
68+
command: Some("/mnt/vmtest/main.sh nixos".to_string()),
6969
..Default::default()
7070
},
7171
],
@@ -84,7 +84,7 @@ fn test_run_single_return_number_return_code() {
8484
name: "not uefi image boots without uefi flag".to_string(),
8585
image: Some(asset("image-not-uefi.raw")),
8686
uefi: false,
87-
command: "exit 12".to_string(),
87+
command: Some("exit 12".to_string()),
8888
..Default::default()
8989
}],
9090
};
@@ -102,7 +102,7 @@ fn test_vmtest_infra_error() {
102102
name: "not an actual image, should return EX_UNAVAILABLE".to_string(),
103103
image: Some(asset("not_an_actual_image")),
104104
uefi: false,
105-
command: "exit 12".to_string(),
105+
command: Some("exit 12".to_string()),
106106
..Default::default()
107107
}],
108108
};
@@ -124,14 +124,14 @@ fn test_run_one() {
124124
name: "uefi image boots with uefi flag".to_string(),
125125
image: Some(uefi_image.as_pathbuf()),
126126
uefi: true,
127-
command: "/mnt/vmtest/main.sh nixos".to_string(),
127+
command: Some("/mnt/vmtest/main.sh nixos".to_string()),
128128
..Default::default()
129129
},
130130
Target {
131131
name: "not uefi image boots without uefi flag".to_string(),
132132
image: Some(non_uefi_image.as_pathbuf()),
133133
uefi: false,
134-
command: "/mnt/vmtest/main.sh nixos".to_string(),
134+
command: Some("/mnt/vmtest/main.sh nixos".to_string()),
135135
..Default::default()
136136
},
137137
],
@@ -156,14 +156,14 @@ fn test_run_out_of_bounds() {
156156
name: "uefi image boots with uefi flag".to_string(),
157157
image: Some(uefi_image.as_pathbuf()),
158158
uefi: true,
159-
command: "/mnt/vmtest/main.sh nixos".to_string(),
159+
command: Some("/mnt/vmtest/main.sh nixos".to_string()),
160160
..Default::default()
161161
},
162162
Target {
163163
name: "not uefi image boots without uefi flag".to_string(),
164164
image: Some(non_uefi_image.as_pathbuf()),
165165
uefi: false,
166-
command: "/mnt/vmtest/main.sh nixos".to_string(),
166+
command: Some("/mnt/vmtest/main.sh nixos".to_string()),
167167
..Default::default()
168168
},
169169
],
@@ -184,7 +184,7 @@ fn test_not_uefi() {
184184
name: "uefi image does not boot without uefi flag".to_string(),
185185
image: Some(uefi_image.as_pathbuf()),
186186
uefi: false,
187-
command: "echo unreachable".to_string(),
187+
command: Some("echo unreachable".to_string()),
188188
..Default::default()
189189
}],
190190
};
@@ -202,7 +202,7 @@ fn test_command_runs_in_shell() {
202202
kernel: Some(asset("bzImage-v5.15-default")),
203203
// `$0` is a portable way of getting the name of the shell without relying
204204
// on env vars which may be propagated from the host into the guest.
205-
command: "if true; then echo -n $0 > /mnt/vmtest/result; fi".to_string(),
205+
command: Some("if true; then echo -n $0 > /mnt/vmtest/result; fi".to_string()),
206206
..Default::default()
207207
}],
208208
};
@@ -260,7 +260,7 @@ fn test_kernel_target_env_var_propagation() {
260260
target: vec![Target {
261261
name: "host env vars are propagated into guest".to_string(),
262262
kernel: Some(asset("bzImage-v5.15-default")),
263-
command: "echo -n $TEST_ENV_VAR > /mnt/vmtest/result".to_string(),
263+
command: Some("echo -n $TEST_ENV_VAR > /mnt/vmtest/result".to_string()),
264264
..Default::default()
265265
}],
266266
};
@@ -286,7 +286,7 @@ fn test_kernel_target_cwd_preserved() {
286286
target: vec![Target {
287287
name: "host cwd preserved in guest".to_string(),
288288
kernel: Some(asset("bzImage-v5.15-default")),
289-
command: "cat text_file.txt".to_string(),
289+
command: Some("cat text_file.txt".to_string()),
290290
..Default::default()
291291
}],
292292
};
@@ -314,7 +314,7 @@ fn test_command_process_substitution() {
314314
kernel: Some(asset("bzImage-v5.15-default")),
315315
// `$0` is a portable way of getting the name of the shell without relying
316316
// on env vars which may be propagated from the host into the guest.
317-
command: "cat <(echo -n $0) > /mnt/vmtest/result".to_string(),
317+
command: Some("cat <(echo -n $0) > /mnt/vmtest/result".to_string()),
318318
..Default::default()
319319
}],
320320
};
@@ -335,7 +335,7 @@ fn test_qemu_error_shown() {
335335
target: vec![Target {
336336
name: "invalid kernel path".to_string(),
337337
kernel: Some(asset("doesn't exist")),
338-
command: "true".to_string(),
338+
command: Some("true".to_string()),
339339
..Default::default()
340340
}],
341341
};
@@ -360,7 +360,7 @@ fn test_kernel_ro_flag() {
360360
name: "cannot touch host rootfs with ro".to_string(),
361361
kernel: Some(asset("bzImage-v5.15-default")),
362362
kernel_args: Some("ro".to_string()),
363-
command: format!("touch {}/file", touch_dir.path().display()),
363+
command: Some(format!("touch {}/file", touch_dir.path().display())),
364364
..Default::default()
365365
}],
366366
};
@@ -381,7 +381,7 @@ fn test_run_custom_resources() {
381381
name: "Custom number of CPUs".to_string(),
382382
image: Some(uefi_image_t1.as_pathbuf()),
383383
uefi: true,
384-
command: r#"bash -xc "[[ "$(nproc)" == "1" ]]""#.into(),
384+
command: Some(r#"bash -xc "[[ "$(nproc)" == "1" ]]""#.into()),
385385
vm: VMConfig {
386386
num_cpus: 1,
387387
..Default::default()
@@ -393,8 +393,9 @@ fn test_run_custom_resources() {
393393
image: Some(uefi_image_t2.as_pathbuf()),
394394
uefi: true,
395395
// Should be in the 200 thousands, but it's variable.
396-
command: r#"bash -xc "cat /proc/meminfo | grep 'MemTotal: 2..... kB'""#
397-
.into(),
396+
command: Some(
397+
r#"bash -xc "cat /proc/meminfo | grep 'MemTotal: 2..... kB'""#.into(),
398+
),
398399
vm: VMConfig {
399400
memory: "256M".into(),
400401
..Default::default()
@@ -421,7 +422,7 @@ fn test_run_custom_mounts() {
421422
name: "mount".to_string(),
422423
image: Some(uefi_image.as_pathbuf()),
423424
uefi: true,
424-
command: r#"bash -xc "[[ -e /tmp/mount/README.md ]]""#.into(),
425+
command: Some(r#"bash -xc "[[ -e /tmp/mount/README.md ]]""#.into()),
425426
vm: VMConfig {
426427
mounts: HashMap::from([(
427428
"/tmp/mount".into(),
@@ -438,7 +439,7 @@ fn test_run_custom_mounts() {
438439
name: "RO mount".to_string(),
439440
image: Some(uefi_image.as_pathbuf()),
440441
uefi: true,
441-
command: r#"bash -xc "(touch /tmp/ro/hi && exit -1) || true""#.into(),
442+
command: Some(r#"bash -xc "(touch /tmp/ro/hi && exit -1) || true""#.into()),
442443
vm: VMConfig {
443444
mounts: HashMap::from([(
444445
"/tmp/ro".into(),

0 commit comments

Comments
 (0)