diff --git a/crates/lib/src/install.rs b/crates/lib/src/install.rs index 6d0dcc607..a20a949af 100644 --- a/crates/lib/src/install.rs +++ b/crates/lib/src/install.rs @@ -345,6 +345,12 @@ pub(crate) struct InstallConfigOpts { #[clap(long)] pub(crate) karg: Option>, + /// Remove a kernel argument. This option can be provided multiple times. + /// + /// Example: --karg-delete=nosmt --karg=console=ttyS0,115200n8 + #[clap(long)] + pub(crate) karg_delete: Option>, + /// The path to an `authorized_keys` that will be injected into the `root` account. /// /// The implementation of this uses systemd `tmpfiles.d`, writing to a file named @@ -1124,6 +1130,10 @@ async fn install_container( // Keep this in sync with install/completion.rs for the Anaconda fixups let install_config_kargs = state.install_config.as_ref().and_then(|c| c.kargs.as_ref()); + let install_config_karg_deletes = state + .install_config + .as_ref() + .and_then(|c| c.karg_deletes.as_ref()); // Final kargs, in order: // - root filesystem kargs @@ -1131,6 +1141,7 @@ async fn install_container( // - kargs.d from container image // - args specified on the CLI let mut kargs = Cmdline::new(); + let mut karg_deletes = Vec::<&str>::new(); kargs.extend(&root_setup.kargs); @@ -1142,6 +1153,19 @@ async fn install_container( kargs.extend(&kargsd); + // delete kargs before processing cli kargs, so cli kargs can override all other configs + if let Some(install_config_karg_deletes) = install_config_karg_deletes { + for karg_delete in install_config_karg_deletes { + karg_deletes.push(karg_delete); + } + } + if let Some(deletes) = state.config_opts.karg_delete.as_ref() { + for karg_delete in deletes { + karg_deletes.push(karg_delete); + } + } + delete_kargs(&mut kargs, &karg_deletes); + if let Some(cli_kargs) = state.config_opts.karg.as_ref() { for karg in cli_kargs { kargs.extend(karg); @@ -1224,6 +1248,18 @@ async fn install_container( Ok((deployment, aleph)) } +pub(crate) fn delete_kargs(existing: &mut Cmdline, deletes: &Vec<&str>) { + for delete in deletes { + if let Some(param) = utf8::Parameter::parse(&delete) { + if param.value().is_some() { + existing.remove_exact(¶m); + } else { + existing.remove(¶m.key()); + } + } + } +} + /// Run a command in the host mount namespace pub(crate) fn run_in_host_mountns(cmd: &str) -> Result { let mut c = Command::new(bootc_utils::reexec::executable_path()?); @@ -3085,4 +3121,21 @@ UUID=boot-uuid /boot ext4 defaults 0 0 Ok(()) } + + #[test] + fn test_delete_kargs() -> Result<()> { + let mut cmdline = Cmdline::from("console=tty0 quiet debug nosmt foo=bar foo=baz bar=baz"); + + let deletions = vec!["foo=bar", "bar", "debug"]; + + delete_kargs(&mut cmdline, &deletions); + + let result = cmdline.to_string(); + assert!(!result.contains("foo=bar")); + assert!(!result.contains("bar")); + assert!(!result.contains("debug")); + assert!(result.contains("foo=baz")); + + Ok(()) + } } diff --git a/crates/lib/src/install/config.rs b/crates/lib/src/install/config.rs index 07acd2b5e..a5194eb9c 100644 --- a/crates/lib/src/install/config.rs +++ b/crates/lib/src/install/config.rs @@ -104,6 +104,9 @@ pub(crate) struct InstallConfiguration { /// Kernel arguments, applied at installation time #[serde(skip_serializing_if = "Option::is_none")] pub(crate) kargs: Option>, + /// Deleting Kernel arguments, applied at installation time + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) karg_deletes: Option>, /// Supported architectures for this configuration pub(crate) match_architectures: Option>, /// Ostree repository configuration @@ -208,6 +211,11 @@ impl Mergeable for InstallConfiguration { .get_or_insert_with(Default::default) .extend(other_kargs) } + if let Some(other_karg_deletes) = other.karg_deletes { + self.karg_deletes + .get_or_insert_with(Default::default) + .extend(other_karg_deletes) + } } } } @@ -242,6 +250,7 @@ impl InstallConfiguration { // Remove all configuration which is handled by `install to-filesystem`. pub(crate) fn filter_to_external(&mut self) { self.kargs.take(); + self.karg_deletes.take(); } #[cfg(feature = "install-to-disk")] @@ -346,6 +355,7 @@ root-fs-type = "xfs" r##"[install] root-fs-type = "ext4" kargs = ["console=ttyS0", "foo=bar"] +karg-deletes = ["debug", "bar=baz"] "##, ) .unwrap(); @@ -359,6 +369,12 @@ kargs = ["console=ttyS0", "foo=bar"] .map(ToOwned::to_owned) .collect(), ), + karg_deletes: Some( + ["baz", "bar=baz"] + .into_iter() + .map(ToOwned::to_owned) + .collect(), + ), ..Default::default() }), }; @@ -372,7 +388,16 @@ kargs = ["console=ttyS0", "foo=bar"] .map(ToOwned::to_owned) .collect() ) - ) + ); + assert_eq!( + install.karg_deletes, + Some( + ["debug", "bar=baz", "baz", "bar=baz"] + .into_iter() + .map(ToOwned::to_owned) + .collect() + ) + ); } #[test] diff --git a/crates/tests-integration/src/install.rs b/crates/tests-integration/src/install.rs index dc9d2afd2..21963ed25 100644 --- a/crates/tests-integration/src/install.rs +++ b/crates/tests-integration/src/install.rs @@ -169,6 +169,26 @@ pub(crate) fn run_alongside(image: &str, mut testargs: libtest_mimic::Arguments) generic_post_install_verification()?; Ok(()) }), + Trial::test("install with --karg-delete", move || { + let sh = &xshell::Shell::new()?; + reset_root(sh, image)?; + cmd!(sh, "sudo {BASE_ARGS...} {target_args...} {image} bootc install to-filesystem --acknowledge-destructive --karg-delete=localtestkarg --replace=alongside /target").run()?; + cmd!( + sh, + "sudo {BASE_ARGS...} {target_args...} {image} bootc install finalize /target" + ) + .run()?; + let entries = cmd!( + sh, + "sudo /bin/sh -c 'grep localtestkarg /boot/loader/entries/*.conf'" + ) + .ignore_status() + .read()?; + + assert!(entries.is_empty()); + + Ok(()) + }), ]; libtest_mimic::run(&testargs, tests.into()).exit() diff --git a/docs/src/man/bootc-install-to-disk.8.md b/docs/src/man/bootc-install-to-disk.8.md index 3ee99e0d8..55580ed7b 100644 --- a/docs/src/man/bootc-install-to-disk.8.md +++ b/docs/src/man/bootc-install-to-disk.8.md @@ -119,6 +119,10 @@ its DPS type GUID, without requiring an explicit `root=` kernel argument. Add a kernel argument. This option can be provided multiple times +**--karg-delete**=*KARG_DELETE* + + Remove a kernel argument. This option can be provided multiple times + **--root-ssh-authorized-keys**=*ROOT_SSH_AUTHORIZED_KEYS* The path to an `authorized_keys` that will be injected into the `root` account diff --git a/docs/src/man/bootc-install-to-existing-root.8.md b/docs/src/man/bootc-install-to-existing-root.8.md index edaf87d76..4b4f8925d 100644 --- a/docs/src/man/bootc-install-to-existing-root.8.md +++ b/docs/src/man/bootc-install-to-existing-root.8.md @@ -175,6 +175,10 @@ of migrating the fstab entries. See the "Injecting kernel arguments" section abo Add a kernel argument. This option can be provided multiple times +**--karg-delete**=*KARG_DELETE* + + Remove a kernel argument. This option can be provided multiple times + **--root-ssh-authorized-keys**=*ROOT_SSH_AUTHORIZED_KEYS* The path to an `authorized_keys` that will be injected into the `root` account diff --git a/docs/src/man/bootc-install-to-filesystem.8.md b/docs/src/man/bootc-install-to-filesystem.8.md index 56c76c6e5..f46892de3 100644 --- a/docs/src/man/bootc-install-to-filesystem.8.md +++ b/docs/src/man/bootc-install-to-filesystem.8.md @@ -83,6 +83,10 @@ is currently expected to be empty by default. Add a kernel argument. This option can be provided multiple times +**--karg-delete**=*KARG_DELETE* + + Remove a kernel argument. This option can be provided multiple times + **--root-ssh-authorized-keys**=*ROOT_SSH_AUTHORIZED_KEYS* The path to an `authorized_keys` that will be injected into the `root` account diff --git a/tmt/plans/integration.fmf b/tmt/plans/integration.fmf index 46c58eb55..1281b9ebe 100644 --- a/tmt/plans/integration.fmf +++ b/tmt/plans/integration.fmf @@ -237,4 +237,12 @@ execute: how: fmf test: - /tmt/tests/tests/test-39-upgrade-tag + +/plan-40-install-karg-delete: + summary: Test bootc install --karg-delete + discover: + how: fmf + test: + - /tmt/tests/tests/test-40-install-karg-delete + extra-fixme_skip_if_composefs: true # END GENERATED PLANS diff --git a/tmt/tests/booted/test-install-karg-delete.nu b/tmt/tests/booted/test-install-karg-delete.nu new file mode 100644 index 000000000..ca9364333 --- /dev/null +++ b/tmt/tests/booted/test-install-karg-delete.nu @@ -0,0 +1,47 @@ +# number: 40 +# tmt: +# summary: Test bootc install --karg-delete +# duration: 30m +# extra: +# fixme_skip_if_composefs: true +# +use std assert +use tap.nu +# +# Use an OS-matched target image to avoid version mismatches +let target_image = (tap get_target_image) + +# setup filesystem +mkdir /var/mnt +truncate -s 10G disk.img +mkfs.ext4 disk.img +mount -o loop disk.img /var/mnt + +# Mask off the bootupd state to reproduce https://github.com/bootc-dev/bootc/issues/1778 +# Also it turns out that installation outside of containers dies due to `error: Multiple commit objects found` +# so we mask off /sysroot/ostree +# And using systemd-run here breaks our install_t so we disable SELinux enforcement +setenforce 0 + +mkdir /etc/bootc/install +{ install: { kargs: ["foo=bar"] } } | to toml | save /etc/bootc/install/99-test.toml + +tap run_install $"bootc install to-filesystem --disable-selinux --bootloader none --source-imgref ($target_image) --karg-delete localtestkarg --karg-delete foo /var/mnt" + + +# Verify the karg is gone from the bootloader entries +let entries = (glob /var/mnt/boot/loader/entries/*.conf + | each { open $in | lines } + | flatten) + +let localtestkarg_found = ($entries | find "localtestkarg" | is-empty) +assert $localtestkarg_found "Found localtestkarg in bootloader entries, but it should have been deleted" + +let foo_found = ($entries | find "foo" | is-empty) +assert $foo_found "Found foo in bootloader entries, but it should have been deleted" + +# Clean up +umount /var/mnt +rm -rf disk.img + +tap ok diff --git a/tmt/tests/tests.fmf b/tmt/tests/tests.fmf index b26b3ad9c..2ad8717af 100644 --- a/tmt/tests/tests.fmf +++ b/tmt/tests/tests.fmf @@ -136,3 +136,8 @@ summary: Test bootc upgrade --tag functionality with containers-storage duration: 30m test: nu booted/test-upgrade-tag.nu + +/test-40-install-karg-delete: + summary: Test bootc install --karg-delete + duration: 30m + test: nu booted/test-install-karg-delete.nu