Skip to content

Commit 8cb46ce

Browse files
committed
install: Add --karg-delete
This adds `--karg-delete` to install. In some relatively rare use cases, one might want to remove a kernel argument shipped in the container image. Fixes: #1229 Signed-off-by: Terence Lee <hone02@gmail.com>
1 parent 0018400 commit 8cb46ce

6 files changed

Lines changed: 164 additions & 1 deletion

File tree

crates/lib/src/install.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,12 @@ pub(crate) struct InstallConfigOpts {
345345
#[clap(long)]
346346
pub(crate) karg: Option<Vec<CmdlineOwned>>,
347347

348+
/// Remove a kernel argument. This option can be provided multiple times.
349+
///
350+
/// Example: --karg-delete=nosmt --karg=console=ttyS0,115200n8
351+
#[clap(long)]
352+
pub(crate) karg_delete: Option<Vec<String>>,
353+
348354
/// The path to an `authorized_keys` that will be injected into the `root` account.
349355
///
350356
/// The implementation of this uses systemd `tmpfiles.d`, writing to a file named
@@ -1124,13 +1130,18 @@ async fn install_container(
11241130

11251131
// Keep this in sync with install/completion.rs for the Anaconda fixups
11261132
let install_config_kargs = state.install_config.as_ref().and_then(|c| c.kargs.as_ref());
1133+
let install_config_karg_deletes = state
1134+
.install_config
1135+
.as_ref()
1136+
.and_then(|c| c.karg_deletes.as_ref());
11271137

11281138
// Final kargs, in order:
11291139
// - root filesystem kargs
11301140
// - install config kargs
11311141
// - kargs.d from container image
11321142
// - args specified on the CLI
11331143
let mut kargs = Cmdline::new();
1144+
let mut karg_deletes = Vec::<&str>::new();
11341145

11351146
kargs.extend(&root_setup.kargs);
11361147

@@ -1142,6 +1153,19 @@ async fn install_container(
11421153

11431154
kargs.extend(&kargsd);
11441155

1156+
// delete kargs before processing cli kargs, so cli kargs can override all other configs
1157+
if let Some(install_config_karg_deletes) = install_config_karg_deletes {
1158+
for karg_delete in install_config_karg_deletes {
1159+
karg_deletes.push(karg_delete);
1160+
}
1161+
}
1162+
if let Some(deletes) = state.config_opts.karg_delete.as_ref() {
1163+
for karg_delete in deletes {
1164+
karg_deletes.push(karg_delete);
1165+
}
1166+
}
1167+
delete_kargs(&mut kargs, &karg_deletes);
1168+
11451169
if let Some(cli_kargs) = state.config_opts.karg.as_ref() {
11461170
for karg in cli_kargs {
11471171
kargs.extend(karg);
@@ -1224,6 +1248,18 @@ async fn install_container(
12241248
Ok((deployment, aleph))
12251249
}
12261250

1251+
pub(crate) fn delete_kargs(existing: &mut Cmdline, deletes: &Vec<&str>) {
1252+
for delete in deletes {
1253+
if let Some(param) = utf8::Parameter::parse(&delete) {
1254+
if param.value().is_some() {
1255+
existing.remove_exact(&param);
1256+
} else {
1257+
existing.remove(&param.key());
1258+
}
1259+
}
1260+
}
1261+
}
1262+
12271263
/// Run a command in the host mount namespace
12281264
pub(crate) fn run_in_host_mountns(cmd: &str) -> Result<Command> {
12291265
let mut c = Command::new(bootc_utils::reexec::executable_path()?);
@@ -3085,4 +3121,21 @@ UUID=boot-uuid /boot ext4 defaults 0 0
30853121

30863122
Ok(())
30873123
}
3124+
3125+
#[test]
3126+
fn test_delete_kargs() -> Result<()> {
3127+
let mut cmdline = Cmdline::from("console=tty0 quiet debug nosmt foo=bar foo=baz bar=baz");
3128+
3129+
let deletions = vec!["foo=bar", "bar", "debug"];
3130+
3131+
delete_kargs(&mut cmdline, &deletions);
3132+
3133+
let result = cmdline.to_string();
3134+
assert!(!result.contains("foo=bar"));
3135+
assert!(!result.contains("bar"));
3136+
assert!(!result.contains("debug"));
3137+
assert!(result.contains("foo=baz"));
3138+
3139+
Ok(())
3140+
}
30883141
}

crates/lib/src/install/config.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ pub(crate) struct InstallConfiguration {
104104
/// Kernel arguments, applied at installation time
105105
#[serde(skip_serializing_if = "Option::is_none")]
106106
pub(crate) kargs: Option<Vec<String>>,
107+
/// Deleting Kernel arguments, applied at installation time
108+
#[serde(skip_serializing_if = "Option::is_none")]
109+
pub(crate) karg_deletes: Option<Vec<String>>,
107110
/// Supported architectures for this configuration
108111
pub(crate) match_architectures: Option<Vec<String>>,
109112
/// Ostree repository configuration
@@ -208,6 +211,11 @@ impl Mergeable for InstallConfiguration {
208211
.get_or_insert_with(Default::default)
209212
.extend(other_kargs)
210213
}
214+
if let Some(other_karg_deletes) = other.karg_deletes {
215+
self.karg_deletes
216+
.get_or_insert_with(Default::default)
217+
.extend(other_karg_deletes)
218+
}
211219
}
212220
}
213221
}
@@ -242,6 +250,7 @@ impl InstallConfiguration {
242250
// Remove all configuration which is handled by `install to-filesystem`.
243251
pub(crate) fn filter_to_external(&mut self) {
244252
self.kargs.take();
253+
self.karg_deletes.take();
245254
}
246255

247256
#[cfg(feature = "install-to-disk")]
@@ -346,6 +355,7 @@ root-fs-type = "xfs"
346355
r##"[install]
347356
root-fs-type = "ext4"
348357
kargs = ["console=ttyS0", "foo=bar"]
358+
karg-deletes = ["debug", "bar=baz"]
349359
"##,
350360
)
351361
.unwrap();
@@ -359,6 +369,12 @@ kargs = ["console=ttyS0", "foo=bar"]
359369
.map(ToOwned::to_owned)
360370
.collect(),
361371
),
372+
karg_deletes: Some(
373+
["baz", "bar=baz"]
374+
.into_iter()
375+
.map(ToOwned::to_owned)
376+
.collect(),
377+
),
362378
..Default::default()
363379
}),
364380
};
@@ -372,7 +388,16 @@ kargs = ["console=ttyS0", "foo=bar"]
372388
.map(ToOwned::to_owned)
373389
.collect()
374390
)
375-
)
391+
);
392+
assert_eq!(
393+
install.karg_deletes,
394+
Some(
395+
["debug", "bar=baz", "baz", "bar=baz"]
396+
.into_iter()
397+
.map(ToOwned::to_owned)
398+
.collect()
399+
)
400+
);
376401
}
377402

378403
#[test]

crates/tests-integration/src/install.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,26 @@ pub(crate) fn run_alongside(image: &str, mut testargs: libtest_mimic::Arguments)
169169
generic_post_install_verification()?;
170170
Ok(())
171171
}),
172+
Trial::test("install with --karg-delete", move || {
173+
let sh = &xshell::Shell::new()?;
174+
reset_root(sh, image)?;
175+
cmd!(sh, "sudo {BASE_ARGS...} {target_args...} {image} bootc install to-filesystem --acknowledge-destructive --karg-delete=localtestkarg --replace=alongside /target").run()?;
176+
cmd!(
177+
sh,
178+
"sudo {BASE_ARGS...} {target_args...} {image} bootc install finalize /target"
179+
)
180+
.run()?;
181+
let entries = cmd!(
182+
sh,
183+
"sudo /bin/sh -c 'grep localtestkarg /boot/loader/entries/*.conf'"
184+
)
185+
.ignore_status()
186+
.read()?;
187+
188+
assert!(entries.is_empty());
189+
190+
Ok(())
191+
}),
172192
];
173193

174194
libtest_mimic::run(&testargs, tests.into()).exit()

tmt/plans/integration.fmf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,11 @@ execute:
230230
how: fmf
231231
test:
232232
- /tmt/tests/tests/test-38-install-bootloader-none
233+
234+
/plan-39-install-karg-delete:
235+
summary: Test bootc install --karg-delete
236+
discover:
237+
how: fmf
238+
test:
239+
- /tmt/tests/tests/test-39-install-karg-delete
233240
# END GENERATED PLANS
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# number: 39
2+
# tmt:
3+
# summary: Test bootc install --karg-delete
4+
# duration: 30m
5+
#
6+
use std assert
7+
use tap.nu
8+
#
9+
# Use an OS-matched target image to avoid version mismatches
10+
let target_image = (tap get_target_image)
11+
12+
# setup filesystem
13+
mkdir /var/mnt
14+
truncate -s 10G disk.img
15+
mkfs.ext4 disk.img
16+
mount -o loop disk.img /var/mnt
17+
18+
# Mask off the bootupd state to reproduce https://github.com/bootc-dev/bootc/issues/1778
19+
# Also it turns out that installation outside of containers dies due to `error: Multiple commit objects found`
20+
# so we mask off /sysroot/ostree
21+
# And using systemd-run here breaks our install_t so we disable SELinux enforcement
22+
setenforce 0
23+
24+
mkdir /etc/bootc/install
25+
{ install: { kargs: ["foo=bar"] } } | to toml | save /etc/bootc/install/99-test.toml
26+
27+
let base_args = $"bootc install to-filesystem --disable-selinux --source-imgref ($target_image) --karg-delete localtestkarg --karg-delete foo"
28+
let install_cmd = if (tap is_composefs) {
29+
let st = bootc status --json | from json
30+
let bootloader = ($st.status.booted.composefs.bootloader | str downcase)
31+
$"($base_args) --composefs-backend --bootloader=($bootloader) /var/mnt"
32+
} else {
33+
$"($base_args) --bootloader none /var/mnt"
34+
}
35+
36+
tap run_install $install_cmd
37+
38+
# Verify the karg is gone from the bootloader entries
39+
let entries = (glob /var/mnt/boot/loader/entries/*.conf
40+
| each { open $in | lines }
41+
| flatten)
42+
43+
let localtestkarg_found = ($entries | find "localtestkarg" | is-empty)
44+
assert $localtestkarg_found "Found localtestkarg in bootloader entries, but it should have been deleted"
45+
46+
let foo_found = ($entries | find "foo" | is-empty)
47+
assert $foo_found "Found foo in bootloader entries, but it should have been deleted"
48+
49+
# Clean up
50+
umount /var/mnt
51+
rm -rf disk.img
52+
53+
tap ok

tmt/tests/tests.fmf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,8 @@
131131
summary: Test bootc install with --bootloader=none
132132
duration: 30m
133133
test: nu booted/test-install-bootloader-none.nu
134+
135+
/test-39-install-karg-delete:
136+
summary: Test bootc install --karg-delete
137+
duration: 30m
138+
test: nu booted/test-install-karg-delete.nu

0 commit comments

Comments
 (0)